浅谈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.配置

cs 复制代码
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.启动类

java 复制代码
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.写自己的过滤器

java 复制代码
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.配置

Groovy 复制代码
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.全局过滤器

java 复制代码
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;
    }
}
java 复制代码
/**
* 降级配置
*/
@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地址转换为一个数字,根据该数字来选择服务器。

相关推荐
一嘴一个橘子4 小时前
mybatis - 动态语句、批量注册mapper、分页插件
java
组合缺一4 小时前
Json Dom 怎么玩转?
java·json·dom·snack4
危险、4 小时前
一套提升 Spring Boot 项目的高并发、高可用能力的 Cursor 专用提示词
java·spring boot·提示词
kaico20184 小时前
JDK11新特性
java
钊兵4 小时前
java实现GeoJSON地理信息对经纬度点的匹配
java·开发语言
jiayong234 小时前
Tomcat性能优化面试题
java·性能优化·tomcat
爬山算法4 小时前
Hibernate(51)Hibernate的查询缓存如何使用?
spring·缓存·hibernate
秋刀鱼程序编程4 小时前
Java基础入门(五)----面向对象(上)
java·开发语言
纪莫4 小时前
技术面:MySQL篇(InnoDB的锁机制)
java·数据库·java面试⑧股
Remember_9934 小时前
【LeetCode精选算法】滑动窗口专题二
java·开发语言·数据结构·算法·leetcode