JavaSpringCloudGateWay

1、介绍

Alibaba Sentinel 支持对 Spring Cloud Gateway、Netflix Zuul 等主流的 API Gateway 进行限流与熔断配置。
本文将介绍如何在 Spring Cloud Gateway 中使用 Alibaba Sentinel 进行限流配置,从而代替 Hystrix.

2、集成步骤

2.1. 首先需在Gateway网关模块引入以下依赖配置

(以 Maven 为例):
方案一:只需引入 sentinel-spring-cloud-gateway-adapter 无需引入 spring-cloud-starter-alibaba-sentinel

  1. <dependency>
  2. <groupId>com.alibaba.csp</groupId>
  3. <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
  4. <version>x.y.z</version>
  5. </dependency>

方案二:如果需要在Sentinel 控制台管理 网关模块的限流,那么推荐这种配置方式
如果想引入 spring-cloud-starter-alibaba-sentinel 那么就把上面那个 adapter 替换成 spring-cloud-alibaba-sentinel-gateway

  1. <!-- 没添加该依赖的话不能从application配置文件配置sentinel -->
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.alibaba.cloud</groupId>
  8. <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
  9. </dependency>

2.2 然后在使用时只需注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可。

  1. @Configuration
  2. public class GatewayConfiguration {
  3. private final List<ViewResolver> viewResolvers;
  4. private final ServerCodecConfigurer serverCodecConfigurer;
  5. public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
  6. ServerCodecConfigurer serverCodecConfigurer) {
  7. this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
  8. this.serverCodecConfigurer = serverCodecConfigurer;
  9. }
  10. @Bean
  11. @Order(Ordered.HIGHEST_PRECEDENCE)
  12. public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
  13. // Register the block exception handler for Spring Cloud Gateway.
  14. return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
  15. }
  16. @Bean
  17. @Order(Ordered.HIGHEST_PRECEDENCE)
  18. public GlobalFilter sentinelGatewayFilter() {
  19. return new SentinelGatewayFilter();
  20. }
  21. }

2.3 现在可以选择在控制台中配置Sentinel限流规则 或者选择直接在Java代码中配置限流规则

2.3.1. 如果选择使用 Sentinel 控制台配置限流-熔断规则,那么还需要在 gateway 的配置文件中做出如下配置:

  1. spring:
  2. cloud:
  3. # Sentinel 控制台连接配置
  4. sentinel:
  5. transport:
  6. # 当前服务与控制台交互的端口号,默认为8719,同一个机器上若有多个应用于控制台交互时需要设置成不同的端口
  7. port: 8739
  8. dashboard: 10.1.3.77:9090
  9. # 服务启动时直接建立心跳连接
  10. eager: true
  11. # Sentinel 储存规则的数据源配置(我这里使用的是Nacos来存储Sentinel的限流规则)
  12. datasource:
  13. ds:
  14. nacos:
  15. # Nacos 服务地址(可配置单点,或者集群的VIP地址)
  16. server-addr: 10.1.3.76:8848
  17. dataId: ${spring.application.name}-sentinel
  18. groupId: DEFAULT_GROUP
  19. namespace: sms-dev
  20. rule-type: flow
  21. # Sentinel 控制台鉴权配置
  22. sentinel:
  23. dashboard:
  24. auth:
  25. username: sentinel
  26. password: sentinel

此时就可以在Sentinel控制台页面中配置限流规则,或者在 Nacos 配置中心中添加限流配置文件sms-gateway-sentinel

  1. [
  2. {
  3. "resource": "api-service",
  4. "limitApp": "default",
  5. "grade": 1,
  6. "count": 2,
  7. "strategy": 0,
  8. "controlBehavior": 0,
  9. "clusterMode": false
  10. },
  11. {
  12. "resource": "url-proxy-1",
  13. "limitApp": "default",
  14. "grade": 1,
  15. "count": 2,
  16. "strategy": 0,
  17. "controlBehavior": 0,
  18. "clusterMode": false
  19. }
  20. ]

2.3.2 如果选择在Java代码中配置限流规则

下面是一个详细的配置示例

  1. import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
  2. import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
  3. import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
  4. import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
  5. import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
  6. import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
  7. import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
  8. import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
  9. import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
  10. import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
  11. import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
  12. import com.alibaba.csp.sentinel.slots.block.RuleConstant;
  13. import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
  14. import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
  15. import org.springframework.beans.factory.ObjectProvider;
  16. import org.springframework.cloud.gateway.filter.GlobalFilter;
  17. import org.springframework.context.annotation.Bean;
  18. import org.springframework.context.annotation.Configuration;
  19. import org.springframework.core.Ordered;
  20. import org.springframework.core.annotation.Order;
  21. import org.springframework.http.HttpStatus;
  22. import org.springframework.http.MediaType;
  23. import org.springframework.http.codec.ServerCodecConfigurer;
  24. import org.springframework.web.reactive.function.BodyInserters;
  25. import org.springframework.web.reactive.function.server.ServerResponse;
  26. import org.springframework.web.reactive.result.view.ViewResolver;
  27. import org.springframework.web.server.ServerWebExchange;
  28. import reactor.core.publisher.Mono;
  29. import javax.annotation.PostConstruct;
  30. import java.util.*;
  31. /**
  32. * @description: 网关限流配置
  33. * @create: 2020-08-26 12:23
  34. **/
  35. @Configuration
  36. public class GatewaySentinelConfiguration {
  37. private final List<ViewResolver> viewResolvers;
  38. private final ServerCodecConfigurer serverCodecConfigurer;
  39. public GatewaySentinelConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
  40. ServerCodecConfigurer serverCodecConfigurer) {
  41. this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
  42. this.serverCodecConfigurer = serverCodecConfigurer;
  43. }
  44. @Bean
  45. @Order(Ordered.HIGHEST_PRECEDENCE)
  46. public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
  47. // Register the block exception handler for Spring Cloud Gateway.
  48. return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
  49. }
  50. @Bean
  51. @Order(Ordered.HIGHEST_PRECEDENCE)
  52. public GlobalFilter sentinelGatewayFilter() {
  53. return new SentinelGatewayFilter();
  54. }
  55. /**
  56. * Spring 容器初始化的时候执行该方法
  57. */
  58. @PostConstruct
  59. public void doInit() {
  60. // 加载网关限流规则
  61. initGatewayRules();
  62. // 加载自定义限流异常处理器
  63. initBlockHandler();
  64. }
  65. /**
  66. * 网关限流规则
  67. * 建议直接在 Sentinel 控制台上配置
  68. */
  69. private void initGatewayRules() {
  70. Set<GatewayFlowRule> rules = new HashSet<>();
  71. /*
  72. resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称
  73. count:限流阈值
  74. intervalSec:统计时间窗口,单位是秒,默认是 1 秒
  75. */
  76. // rules.add(new GatewayFlowRule("order-service")
  77. // .setCount(3) // 限流阈值
  78. // .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
  79. // --------------------限流分组----------start----------
  80. rules.add(new GatewayFlowRule("url-proxy-1")
  81. .setCount(1) // 限流阈值
  82. .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
  83. rules.add(new GatewayFlowRule("api-service")
  84. .setCount(5) // 限流阈值
  85. .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
  86. // --------------------限流分组-----------end-----------
  87. // 加载网关限流规则
  88. GatewayRuleManager.loadRules(rules);
  89. // 加载限流分组
  90. initCustomizedApis();
  91. // ---------------熔断-降级配置-------------
  92. DegradeRule degradeRule = new DegradeRule("api-service") // 资源名称
  93. .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) // 异常比率模式(秒级)
  94. .setCount(0.5) // 异常比率阈值(50%)
  95. .setTimeWindow(10); // 熔断降级时间(10s)
  96. // 加载规则.
  97. DegradeRuleManager.loadRules(Collections.singletonList(degradeRule));
  98. }
  99. /**
  100. * 自定义限流异常处理器
  101. */
  102. private void initBlockHandler() {
  103. BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
  104. @Override
  105. public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
  106. Map<String, String> result = new HashMap<>(3);
  107. result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
  108. result.put("message", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
  109. result.put("x","xx");
  110. return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
  111. .contentType(MediaType.APPLICATION_JSON)
  112. // .body(BodyInserters.fromValue(result));
  113. .body(BodyInserters.fromObject(result));
  114. }
  115. };
  116. // 加载自定义限流异常处理器
  117. GatewayCallbackManager.setBlockHandler(blockRequestHandler);
  118. }
  119. /**
  120. * 分组限流
  121. */
  122. private void initCustomizedApis() {
  123. //demo
  124. Set<ApiDefinition> definitions = new HashSet<>();
  125. // product-api 组
  126. ApiDefinition api1 = new ApiDefinition("product-api")
  127. .setPredicateItems(new HashSet<ApiPredicateItem>() {{
  128. // 匹配 /product-service/product 以及其子路径的所有请求
  129. add(new ApiPathPredicateItem().setPattern("/product-service/product/**")
  130. .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
  131. }});
  132. // order-api 组
  133. ApiDefinition api2 = new ApiDefinition("order-api")
  134. .setPredicateItems(new HashSet<ApiPredicateItem>() {{
  135. // 只匹配 /order-service/order/index
  136. add(new ApiPathPredicateItem().setPattern("/order-service/order/index"));
  137. }});
  138. definitions.add(api1);
  139. definitions.add(api2);
  140. // 加载限流分组
  141. GatewayApiDefinitionManager.loadApiDefinitions(definitions);
  142. }
  143. }

Tips:这种配置方式只适合固定的限流规则配置,如果需要灵活变动,那么建议使用上面那种方式

3、总结

如果 Gateway 用 Sentinel

  • 建议在 Sentinel 控制台对 网关模块 进行具体的限流,熔断降级配置。
  • 否则还是推荐直接用 Gateway 内置的 RequestRateLimiterHystrix 进行熔断限流配置。

还可以参考:https://github.com/alibaba/Sentinel/wiki/Guideline:-从-Hystrix-迁移到-Sentinel
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  • route 维度: 即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId* 在网关上用 Sentinel , 那么默认情况下一个 RouteId 对应的服务就是一个资源。
  • GatewayFlowRule: 网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。

自定义 API 维度: 用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如可以定义一个 API 叫 my_api,请求 path 模式为 /foo/**/baz/** 的都归到 my_api 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
官方Demo 示例:https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-spring-cloud-gateway