springboot2.0下的zuul路由网关初探

Easter79
• 阅读 630

Zuul作为微服务系统的网关组件,用于构建边界服务,致力于动态路由、过滤、监控、弹性伸缩和安全。

为什么需要Zuul

Zuul、Ribbon以及Eureka结合可以实现智能路由和负载均衡的功能;网关将所有服务的API接口统一聚合,统一对外暴露。外界调用API接口时,不需要知道微服务系统中各服务相互调用的复杂性,保护了内部微服务单元的API接口;网关可以做用户身份认证和权限认证,防止非法请求操作API接口;网关可以实现监控功能,实时日志输出,对请求进行记录;网关可以实现流量监控,在高流量的情况下,对服务降级;API接口从内部服务分离出来,方便做测试。

Zuul通过Servlet来实现,通过自定义的ZuulServlet来对请求进行控制。核心是一系列过滤器,可以在Http请求的发起和响应返回期间执行一系列过滤器。Zuul采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接通信,而是通过RequestContext对象来共享数据,每个请求都会创建一个RequestContext对象。

Zuul生命周期如下图。 当一个客户端Request请求进入Zuul网关服务时,网关先进入”pre filter“,进行一系列的验证、操作或者判断。然后交给”routing filter“进行路由转发,转发到具体的服务实例进行逻辑处理、返回数据。当具体的服务处理完成后,最后由”post filter“进行处理,该类型的处理器处理完成之后,将Request信息返回客户端。 

Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器.

  Zuul功能:

  • 认证
  • 压力测试
  • 金丝雀测试
  • 动态路由
  • 负载削减
  • 安全
  • 静态响应处理
  • 主动/主动交换管理

Zuul的规则引擎允许通过任何JVM语言来编写规则和过滤器, 支持基于Java和Groovy的构建。

现在我们简单的先搭建一个网关服务器

首先单独建一个项目,用来做网关服务器

引入相关的主要jar包:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

引入spring-cloud-starter-netflix-eureka-client的目的是本身通过url映射的方式来实现zuul的转发有局限性,比如每增加一个服务就需要配置一条内容,另外后端的服务如果是动态来提供,就不能采用这种方案来配置了。实际上在实现微服务架构时,服务名与服务实例地址的关系在eureka server中已经存在了,所以只需要将Zuul注册到eureka server上去发现其他服务,就可以实现对serviceId的映射。

加入yml的配置文件,主要用来连接eureka,及配置网关路由规则

server:
  port: 8088
spring.application.name: startGateway
eureka:
  client:
    service-url: 
      defaultZone: http://root:root@localhost:10000/eureka
zuul.routes.three.path: /three/**
zuul.routes.three.service-id: three
zuul.routes.three.stripPrefix: false
zuul.routes.five.path: /five/**
zuul.routes.five.service-id: five
zuul.routes.five.stripPrefix: false

这边有个坑:

    设置 zuul.prefix 可以为所有的匹配增加前缀, 例如 /api,代理前缀默认会从请求路径中移除(通过 zuul.stripPrefix=false 可以关闭这个功能).

  1. 反响代理配置  
  2. 这里的配置类似nginx的反向代理  
  3. 当请求/api/**会直接交给listOfServers配置的服务器处理  
  4. 当stripPrefix=true的时候 (http://127.0.0.1:3333/api/user/list -> http://192.168.1.100:8080/user/list)  
  5. 当stripPrefix=false的时候(http://127.0.0.1:5555/api/user/list -> http://192.168.1.100:8080/api/user/list)  
  6. zuul.routes.api.path=/api/**  
  7. zuul.routes.api.stripPrefix=false  
  8. api.ribbon.listOfServers=192.168.1.100:8080,192.168.1.101:8080,192.168.1.102:8080

你也可以在指定服务中关闭这个功能:

zuul.routes.five.path: /five/**
zuul.routes.five.service-id: five
zuul.routes.five.stripPrefix: false

在这个例子中, 请求"/five/a"将被跳转到"five"服务的"/five/a"上.如果不配置stripPrefix: false的话就会默认路由到/a上,忽略/five这个前缀,导致404找不到资源

之后在启动类上配置

package cn.chinotan;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
 * @program: test
 * @description: 启动类
 * @author: xingcheng
 * @create: 2018-12-9 15:39
 **/
@SpringCloudApplication
@EnableEurekaClient
@EnableZuulProxy
public class StartGateway {

    public static void main(String[] args) {
        SpringApplication.run(StartGateway.class, args);
    }

}

就完成了一个简单的网关路由

其中@EnableZuulProxy是@EnableZuulServer的加强,@SpringCloudApplication会包含@EnableEurekaClient,所以其实@EnableEurekaClient不需要写

之后在写两个测试controller进行路由判断:

package cn.chinotan.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @program: test
 * @description: zuul测试控制器
 * @author: xingcheng
 * @create: 2018-12-08 18:09
 **/
@RestController
@RequestMapping("/five")
public class ZuulTestFiveController {
    
    @GetMapping("/hello/{name}")
    public String ZuulTestFive(@PathVariable("name") String name) {
        return "hello " + name + "  this is ZuulTestFive";
    }
    
}


package cn.chinotan.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @program: test
 * @description: zuul测试控制器
 * @author: xingcheng
 * @create: 2018-12-08 18:09
 **/
@RestController
@RequestMapping("/three")
public class ZuulTestThreeController {
    
    @GetMapping("/hello/{name}")
    public String ZuulTestFive(@PathVariable("name") String name) {
        return "hello " + name + " this is ZuulTestThree";
    }
    
}

当然这些服务也得连接到euerka上,好让代理网关可以根据service-id进行服务发现

其中,euerka上的连接情况如下:

springboot2.0下的zuul路由网关初探

可以看到网关服务器的端口是8088,两个服务的网关是5555-five和3333-three

接下来,我们不直接请求提供服务的three和five服务器,而是请求网关代理

springboot2.0下的zuul路由网关初探

springboot2.0下的zuul路由网关初探

可以看到网关服务成功的路由了这两次请求

服务过滤

Zuul还有一个主要的功能,便是服务过滤,比如,用户在登录前,可以将服务请求过滤到指定的页面去。 
在项目中,新增一个MyFilter类。代码如下:

@Component
public class MyFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run(){
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        Object accessToken = request.getParameter("token");
        if(accessToken == null) {
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().setHeader("Content-Type", "text/html;charset=UTF-8");
                ctx.getResponse().getWriter().write("登录信息为空!");
            }catch (Exception e){}
            return null;
        }
        return null;
    }
}

其中,filterType方法,返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:pre:路由之前 
routing:路由之时 
post: 路由之后 
error:发送错误调用 
filterOrder:过滤的顺序 
shouldFilter:这里可以写逻辑判断,是否要开启过滤 
run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。
一般我们在使用时,不手打“pre”这些类型,而是通过调用Zuul中已写好的FilterConstants类,其中封装了所有的过滤器类型。这样可以避免打错字符而导致错误的发生。 
接下来,我们访问http://localhost:9005/feign/welcome?name=Cheng 显示:

登录信息为空!

在后面加上token,http://localhost:9005/feign/welcome?name=Cheng&token=111就可以正常访问了

类似的过滤器很多,我们可以自定义,从而实现统一的网关控制、监控、跨域、流量控制、负载均衡,身份认证,服务降级等等。

点赞
收藏
评论区
推荐文章
Stella981 Stella981
3年前
Spring Cloud系列教程(九):服务网关Zuul(Finchley版本)
一、前言Zuul是netflix开源的一个API网关服务器,其本质上是一个webservlet应用。Zuul是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul相当于是PC、APP、H5等客户端和Netflix流应用的Web网站后端所有请求的前门,Zuu
Easter79 Easter79
3年前
SpringCloud系列——Zuul 动态路由
  前言  Zuul是在SpringCloudNetflix平台上提供动态路由,监控,弹性,安全等边缘服务的框架,是Netflix基于jvm的路由器和服务器端负载均衡器,相当于是设备和Netflix流应用的Web网站后端所有请求的前门。本文基于上篇(SpringCloud系列——Ribbon负载均衡(https://www.
Easter79 Easter79
3年前
SpringCloud之zuul搭建
一、zuul简介Zuul的主要功能是路由和过滤器。路由功能是微服务的一部分,比如/api/user映射到user服务,/api/shop映射到shop服务。zuul实现了负载均衡。zuul有以下功能:AuthenticationInsightsStressTestingCanaryTesting
Wesley13 Wesley13
3年前
API网关原理
1、API网关介绍 API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。 API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有
Easter79 Easter79
3年前
SpringCloud学习之Zuul路由转发、拦截和熔断处理(七)
SpringCloudZuul服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供RESTAPI的过程中,除了具备服务路由、均衡负载功能之外,它还具备了权限控制等功能。SpringCloudNetflix中的Zuul就担任了这样的一个角色,为微服务架构提供了前门保护的作用,同时将权限控制
Easter79 Easter79
3年前
SpringCloud微服务(05):Zuul组件,实现路由网关控制
一、Zuul组件简介1、基础概念Zuul网关主要提供动态路由,监控,弹性,安全管控等功能。在分布式的微服务系统中,系统被拆为了多个微服务模块,通过zuul网关对用户的请求进行路由,转发到具体的后微服务模块中。2、Zuul的作用1)按照不同策略,将请求转发到不同的服务上去;
Easter79 Easter79
3年前
SpringCloud(第 018 篇)Zuul 服务 API 网关微服务之代理与反向代理
SpringCloud(第018篇)Zuul服务API网关微服务之代理与反向代理一、大致介绍1、API服务网关顾名思义就是统一入口,类似nginx、F5等功能一样,统一代理控制请求入口,弱化各个微服务被客户端记忆功能;
Easter79 Easter79
3年前
SpringCloud学习笔记(七)之路由网关Zuul
是什么Zuul包含了对请求路由和过滤两个最主要的功能:其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。而过滤功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。Zuul和Eureka进行整合,将zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微
Stella981 Stella981
3年前
Spring Cloud(五)
微服务网关在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。为什么需要APIGateway1\.简化客户端调用复杂度在微服
Easter79 Easter79
3年前
SpringCloud介绍
自己通俗的理解只能是Http协议调用,Dubbo可以支持多种协议就是RPC远程调用就是提供服务注册,服务发现,断路器,网关系统和自动配置的工具,底层采用http协议实现采用Eureka注册中Ribbon实现负载均衡Feignhttp协议调用工具Hystrix断路器Zuul网关系统就是把API服务通过注册中心
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k