NIUCLOUD是一款SaaS管理后台框架多应用插件+云编译。上千名开发者、服务商正在积极拥抱开发者生态。欢迎开发者们免费入驻。一起助力发展! 广告
Spring Cloud Gateway默认为我们提供了一种限流方法:`RequestRateLimiterGatewayFilterFactory`。但这种方法实际并不能用于生产,并不能随着持久化数据的改变而动态改变限流参数,不能做到实时根据流量来改变流量阈值。本文就不介绍`RequestRateLimiterGatewayFilterFactory`了,直接为大家介绍生产中最常用的Spring Cloud Gateway结合sentinel实现限流功能。 Sentinel 从 1.6.0 版本开始提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流: * route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId * 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组 ## 一、Gateway网关集成sentinel 通过maven坐标在微服务模块zimug-server-gateway中加入sentinel客户端 ~~~ <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> ~~~ 在项目的配置文件中加上sentinel配置,因为我是用了nacos,所以去nacos配置中心修改配置文件。如果你的服务没有使用配置中心,在项目本地的application.yml里面配置就可以了。 ~~~ spring: cloud: sentinel: transport: port: 8719 dashboard: 192.168.161.3:8774 ~~~ ## 二、测试一下 zimug-server-gateway项目在nacos中配置文件中有如下两条网关路由 ![](https://img.kancloud.cn/5a/15/5a15e8ce69c081428ef57b283c5d0717_497x337.png) 我们先对这两条路由进行一下访问(本章前面小节多次写过),然后登陆sentinel控制台,看到如下信息,说明我们的网关route已经正确的被sentinel管理,我们可以针对route资源进行相关的流控、降级等配置。 ![](https://img.kancloud.cn/54/6c/546c813fcfe32b06c497d8b8fc049439_1801x360.png) 我们针对"/sysuser/pwd/reset"所代表的路由资源设置QPS=1的流控限制(每秒最大请求数1) ![](https://img.kancloud.cn/e6/6d/e66da33d7d2de7517be2e689be286c36_977x291.png) 然后我们**快速点击**向`http://127.0.0.1:8777/sysuser/pwd/reset`发送请求,得到如下结果,说明针对该路由资源的访问在网关层面就被拦截了。 ![](https://img.kancloud.cn/f9/1b/f91b102328b4016379be7d9399c7c41d_924x499.png) ## 三、深入解析 上面的流控回调信息是在`DefaultBlockRequestHandler`默认定义实现的,当被限流时会返回类似于下面的错误信息:`Blocked by Sentinel: FlowException`。 ![](https://img.kancloud.cn/b4/17/b417af9850e04699d67ccae9902088c2_1272x295.png) 如果你想针对限流进行定制化的信息响应(其实没有必要),您可以在`WebFluxCallbackManager`注册回调自定义的BlockHandler: ![](https://img.kancloud.cn/6d/40/6d40cd2692ce48d1a11b5c868952fe5e_1132x229.png) 自定义的BlockHandler代码如下,就是实现接口返回一个自定义的限流信息返回值:"系统繁忙,请稍后再试!"。其中Mono是webflux编程场景下的返回值使用方法之一。 ~~~ public class MySentinelBlockHandler implements BlockRequestHandler { @Override public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) { ErrorResult errorResult = new MySentinelBlockHandler.ErrorResult( HttpStatus.TOO_MANY_REQUESTS.value(), "系统繁忙,请稍后再试!"); // JSON result by default. return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS) .contentType(MediaType.APPLICATION_JSON_UTF8) .body(fromObject(errorResult)); } private static class ErrorResult { private final int code; private final String message; ErrorResult(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } } ~~~ 仿造本文第二小节的测试方法,在测试一次,返回结果如下: ![](https://img.kancloud.cn/0e/a0/0ea0a42be8af413b2974677af0058acf_470x115.png)