浅谈Zuul、Gateway

前言

主流网关比对

一、Netflix Zuul

Zuul1.0的通信模型

Zuul1.0的通信模型

Zuul2.0的通信模型

Zuul2.0通信模型

Zuul是通过Servlet来实现的(Servlet 会为每个请求绑创建一个线程,而线程上线文切换,内存消耗大),Zuul通过自定义的ZuulServlet(类似于Spring MVC的DispatcherServlet)来对请求进行控制(一系列过滤器处理Http请求)。
所有的Request都要经过ZuulServlet的处理,三个核心的方法preRoute(),route(), postRoute(),zuul对request处理逻辑都在这三个方法里,ZuulServlet交给ZuulRunner去执行。
ZuulRunner直接将执行逻辑交由FilterProcessor处理,ZuulServlet、ZuulRunner、FilterProcessor都是单例。
FilterProcessor对filter的处理逻辑。
    1.根据Type获取所有输入该Type的filter,List<ZuulFilter> list。
    2.遍历该list,执行每个filter的处理逻辑,processZuulFilter(ZuulFilter filter)。
    3.RequestContext对每个filter的执行状况进行记录,此处的执行状态主要包括其执行时间、以及执行成功或者失败,若失败则对异常封装后抛出。
    4.zuul框架对每个filter的执行结果都没有太多的处理,没把上一filter的执行结果交由下一个将要执行的filter,仅记录执行状态,如果执行失败抛出异常并终止执行。

1、Zuul过滤器的功能

1.身份验证和安全性 - 确定每个资源的身份验证要求并拒绝不满足这些要求的请求。
2.洞察和监控 - 在边缘跟踪有意义的数据和统计数据,以便为我们提供准确的生产视图。
3.动态路由 - 根据需要动态地将请求路由到不同的后端群集。
4.压力测试 - 逐渐增加群集的流量以衡量性能。
5.负载分配 - 为每种类型的请求分配容量并删除超过限制的请求。
6.静态响应处理 - 直接在边缘构建一些响应,而不是将它们转发到内部集群。

2、Zuul 生命周期(四类过滤)

​​​​​1.PRE: 在请求被路由之前调用。可在集群中选择请求的微服务、认证鉴权,限流等。
2.ROUTING: 将请求路由到微服务。可构建发送给微服务的请求,并使用Apache HttpClient或 Ribbon请求微服务,现在也支持OKHTTP。
3.POST: 在路由到微服务以后执行。可在这种过滤中处理逻辑,如收集统计信息和指标、将响应从微服务发送给客户端等。
4.ERROR: 在其他阶段发生错误时执行该过滤器。可做全局异常处理。

Zuul的10种过滤器

 3、Zuul的使用

1.配置

server:
  port: 80
spring:
  application:
    name: demo-zuul
eureka:
  client:
    enabled: true #该客户端是否可用
    service-url:
      defaultZone: http://localhost:8761/eureka #注册中心地址
    register-with-eureka: true #注册该服务,默认为true
    fetch-registry: true #获取服务列表,默认为true

2.启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableZuulProxy//开启网关功能
public class DemoZuulApplication {

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

    //配置动态路由规则
    @Bean
    public PatternServiceRouteMapper getPatternServiceRouteMapper() {
        return new PatternServiceRouteMapper("(?<name>^.+)", "${name}");
    }
}

3.写自己的过滤器

import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;

@Component
public class TokenFilter extends ZuulFilter {

    /**
     * 拦截类型,4种类型 pre route error post
     */
    @Override
    public String filterType() {
        //  FilterConstants.PRE_TYPE;
        //  FilterConstants.ROUTE_TYPE;
        //  FilterConstants.ERROR_TYPE;
        //  FilterConstants.POST_TYPE;
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 该过滤器在所有过滤器的执行顺序值,值越小,越前面执行
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 是否拦截
     */
    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        // RequestContext ctx = RequestContext.getCurrentContext();
        // ctx.getBoolean("isOk");
        HttpServletRequest request = ctx.getRequest();
        String requestURI = request.getRequestURI();
        //排除拦截的url
        if (requestURI.equals("/demo-member/user/loadBalance")) {
            return false;
        }
        return true;
    }

     /**
      * 过滤器具体的业务逻辑
      */
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String token = request.getParameter("token");
        // ctx.set("isOk",true); // 可以在上下文里面设置一个key,在下一次拦截时,就可以获取到
        if (null == token) {
            ctx.setResponseBody("token is null");
            ctx.setResponseStatusCode(400);
            ctx.setSendZuulResponse(false);
            return null;
        }
        if (!"123456".equals(token)) {
            ctx.setResponseBody("token is error");
            ctx.setResponseStatusCode(400);
            ctx.setSendZuulResponse(false);
            return null;
        }
        ctx.setSendZuulResponse(true);
        return null;
    }
}

二、Spring Cloud Gateway

spring cloud gateway 的核心是一系列过滤器,可将客户端的请求转到不同服务器,可简称为过滤和路由。与Zuul的主要的区别在底层的通信框架上Gateway 底层使用通信框架Netty提供非阻塞异步请求处理,内嵌 Hystrix 断路器。
(1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0。
(2)集成 Hystrix 断路器。
(3)集成 Spring Cloud DiscoveryClient。
(4)Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters。
(5)具备一些网关的高级功能:动态路由、限流、路径重写。

GateWay的通信模型

GateWay的通信模型


GateWay的路由流程

GateWay内部工作流程 

GateWay的内部工作流程

1、GateWay三大组件

1.路由 Route
id:路由标识,要求唯一,名称任意(默认uuid)。
uri:请求最终被转发到的目标地址。
order: 路由优先级,数字越小,优先级越高。
predicates:断言数组,即判断条件,如果返回值是boolean,则转发请求到 uri 属性指定的服务中。
filters:过滤器,在请求传递过程中,可做一些逻辑处理。

2.断言 Predicate
接受一个输入参数,返回一个布尔值结果。用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。

过滤器 filter:

GateWay的过滤器


生命周期:
PRE:在路由之前调用。可实现身份验证、在集群中选择请求的微服务、记录调试信息等。
POST:在路由到微服务后调用。可用来为响应添加标准的 HTTP Header、收集统计信息和指标等。
作用范围
GatewayFilter:单个路由或一个组的路由上(要在配置文件中配置)。
GlobalFilter:所有的路由上(无需配置,全局生效)。

2、GateWay的配置及使用

1.配置

spring:
  cloud:
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件    
   		    - Method=GET,POST
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
            - After=2022-05-05T02:00:00.000+08:00[Asia/Shanghai] # 路由指定时间后生效、还有其他的参数(Before、Between)
		filters: #过滤器  
	    	- AddResponseHeader=X-Response-test1, test1
            # - RewritePath=/service1/(?<segment>.*), /$\{segment} # 重写路由地址,http://127.0.0.1:21000/service1/hystrix/calculate 会转发到 http://127.0.0.1:20004/hystrix/calculate,由于 YAML 规范,$ 被 $\ 取代。
            - RewritePath=/service1/(?<segment>.*), /service1/$\{segment}
            # - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
            - RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=123456 # 重写 response 请求头
            - StripPrefix=0 # 过滤器StripPrefix,作用是去掉请求路径的最前面n个部分截取掉。StripPrefix=1就代表截取路径的个数为1,比如前端过来请求/test/good/1/view,匹配成功后,路由到后端的请求路径就会变成http://localhost:8888/good/1/view
            - AddRequestHeader=Accept-Language, zh,zh-CN;q=0.9 # 将 Accept-Language=zh,zh-CN;q=0.9 添加到所有匹配请求的 header中
            - AddRequestParameter=host, 127.0.0.1 # 将 host=127.0.0.1 添加到所有匹配请求的 Param 参数中
            - AddResponseHeader=X-Response-Foo, Bar # 将X-Response-Foo:BarHeaders 添加到所有匹配请求的下游响应的 Headers 中。
            - PrefixPath=/mypath # 将/mypath作为所有匹配请求的路径的前缀。因此,对 /service1 的请求将发送到 /mypath/service1。
            - RedirectTo=302, https://blog.csdn.net/qq_41538097/article/details/124626658 # 所有匹配到的请求都重定向到该地址
            - RemoveRequestHeader=X-Request-Foo # 这将删除 Request Headers 中的 X-Request-Foo,然后将其发送到下游
            - RemoveResponseHeader=X-Response-Foo # 这将从响应中删除 Response Headers 中的 X-Response-Foo,然后将其返回给网关 Client 端。
            - RemoveRequestParameter=red # 将 red 参数发送到下游之前将其删除
            - SetPath=/{segment} # 请求路径/red/blue 设置为 /blue 发送到下游请求
            - SetRequestHeader=X-Request-Red, Blue # 注意:替换(而不是添加),替换 Request Header 的 X-Request-Red=Blue
            - SetResponseHeader=X-Response-Red, Blue # 注意:替换(而不是添加),替换 Response Header 的 X-Response-Red=Blue
            - SetStatus=401 # 无论哪种情况,响应的 HTTP 状态都设置为 401。也可以是枚举的字符串:NOT_FOUND
            - name: RequestRateLimiter # 令牌桶算法,IP 限流
		        args:
		          redis-rate-limiter.replenishRate: 20 # 每秒允许多少个请求
		          redis-rate-limiter.burstCapacity: 10 # 允许用户在一秒钟内执行的最大请求数,将此值设置为零将阻止所有请求。
            - name: Retry
                args:
                  retries: 3 # 请求失败重试 3 次
                  statuses: BAD_GATEWAY
                  methods: GET,POST
                  backoff:
                    firstBackoff: 10ms
                    maxBackoff: 50ms
                    factor: 2
                    basedOnPreviousValue: false
			 # 降级配置
            - name: Hystrix
              args:
                name: testOne
                # 降级接口的地址
                fallbackUri: forward:/junFallback
            - name: RequestSize # 限制请求大小,超过则拒绝,如果未配置,则默认请求大小设置为 5 MB。
              args:
                maxSize: 500MB # 单位默认B,支持 B、KB、MB、GB、TB
            - name: SetRequestHostHeader # 覆盖主机标头(HTTP/1.1 请求头 Headers 默认包含 Host 头,可以是 IP,域名)
              args:
                host: 127.0.0.1
		- id: order-service # 路由id,自定义,只要唯一即可
			  uri: lb://order-service
			  order: 8003
			  predicates:
				- Path=/order-service/oauth/token
			  filters:   
				- AddResponseHeader=X-Response-test2, test2

# hystrix 信号量隔离,2秒后自动超时
hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE
          thread:
            timeoutInMilliseconds: 2000
  shareSecurityContext: true

2.全局过滤器

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
* 自定义过滤器
*/
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取请求参数
        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
        // 2.获取authorization参数
        String auth = params.getFirst("authorization");
        // 3.校验
        if ("admin".equals(auth)) {
            // 放行
            return chain.filter(exchange);
        }
        // 4.拦截
        // 4.1.禁止访问,设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        // 4.2.结束处理
        return exchange.getResponse().setComplete();
    }
	
	@Override
    public int getOrder() {
        return -1;
    }
}
/**
* 降级配置
*/
@RestController
public class JunHystrixController {
 
    @RequestMapping("/junFallback")
    public Map<String,String> junFallback(){
       System.err.println("服务降级中");
        Map<String,String> map = new HashMap<>();
        map.put("resultCode","fail");
        map.put("resultMessage","服务异常");
        map.put("resultObj","null");
        return map;
    }
}

3、gateway的限流

1、计数器算法:以QPS为100举例,如果1秒钟内钱200ms请求数量到达了100,后面800ms中的请求都会被拒绝,这种情况称为”突刺现象“
2、漏桶算法:可以解决突刺现象。比如创建一个很大的队列来接收请求,一个较小的线程池来处理请求。但是也有极限情况,当队列满了时, 请求也会拒绝掉。
3、令牌桶算法:可以说是漏桶算法的改进。在桶中放令牌,请求获取令牌后才能继续执行。如果桶中无令牌,请求可以选择进行等待或直接拒绝。

4、gateway网关负载均衡策略

(1)轮询策略 
将请求均匀地分配到每个服务器上。请求量增加时,会导致某些服务器的负载过高。

(2)加权轮询策略 
在轮询策略的基础上加了权重的概念,每个服务器都有一个权重值,权重值越高的服务器会被分配更多的请求,动态地调整权重值。

(3)IP哈希策略 
通过哈希算法将客户端IP地址转换为一个数字,根据该数字来选择服务器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值