服务热线
135-6963-3175
Spring Cloud Gateway详解
文章概要:
1、Spring cloud gateway的作用
2、网关的运行原理
3、网关断言和自定义路由
5、网关过滤器
6、服务转发
7、网关实现限流
Zuul与Spring cloud gateway的区别
区别 | Zuul | spring cloud gateway |
底层 | servlet2.5兼容servlet3.x | 基于springboot2.x,spring5+ |
阻塞 | 阻塞式Servlet API | Reactor 非阻塞式API,支持websockets |
长链接 | 不支持 | 支持 |
请求方式 | 同步,数据封装在RequestContext里 | 异步,数据封装在ServerWebExchange |
支持服务器 | tomcat\undertow | netty |
支持路由规则 | 仅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的路由规则:
谓词工厂 | 类型 | 说明 |
AfterRoutePredicateFactory | datetime | 请求时间满足在配置时间之后 |
BeforeRoutePredicateFactory | datetime | 请求时间满足在配置时间之前 |
BetweenRoutePredicateFactory | datetime | 请求时间满足在配置时间之间 |
CookieRoutePredicateFactory | Cookie | 请求指定Cookie正则匹配指定值 |
HeaderRoutePredicateFactory | Header | 请求指定Header正则匹配指定值 |
CloudFoundryRouteServiceRoutePredicateFactory | Header | 请求Headers是否包含指定名称 |
MethodRoutePredicateFactory | Method | 请求Method匹配配置的Methos |
PathRoutePredicateFactory | Path | 请求路径正则匹配指定值 |
QueryRoutePredicateFactory | Queryparam | 请求查询参数正则匹配指定值 |
RemoteAddrRoutePredicateFactory | Remoteaddr | 请求远程地址匹配配置指定值 |
HostRoutePredicateFactory | Host | 请求Host匹配指定值 |
WeightRoutePredicateFactory | Weight | 权重路由工厂 |
当前路由设置多个规则时,路由优先级计算如下:
根据权重匹配:同一组路由的优先级由权重决定,优先匹配权重高的路由。
根据路由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路径下。
端点列表:
ID | HTTP | 描述 |
globalfilters | GET | 所有的全局过滤器 |
routefilters | GET | 所有的过滤器工厂 |
refresh | POST | 清空路由缓存 |
routes | GET | 获取所有路由列表 |
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 }
如果没有返回成功消息,可调用刷新接口刷新路由列表。