Spring Cloud Gateway 04 扩展
参考:
https://cloud.tencent.com/developer/article/1538107
https://blog.51cto.com/zero01/2430532
一、Spring Cloud Gateway 的监控端点
说到监控,就应该能想到Spring Boot Actuator。而Spring Cloud Gateway基于Actuator提供了许多的监控端点。只需要在项目中添加spring-boot-starter-actuator依赖,并将 gateway 端点暴露,即可获得若干监控端点。配置示例:
==spring-cloud-gateway-server 4.x ==
management:
endpoint:
gateway:
enabled: true
endpoints:
web:
exposure:
include: gateway # 或者配置“*”暴露全部端点
Spring Cloud Gateway的监控端点如下表:
端点 | 请求方法 | 描述 |
---|---|---|
globalfilters | GET | 展示所有的全局过滤器信息 |
routefilters | GET | 展示所有的过滤器工厂信息 |
refresh | POST(无消息体) | 清空路由缓存,即刷新路由信息 |
routes | GET | 展示所有的路由信息列表 |
routes/{id} | GET | 展示指定id的路由的信息 |
routes/{id} | POST(有消息体) | 新增一个路由 |
routes/{id} | DELETE(无消息体) | 删除一个路由 |
Gateway所有的监控端点都挂载在 /actuator/gateway 路径下
- 查看 gateway 下的挂载点
- http://127.0.0.1:{{port}}/actuator/gateway
[{"href": "/actuator/gateway/routedefinitions", "methods": [GET"]},
{"href": "/actuator/gateway/globalfilters", "methods": ["GET"]},
{"href": "/actuator/gateway/routefilters", "methods": ["GET"]},
{"href": "/actuator/gateway/routes/ReactiveCompositeDiscoveryClient_YUNJIAN.ZK.GATEWAY/combinedfilters", "methods": ["GET"]},
{"href": "/actuator/gateway/routes/ReactiveCompositeDiscoveryClient_YUNJIAN.ZK.SYSTEM/combinedfilters", "methods": ["GET"]},
{"href": "/actuator/gateway/routes/test/combinedfilters", "methods": [GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.system/combinedfilters", "methods": [GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.wechat/combinedfilters", "methods": [GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.dev.tool/combinedfilters", "methods": [GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.mail/combinedfilters", "methods": [GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.file/combinedfilters", "methods": [GET"]},
{"href": "/actuator/gateway/routes", "methods": [POST",GET"]},
{"href": "/actuator/gateway/routes/ReactiveCompositeDiscoveryClient_YUNJIAN.ZK.GATEWAY", "methods": [POST",DELETE",GET"]},
{"href": "/actuator/gateway/routes/ReactiveCompositeDiscoveryClient_YUNJIAN.ZK.SYSTEM", "methods": [POST",DELETE",GET"]},
{"href": "/actuator/gateway/routes/test", "methods": [POST",DELETE",GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.system", "methods": [POST",DELETE",GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.wechat", "methods": [POST",DELETE",GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.dev.tool", "methods": [POST",DELETE",GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.mail", "methods": [POST",DELETE",GET"]},
{"href": "/actuator/gateway/routes/yunjian.zk.file", "methods": [POST",DELETE",GET"]},
{"href": "/actuator/gateway/", "methods": [GET"]},
{"href": "/actuator/gateway/routepredicates", "methods": [GET"]},
{"href": "/actuator/gateway/refresh", "methods": [POST"]}]
例如globalfilters端点的完整访问路径是 /actuator/gateway/globalfilters。该端点主要是查看Gateway启用了哪些全局过滤器以及它们的执行顺序(数字越小越优先执行)。
2. 查看Gateway启用的全局过滤器
- http://127.0.0.1:{{port}}/actuator/gateway/globalfilters
结果如下:
{
"org.springframework.cloud.gateway.filter.NettyRoutingFilter@2fb70301": 2147483647,
"org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@34b462e0": 10000,
"org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter@53d808ea": 10150,
"org.springframework.cloud.gateway.filter.ForwardRoutingFilter@1e592ef2": 2147483647,
"org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@3bbc47c9": -1,
"org.springframework.cloud.gateway.filter.ForwardPathFilter@6bf77ee": 0,
"org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@71f4aeb6": -2147482648,
"org.springframework.cloud.gateway.filter.GatewayMetricsFilter@25109d84": 0,
"org.springframework.cloud.gateway.filter.LoadBalancerServiceInstanceCookieFilter@404dc999": 10151,
"org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@c3d4bd7": 2147483646,
"org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@5b0575d0": -2147483648
}
- 查看 Gateway 启用的过滤器工厂
- http://127.0.0.1:{{port}}/actuator/gateway/routefilters
{
"[RemoveRequestParameterGatewayFilterFactory@6296e4bf configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[SetPathGatewayFilterFactory@33a8f553 configClass = SetPathGatewayFilterFactory.Config]": null,
"[CacheRequestBodyGatewayFilterFactory@43f7f48d configClass = CacheRequestBodyGatewayFilterFactory.Config]": null,
"[RewritePathGatewayFilterFactory@71d78cac configClass = RewritePathGatewayFilterFactory.Config]": null,
"[ModifyRequestBodyGatewayFilterFactory@dd77e0d configClass = ModifyRequestBodyGatewayFilterFactory.Config]": null,
"[RetryGatewayFilterFactory@6e12f38c configClass = RetryGatewayFilterFactory.RetryConfig]": null,
"[SaveSessionGatewayFilterFactory@176e839e configClass = Object]": null,
"[AddRequestHeaderGatewayFilterFactory@61fa3583 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[AddRequestParameterGatewayFilterFactory@1abbc1d4 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[RequestSizeGatewayFilterFactory@4866e0a7 configClass = RequestSizeGatewayFilterFactory.RequestSizeConfig]": null,
"[SetResponseHeaderGatewayFilterFactory@240291d9 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[RequestHeaderToRequestUriGatewayFilterFactory@16361e61 configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[PrefixPathGatewayFilterFactory@1e14b269 configClass = PrefixPathGatewayFilterFactory.Config]": null,
"[SetRequestHeaderGatewayFilterFactory@284b487f configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[AddRequestHeadersIfNotPresentGatewayFilterFactory@2a19c36b configClass = Object]": null,
"[RewriteLocationResponseHeaderGatewayFilterFactory@451a4187 configClass = RewriteLocationResponseHeaderGatewayFilterFactory.Config]": null,
"[RemoveRequestHeaderGatewayFilterFactory@23dc70c1 configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[AddResponseHeaderGatewayFilterFactory@4b20aa21 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[DedupeResponseHeaderGatewayFilterFactory@4eec5fa6 configClass = DedupeResponseHeaderGatewayFilterFactory.Config]": null,
"[RedirectToGatewayFilterFactory@2a0ce342 configClass = RedirectToGatewayFilterFactory.Config]": null,
"[RemoveJsonAttributesResponseBodyGatewayFilterFactory@3cae4518 configClass = Object]": null,
"[RewriteRequestParameterGatewayFilterFactory@3340ff7c configClass = RewriteRequestParameterGatewayFilterFactory.Config]": null,
"[MapRequestHeaderGatewayFilterFactory@364c93e6 configClass = MapRequestHeaderGatewayFilterFactory.Config]": null,
"[StripPrefixGatewayFilterFactory@52963839 configClass = StripPrefixGatewayFilterFactory.Config]": null,
"[RewriteResponseHeaderGatewayFilterFactory@6ee186f3 configClass = RewriteResponseHeaderGatewayFilterFactory.Config]": null,
"[SetRequestHostHeaderGatewayFilterFactory@2b4954a4 configClass = SetRequestHostHeaderGatewayFilterFactory.Config]": null,
"[SecureHeadersGatewayFilterFactory@6b94c200 configClass = SecureHeadersGatewayFilterFactory.Config]": null,
"[RequestHeaderSizeGatewayFilterFactory@6b6c0b7c configClass = RequestHeaderSizeGatewayFilterFactory.Config]": null,
"[ModifyResponseBodyGatewayFilterFactory@65c689e7 configClass = ModifyResponseBodyGatewayFilterFactory.Config]": null,
"[SetStatusGatewayFilterFactory@60fe75f7 configClass = SetStatusGatewayFilterFactory.Config]": null,
"[RemoveResponseHeaderGatewayFilterFactory@4d3990a5 configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[PreserveHostHeaderGatewayFilterFactory@77d3c3d7 configClass = Object]": null
}
- 查看 Gateway里定义的路由
- http://127.0.0.1:{{port}}/actuator/gateway/routefilters
[{
"predicate": "Paths: [/test/baidu], match trailing slash: true",
"route_id": "test",
"filters": ["[[StripPrefix parts = 2], order = 1]"],
"uri": "https://www.baidu.com:443",
"order": 0
} ... ]
二、Spring Cloud Gateway 限流相关
在高并发的系统中,限流往往是一个绕不开的话题,我们都知道网关是流量的入口,所以在网关上做限流也是理所当然的。Spring Cloud Gateway内置了一个过滤器工厂,用于提供限流功能,这个过滤器工厂就是是RequestRateLimiterGatewayFilterFactory,该过滤器工厂基于令牌桶算法实现限流功能。
目前,该过滤器工厂默认使用 RedisRateLimiter 作为限速器,需要依赖Redis来存储限流配置,以及统计数据等。当然你也可以实现自己的RateLimiter,只需实现 org.springframework.cloud.gateway.filter.ratelimit.RateLimiter 接口,或者继承 org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter抽象类
Tips:
- Redis Rate Limiter的实现基于这篇文章:Scaling your API with rate limiters
- Spring 官方引用的令牌桶算法文章:Token bucket
- 关于令牌桶之类的限流算法可以参考另一篇文章,这里就不过多赘述了:应用限流及其常见算法
动手实践
- 添加Redis依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
- 添加 redis 配置:
spring:
cloud:
gateway:
routes:
- id: user-center
uri: lb://user-center
predicates:
- Path=/user-center/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 令牌桶的上限
redis-rate-limiter.burstCapacity: 2
# 使用SpEL表达式从Spring容器中获取Bean对象
key-resolver: "#{@pathKeyResolver}"
# redis相关
redis:
host: 127.0.0.1
port: 6379
- 编写一个KeyResolver,用于定义针对什么进行限流。例如按照访问路径限流,就写一个针对访问路径的KeyResolver;按照请求参数限流,那就写一个针对请求参数的KeyResolver,以此类推。这里我们按照访问路径限流,具体实现代码如下:
@Configuration
public class RaConfiguration {
/**
* 按照Path限流
*
* @return key
*/
@Bean
public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest()
// 获取path
.getPath()
.toString()
);
}
}
从代码的实现不难看出,实际就只是返回了一个访问路径,这样限流规则就会作用到访问路径上。例如访问:http:// G A T E W A Y U R L / u s e r s / 1 ,对于这个路径,它的 r e d i s − r a t e − l i m i t e r . r e p l e n i s h R a t e = 1 , r e d i s − r a t e − l i m i t e r . b u r s t C a p a c i t y = 2 。访问: h t t p : / / {GATEWAY_URL}/users/1,对于这个路径,它的redis-rate-limiter.replenishRate = 1,redis-rate-limiter.burstCapacity = 2。 访问:http:// GATEWAYURL/users/1,对于这个路径,它的redis−rate−limiter.replenishRate=1,redis−rate−limiter.burstCapacity=2。访问:http://{GATEWAY_URL}/shares/1,对于这个路径,它的redis-rate-limiter.replenishRate = 1,redis-rate-limiter.burstCapacity = 2;以此类推…
测试
接下来进行一个简单的测试,看看限流是否起作用了。持续频繁访问某个路径,当令牌桶的令牌被消耗完了,就会返回 429 这个HTTP状态码。
若有凝问或错误,请指出,我好及时改正,让我们一起进步!
email : binary_space@126.com
qq : 103 586 2795
敲门砖: 代码谱写人生