技术交流28群

服务热线

135-6963-3175

微信服务号

spring cloud gateway详解 更新时间 2018-4-22 浏览3107次

Spring Cloud Gateway详解

文章概要:

   1、Spring cloud gateway的作用

   2、网关的运行原理

   3、网关断言和自定义路由

   5、网关过滤器

   6、服务转发

   7、网关实现限流



Zuul与Spring cloud gateway的区别

区别Zuulspring cloud gateway
底层servlet2.5兼容servlet3.x基于springboot2.x,spring5+
阻塞阻塞式Servlet APIReactor 非阻塞式API,支持websockets
长链接不支持支持
请求方式同步,数据封装在RequestContext里异步,数据封装在ServerWebExchange
支持服务器tomcat\undertownetty
支持路由规则仅Path可支持Header\Cookie\Method\Query等多种断言定义


一、spring cloud gateway的作用

可以实现动态路由、监控、回退、安全认证、日志记录

gateway是由Netty+webflux实现,不要加入web依赖,否则会报错


二、网关的运行原理

1、客户端向spring cloud gateway发出请求

2、DispatcherHandler接收用户请求

3、RouterPredicateHandlerMapping进行路由匹配。(路由断言处理器)

4、如果网关处理程序发现请求与路由匹配,则将请求发送到FilteringWebHandler(网关处理程序),不匹配则将请求返给DispatcherHandler处理。

5、FilteringWebHandler通过特定过滤器发送请求,先执行所有“PRE”逻辑,然后进行代理请求,最后执行“POST”逻辑。

6、FilteringWebHandler将请求转发到具体的服务中。

7、FilteringWebHandler将处理结果返给用户。


三、网关自定义路由

java8中引入了函数式接口Predicate,接收一个输入参数并返回一个布尔值结果。spring cloud gateway基于该接口组合简单条件进行请求校验和转发。

使用路由的流程:1、加载路由中的Predicate断言。2、用Predicate判断路由是否可用。

spring cloud gateway的路由规则由一系列RouteDefinitionLocator类管理。(路由定义定位器)。默认情况下PropertiesRouteDefinitionLocator使用@ConfigurationProperties机制来加载属性。

两种配置:

1、Java API方式

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
   return builder.routers().route("1json",r -> r.path("/1json/**")).build();
}

2、配置文件

application.yml:
server:
  port:8080
spring:
  cloud:
    application:
        name: Gateway
    gateway:
        routes:
            - id: 1json     #路由ID
               uri: http://www.1json.com #目标服务地址
               predicates: #路由条件,接收一个输入参数,返回一个bol结果
                  - Path=/1json/**
                  
                  
application.properties:
spring.cloud.gateway.routes[0].predicates[0]=Method=GET

启动项目访问http://localhost:8080/1json/1.html 会转发到www.1json.com/1json/1.html


spring cloud gateway的路由规则:

谓词工厂类型说明
AfterRoutePredicateFactorydatetime请求时间满足在配置时间之后
BeforeRoutePredicateFactorydatetime请求时间满足在配置时间之前
BetweenRoutePredicateFactorydatetime请求时间满足在配置时间之间
CookieRoutePredicateFactoryCookie请求指定Cookie正则匹配指定值
HeaderRoutePredicateFactoryHeader请求指定Header正则匹配指定值
CloudFoundryRouteServiceRoutePredicateFactoryHeader请求Headers是否包含指定名称
MethodRoutePredicateFactoryMethod请求Method匹配配置的Methos
PathRoutePredicateFactoryPath请求路径正则匹配指定值
QueryRoutePredicateFactoryQueryparam请求查询参数正则匹配指定值
RemoteAddrRoutePredicateFactoryRemoteaddr请求远程地址匹配配置指定值
HostRoutePredicateFactoryHost请求Host匹配指定值
WeightRoutePredicateFactoryWeight权重路由工厂

当前路由设置多个规则时,路由优先级计算如下:

根据权重匹配:同一组路由的优先级由权重决定,优先匹配权重高的路由。

根据路由id值匹配:不同组路由的优先级根据路由ID来计算。优先匹配ID小的路由。即,当一个请求满足多个路由的谓词条件时,请求只会被首个成功匹配的路由转发。

当各种断言谓词同时存在同一个路由时,请求必须同时满足所有的条件才会被这个路由匹配。


四、网关实现服务转发

spring cloud gateway提供了一种默认的转发功能,当网关被注册到注册中心后,网关会代理“服务中心”的转发服务。

1、添加依赖

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

2、添加配置

spring.cloud.gateway.discovery.locator.enabled=true

整合后的服务访问路径:http://网关地址:端口/服务中心serviceId/方法



五 过滤器

可以在过滤器做自己的鉴权、限流、日志输出等工作。

分为网关过滤器GatewayFilter:应用在单个路由或者一个分组的路由上

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

生命周期可以分为:PRE:请求在被路由之前执行该过滤器

                            POST请求被路由到微服务之后执行该过滤器(可以用来实现响应头的修改,收集统计信息和指标、响应发送给客户端、流出日志、流量监控)等功能。

网关过滤器

过滤器描述
AddRequestHeader网关过滤器工长,在请求头添加自定义键值对
AddRequestParameter在请求中添加请求参数的键值对
AddResponseHeader在响应头中添加键值对
Hystrix---HystrixCommand将断路器引入网关路由中,让服务免受级联故障的影响,并在故障时提供回退响应。在使用时需要一个名为“HystrixCommand”参数
PrefixPath用于使用简单的Prefix参数
PreserveHostHeader用于设置路由过滤器的属性,检查是否发送原始主机头或由HTTP客户端确定主机头。
RequestRateLimiter用于确定当前请求是否允许继续,如果不允许,则返回提示HTTP 429 -Too Many Requests。
RedirectTo用于接收请求的状态和URL的参数。该状态是一个重定向的300系列的HTTP代码,如301。URL是Location头部的值
RemoveNonProxyHeaders用于从转发的请求头中删除请求头
RemoveRequestHeader删除请求头,需要请求头名
RemoveResponseHeader
删除响应头,它需要响应头名
RewritePath使用java正则重写请求路径
SaveSession
在转发下游调用之前强制执行保存Session操作
SecureHeaders为响应添加安全头
SetPath
允许通过路径的模版段来操作请求路径,spring框架的URI模版,支持多种匹配
SetResponseHeader用于设置响应头,需要一个Key-Value对
SetStatus用于设置请求响应状态,需要一个Status参数,该参数的值必须是有效的Spring HttpStatus.例如,整型的404或者枚举类型的字符串NOT_FOUND。
StripPrefix
用于剥离前缀。需要parts参数,表明在请求被发送到下游之前从请求路径中剥离的元素数量。
Retry

用于进行重试,它需要Retries\Statuses\Methods\Series参数。如下:Retries:重试的次数。

Statuses:重试的Http状态代码。org.springframework.http.HttpStatus.

Methods:重试的Http方法。org.springframework.http.HttpMethod.

Series:重试的状态代码。org.springframework.http.HttpStatus.Series.

RequestSize限制请求的大小,当请求超过限制时启用,限制请求到达下游服务。该过滤器将RequestSize(请求的大小)作为参数

例子:spring.cloud.gateway.routes[0].filters[0]=AddRequestParameter=key_name,key_value


全局过滤器

内置了9种:

它在exchange的属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的值中查找URI

全局过滤器描述
Forward Routing Filter如果uri是forward协议,将用springDispatcherHandler处理
LoadBalancerClient Filter如果是lb协议,则用LoadBalancerClient将名称(lb://myservice中的myservice)解析为实际的主机和端口,并替换URI中的相关属性。还会查询GATEWAY_PREFIX_ATTR属性,判断它是否等于lb.
Netty Routing Filter若URI使用的是http或者https协议,则运行netty路由过滤器。它用NettyHttpClient发出下游代理请求。响应放在ServerWebExchangeUtils,CLIENT_RESPONSE_ATTR的交换属性中,以便在以后的过滤器中使用

Netty Write Response Filter

(netty响应过滤器)

如果CLIENT_RESPONSE_ATTR的值中存在NettyHtpClientResponse,则运行Netty写响应过滤器,它在所有其他过滤器完成后运行,并将代理响应写回到网关客户端的响应数据中
RouteToRequestUrl Filter若GATE_WAY_ROUTE_ATTR的值中存在route对象,则运行路由到请求请求地址,它根据请求URI创建一个新的URI,新URI位于ServerWebExchangeUtils.GATEWAY_REQUEST_URI_ATTR中,如果uri具有协议前缀,例如lb:ws//serviceid,则将从URI中剥离lb协议并放置在ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR中,以便稍后在过滤器中使用

Websocket Routing Filter

(ws路由过滤器)

如果是ws协议或者wss协议,则运行Websocket路由过滤器,利用Spring Web Socket底层代码将Websocket请求转发到下游。可以通过在URI前添加lb来对websockets进行负载均衡,例如lb:ws://serviecid,注意:如果在普通HTTP上使用SockJS作为回退,则应配置正常的HTTP路由及Websocket路由

Gateway Metrics Filter

(网关指标过滤器)

启用它需要添加spring-boot-starter-actuator的依赖。默认情况下只要属性spring.cloud.gateway.metrics.enabled未设置false,网关指标过滤器就会运行。此过滤器会添加一个名为gateway.requests的指标,包含以下属性:

routeid:路由ID.

routeUri:API将被路由到的URI.

outcome:由HttpStatus.Series分类的结果。

status:HTTP请求返给客户端的状态

Combined Global Filter and GatewayFilter Ordering

(组合式全局过滤器和网关过滤器排序)

当请求进入并匹配到一个路由时,Filtering Web Handler会将GlobalFilter的所有实例和GatewayFilter的所有路由特定实例添加到过滤器链中,这个组合的过滤器链由org.springframework.core.Ordered接口排序,通过gatOrder()方法或注解@Order来设置。spring cloud gateway对过滤器逻辑执行的PRE阶段和POST阶段进行了区分。

Marking An Exchange As Routed

(路由交换)

网关在路由了ServerWebExchange后,会通过将gatewayAlreadyRouted添加到exchange属性来将交换标识为路由。一旦请求被标识为路由,则其他路由过滤器会跳过该请求。可以用便捷方法将交换标识为路由,或检查交换是否已路由。


五、限流

一般情况用户增长流量越多越好,但实际除正常流量外还存在非正常比如网络攻击、恶意爬虫等会过度消耗网络服务资源的情况限流和缓存、降级一样,也是保护高并发系统的利器。

常见限流措施:

限制总并发数:如数据库连接池、线程池。

限制瞬时并发数。如Nginx的limit_conn模块

限制时间窗口内的平均速率。,如Nginx的limit_req模块

限制消息中间件的消费速率

限制远程接口的调用速率

限制每秒的平均速率

限制每秒的平均速率

对线程池进行隔离。如果超过线程池的负载,则进行熔断。

通过Tomcat容器限制线程数来控制并发。

除上述,还可根据网络连接数、网络流量、时间窗口的平均速度、用户访问频次、IP地址、URI、CPU和内存负载等来限流。

一般限流都在网关层实现,比如使用Nginx、Zuul、Spring Cloud gateway、Openresty、Kong等。当然也可在应用层通过AOP实现限流。


限流算法

1、计算法

简单粗暴、每过来一个请求就把计数器+1,达到设定的值则后续请求会被全部拒绝,等单位时间结束后把计数恢复成0,重新开始计数。

会存在突刺现象:若设定QPS为100,如果1s内前100ms已经通过来100个请求,则后面的900ms内的请求全部会被拒绝。若时间设置短些依旧不能解决突刺现象,反而会增加计数器的负担。

为结局突刺现象,可以采取平滑的限流算法:漏桶算法和令牌桶算法。

2、漏桶法

原理:把请求先放入漏桶里等待,然后漏桶以一定的速度处理进入漏桶中的请求,如果请求的进入速度过大,则导致漏桶装不下请求而拒绝后续的请求。

两个决定限流情况的变量:桶的大小,漏洞的大小

一般硬件资源是特定的(漏桶的漏出速率是固定的),所以即使在理想状态下(没有阻塞),该算法对突发流量依然缺乏效率

3、令牌桶算法

当请求到达时,先去令牌桶中取得一个令牌(排号),然后等待响应。

好处:可以方便的改变速率,按需改变放入桶中的令牌速率;可以定时或者根据实时计算来增加令牌数量。

采用令牌桶算法的框架主要:RateLimiter\Bucket4j\RateLimitJ。


spring cloud gateway内置的限流工厂实现限流

内置了RequestRateLimiterGatewayFilterFactory,底层使用Redis和Lua脚本实现。

依赖:

<denpendency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</denpendency>
<denpendency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</denpendency>
java:
public KeyResolver ipKeyResolver(){
 return exchange->Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}

spring配置:

spring:
   redis:
     host:..
     ...
   cloud:
     gateway:
       routes:
       -  id:requestratelimiter_route
         uri:http://example.org
         filters:
         -  name:RequestRateLimiter
           args: 
             redis-rate-limiter.replenishRate:10 #允许用户每秒执行多少请求,而不会丢弃任何请求。这是令牌桶填充的速率。
            redis-rate-limiter.burstCapacity:20 #一秒钟内允许执行的最大请求数。这是令牌桶可以容纳的令牌数。将此值设置为零将阻止所有请求。
            key-resolver: "#{@userkeyResolver}" #根据关键字标识的限流

当被限流成功,控制台:429 TOO_MANY_REQUESTS,说明通过IP地址限流已成功实现。


实现高可用的常见措施有降级、限流、异地多活等,而实现高并发的常用措施有异步、缓存等。

微服务系统中要实现网关高可用可采用请求发送到Nginx或者F5,接着Nginx或者F5将请求转入后端的spring cloud gateway集群,然后集群通过服务中心(Eureka\consul\Nacos)调用后端服务。


Spring Cloud Gateway端点

spring cloud gateway提供的端点,如获取过滤器列表、获取路由列表、获取路由信息、添加路由信息、刷新路由信息等。

依赖:

<denpendency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</denpendency>
<denpendency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</denpendency>

都在/actuator/gateway路径下。

端点列表:

IDHTTP描述
globalfiltersGET所有的全局过滤器
routefiltersGET所有的过滤器工厂
refreshPOST清空路由缓存
routesGET获取所有路由列表
routes/{id}GET获取指定id的路由信息
routes/{id}POST添加一个路由
routes/{id}DELETE移除一个路由

开启端点支持:

management.endpoint.gateway.enabled=true

management.endpoints.web.exposure.include=gateway

添加动态路由:

POST请求 /actuator/gateway/routes/hello3

{
  "id":"hello3",
  "predicates":[{
       "name": "Path",
       "args":{
          "_genkey_0": "hello3/**"
       }
   }],
   "filters":[],
   "uri": "http://www.1json.com", 
   "order":0
}

如果没有返回成功消息,可调用刷新接口刷新路由列表。