Spring Cloud - Gateway

https://www.cnblogs.com/jmcui/p/11259200.html

https://mp.weixin.qq.com/s/b0o1SotPgnoc5odBECAZrQ

https://baijiahao.baidu.com/s?id=1735384008263810697&wfr=spider&for=pc

Gateway 和 Zuul 的区别

https://blog.csdn.net/weixin_42276861/article/details/106904028

Zuul 基于servlet 2.5 (works with 3.x),使用阻塞API。它不支持任何长期的连接,如websocket。

Gateway建立在Spring Framework 5,Project Reactor 和Spring Boot 2 上,使用非阻塞API。支持Websocket,因为它与Spring紧密集成,所以它是一个更好的开发者体验。

为什么 Spring Cloud 最初选择了使用 Netflix 几年前开源的 Zuul 作为网关,之后又选择了自建 Gateway 呢?有一种说法是,高性能版的 Zuul2 在经过了多次跳票之后,对于 Spring 这样的整合专家可能也不愿意再继续等待,所以 Spring Cloud Gateway 应运而生。

Spring Cloud Gateway 的工作流程

① 路由判断;客户端的请求到达网关后,先经过 Gateway Handler Mapping 处理,这里面会做断言(Predicate)判断,看下符合哪个路由规则,这个路由映射后端的某个服务。

② 请求过滤:然后请求到达 Gateway Web Handler,这里面有很多过滤器,组成过滤器链(Filter Chain),这些过滤器可以对请求进行拦截和修改,比如添加请求头、参数校验等等,有点像净化污水。然后将请求转发到实际的后端服务。这些过滤器逻辑上可以称作 Pre-Filters,Pre 可以理解为“在...之前”。

③ 服务处理:后端服务会对请求进行处理。

④ 响应过滤:后端处理完结果后,返回给 Gateway 的过滤器再次做处理,逻辑上可以称作 Post-Filters,Post 可以理解为“在...之后”。

⑤ 响应返回:响应经过过滤处理后,返回给客户端。

小结:客户端的请求先通过匹配规则找到合适的路由,就能映射到具体的服务。然后请求经过过滤器处理后转发给具体的服务,服务处理后,再次经过过滤器处理,最后返回给客户端。

Spring Cloud Gateway 的断言

断言(Predicate)这个词听起来极其深奥,它是一种编程术语,我们生活中根本就不会用它。说白了它就是对一个表达式进行 if 判断,结果为真或假,如果为真则做这件事,否则做那件事。

在 Gateway 中,如果客户端发送的请求满足了断言的条件,则映射到指定的路由器,就能转发到指定的服务上进行处理。

断言配置的示例如下,配置了两个路由规则,有一个 predicates 断言配置,当请求 url 中包含 api/thirdparty,就匹配到了第一个路由 route_thirdparty。(代码示例来自我的开源项目 PassJava)

 

  • 一对多:一个路由规则可以包含多个断言。如上图中路由 Route1 配置了三个断言 Predicate。
  • 同时满足:如果一个路由规则中有多个断言,则需要同时满足才能匹配。如上图中路由 Route2 配置了两个断言,客户端发送的请求必须同时满足这两个断言,才能匹配路由 Route2。
  • 第一个匹配成功:如果一个请求可以匹配多个路由,则映射第一个匹配成功的路由。如上图所示,客户端发送的请求满足 Route3 和 Route4 的断言,但是 Route3 的配置在配置文件中靠前,所以只会匹配 Route3。

uri: lb://passjava-question,表示将请求转发给 passjava-question 微服务,且支持负载均衡。lb 是 loadbalance(负载均衡) 单词的缩写。

那什么叫动态路由呢?

当 passjava-question 服务添加一个微服务,或者 IP 地址更换了,Gateway 都是可以感知到的,但是配置是不需要更新的。这里的动态指的是微服务的集群个数、IP 和端口是动态可变的。

Spring Cloud Gateway 的过滤器

过滤器 Filter 的分类

过滤器 Filter 按照请求和响应可以分为两种:Pre 类型和 Post 类型。

Pre 类型:在请求被转发到微服务之前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等操作。

Post 类型:微服务处理完请求后,返回响应给网关,网关可以再次进行处理,例如修改响应内容或响应头、日志输出、流量监控等。

与 Zuul 不同的是,filter 除了分为 “pre” 和 “post” 两种方式的 filter 外,在 Spring Cloud Gateway 中,filter 从作用范围可分为另外两种,一种是针对于单个路由的 gateway filter,它需要像上面 application.yml 中的 filters 那样在单个路由中配置;另外一种是针对于全部路由的global gateway filter,不需要单独配置,对所有路由生效。

另外一种分类是按照过滤器 Filter 作用的范围进行划分:

GlobalFilter:全局过滤器,应用在所有路由上的过滤器。

GatewayFilter:局部过滤器,应用在单个路由或一组路由上的过滤器。标红色表示比较常用的过滤器。

如果 URL 匹配成功,则去掉 URL 中的 “api”。

局部过滤器

 

filters: #过滤器
   - RewritePath=/api/(?<segment>.*),/$\{segment} # 将跳转路径中包含的 “api” 替换成空

全局过滤器

我们通常用全局过滤器实现鉴权、验签、限流、日志输出等。

整理了一份全局过滤器的表格,具体用法可以参照官方文档。

官方文档:https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_global_filters
 

 全局过滤器最常见的用法是进行负载均衡。配置如下所示:

spring:
  cloud:
    gateway:
      routes:
        - id: route_member # 第三方微服务路由规则
          uri: lb://passjava-member # 负载均衡,将请求转发到注册中心注册的 passjava-member 服务
          predicates: # 断言
            - Path=/api/member/** # 如果前端请求路径包含 api/member,则应用这条路由规则
          filters: #过滤器
            - RewritePath=/api/(?<segment>.*),/$\{segment} # 将跳转路径中包含的api替换成空

这里有个关键字 lb,用到了全局过滤器 LoadBalancerClientFilter,当匹配到这个路由后,会将请求转发到 passjava-member 服务,且支持负载均衡转发,也就是先将 passjava-member 解析成实际的微服务的 host 和 port,然后再转发给实际的微服务。

实现简单的 token 认证

在用 Gateway 做登录认证的时候,通常需要我们自定义一个过滤器做登录认证。

比如客户端登录时,将用户名和密码发送给网关,网关转发给认证服务器后,如果账号密码正确,则拿到一个 JWT token,然后客户端再访问应用服务时,先将请求发送给网关,网关统一做 JWT 认证,如果 JWT 符合条件,再将请求转发给应用服务。

原理如下图所示,红色框框的部分就是待会我要演示的部分。

 

 application.yml

spring:
  application:
    name: cloud-gateway
  redis:
    host: 127.0.0.1
    timeout: 3000
    password: xxxx
    jedis:
      pool:
        max-active: 8
        max-idle: 4
  cloud:
    gateway:
      enabled: true
      metrics:
        enabled: true
      discovery:
        locator:
          enabled: true
      routes:
        # 普通服务的路由配置
        - id: cloud-eureka-client
          uri: lb://cloud-eureka-client
          order: 0
          predicates:
            - Path=/client/**
          filters:
            #  parts 参数指示在将请求发送到下游之前,要从请求中去除的路径中的节数。比如我们访问 /client/hello,调用的时候变成 http://localhost:2222/hello
            - StripPrefix=1
            # 熔断器
            - name: Hystrix
              args:
                name: fallbackcmd
                # 降级处理
                fallbackUri: forward:/fallback
            # 限流器
            # 这定义了每个用户 10 个请求的限制。允许 20 个突发,但下一秒只有 10 个请求可用。
            - name: RequestRateLimiter
              args:
                # SPEL 表达式获取 Spring 中的 Bean,这个参数表示根据什么来限流
                key-resolver: '#{@ipKeyResolver}'
                # 允许用户每秒执行多少请求(令牌桶的填充速率)
                redis-rate-limiter.replenishRate: 10
                # 允许用户在一秒内执行的最大请求数。(令牌桶可以保存的令牌数)。将此值设置为零将阻止所有请求。
                redis-rate-limiter.burstCapacity: 20
        # websocket 的路由配置
        - id: websocket service
          uri: lb:ws://serviceid
          predicates:
            - Path=/websocket/**
management:
  endpoints:
    web:
      exposure:
        # 开启指定端点
        include: gateway,metrics
eureka:
  client:
    service-url:
      defaultZone: http://user:password@localhost:1111/eureka/

 

posted @ 2022-06-19 20:44  Nausicaa0505  阅读(152)  评论(0编辑  收藏  举报