使用 Sentinel 实现接口限流

开发
本次主要是讲 Sentinel 的使用。在 sentinel-dashboard 配置流控规则,以及使用 Sentinel 整合 RestTemplate、OpenFeign 进行流控使用(建议网页版阅读)。

[[399418]]

本文转载自微信公众号「运维开发故事」,作者老郑。转载本文请联系运维开发故事公众号。

在前面一篇文章我已经对 Sentinel 做了一个简单的介绍,相信大家对 Sentinel 有一个简单的了解,本次主要是讲 Sentinel 的使用。在 sentinel-dashboard 配置流控规则,以及使用 Sentinel 整合 RestTemplate、OpenFeign 进行流控使用(建议网页版阅读)。

安装 sentinel dashboard

我使用的 sentinel 版本是: sentinel-dashboard-1.8.0

启动控制台命令:

  1. java -jar sentinel-dashboard-1.8.0.jar 

默认启动的是 8080 端口, 登录账号和密码默认都是: sentinel。 如果需要修改启动端口可以在启动命令前面加 -Dserver.port=9999 进行修改。

使用介绍

通常我们在项目中对于 Sentinel 最常用的场景,就是默认的流控对接口的访问添加流控规则。Sentinel 也提供了对于 RestTemplate 、OpenFegin 的支持。

简单案例

1. 导入依赖

如果我们需要使用 Sentinel ,首先我们需要在业务服务中,导入 Sentinel 客户端的依赖。下面是 Maven 的 pom 依赖。 我们可以直接使用 spring-coud-starter-alibaba-sentinel 进行快速整合。

  1. <dependency> 
  2.   <groupId>com.alibaba.cloud</groupId> 
  3.   <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> 
  4. </dependency> 

 

对于 spring-cloud-alibaba 相关的版本依赖信息如下:

  1. <properties> 
  2.   <spring-boot.version>2.3.10.RELEASE</spring-boot.version> 
  3.   <spring-cloud.version>Hoxton.SR8</spring-cloud.version> 
  4.   <spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version> 
  5. </properties> 
  6. <dependencyManagement> 
  7.   <dependencies> 
  8.     <dependency> 
  9.       <groupId>org.springframework.boot</groupId> 
  10.       <artifactId>spring-boot-dependencies</artifactId> 
  11.       <version>${spring-boot.version}</version> 
  12.       <type>pom</type> 
  13.       <scope>import</scope> 
  14.     </dependency> 
  15.     <dependency> 
  16.       <groupId>org.springframework.cloud</groupId> 
  17.       <artifactId>spring-cloud-dependencies</artifactId> 
  18.       <version>${spring-cloud.version}</version> 
  19.       <type>pom</type> 
  20.       <scope>import</scope> 
  21.     </dependency> 
  22.      
  23.     <dependency> 
  24.       <groupId>com.alibaba.cloud</groupId> 
  25.       <artifactId>spring-cloud-alibaba-dependencies</artifactId> 
  26.       <version>${spring-cloud-alibaba.version}</version> 
  27.       <type>pom</type> 
  28.       <scope>import</scope> 
  29.     </dependency> 
  30.   </dependencies> 
  31. </dependencyManagement> 

 

 

 

2. YML 配置

我们在业务服务中导入了依赖过后,我们需要修改 application.yml 文件让服务启动过后自动注册到 sentinel-dashboard 服务上。

  1. spring: 
  2.   cloud: 
  3.     sentinel: 
  4.       transport: 
  5.         port: 8719 
  6.         dashboard: localhost:8080 

3. 测试接口定义

首先我们需要定义对外开放的接口。

  1. @RestController 
  2. public class HelloController { 
  3.      
  4.     @GetMapping("/hello"
  5.     public String hello () { 
  6.         return "OK"
  7.     } 

4. 通过控制台配置流控规则

注意:如果已经启动 snetinel-dashboard 后并且启动业务服务,在 sentinel-dashboard 后台还是没有服务的话,我们可以先访问一下业务服务的接口,然后在刷新snetinel-dashboard 观察是否正常。如果还是不正常请考虑 sentinel 的 client 版本和 dashboard 是否匹配。

首先选择自己对应服务展开,然后选择【簇点链路】 菜单。选择需要流控的接口 /hello 然后选择 【流控】按钮进行流控配置

我们可以配置, 我们选择【阀值类型】选择【QPS】,然后设置【单机阀值】 填入 1 。表示该接口每秒钟只能接受一个 QPS ,如果超过阈值过后就会触发 【流控】默认 Sentinel 返回 Blocked by Sentinel (flow limiting)

5. 流控规则触发

如果我们需要触发流控规则我们频繁访问 /hello 接口即可。

 

  1. ~ curl http://127.0.0.1:8066/hello 
  2. OK%                                                                                                                                                   ~ curl http://127.0.0.1:8066/hello 
  3. ~ curl http://127.0.0.1:8066/hello 
  4. Blocked by Sentinel (flow limiting)% 

通过上面的结果我们可以看到当单位时间内超过阈值过后, 就会触发 flow limit

整合 RestTemplate

1. YML 配置

Sentinel 整合 Resttemplate 除了需要导入 spring-cloud-starter-alibaba-sentinel 开需要开启 Sentinel 对 Resttemplate 的支持。

  1. resttemplate: 
  2.   sentinel: 
  3.     enabled: true 

2. 创建 RestTemplate

如果 RestTemplate 在使用的时候需要使用到 Sentinel 的流控规则,首先需要在创建 RestTemplate 的时候添加 @SentinelRestTemplate 注解。注意: SentinelExceptionHandler 中的方法都是 static 方法

  1. @Configuration 
  2. public class RestTemplateConfig { 
  3.     @Bean 
  4.     @ConditionalOnMissingBean(RestTemplate.class) 
  5.     @LoadBalanced 
  6.     @SentinelRestTemplate( 
  7.             blockHandler = "handlerException", blockHandlerClass = SentinelExceptionHandler.class, 
  8.             fallback = "handleFallback", fallbackClass = SentinelExceptionHandler.class) 
  9.     public RestTemplate restTemplate() { 
  10.         return new RestTemplate(); 
  11.     } 
  12. // 异常处理类 
  13. public class SentinelExceptionHandler { 
  14.      
  15.     //限流熔断业务逻辑 
  16.     public static SentinelClientHttpResponse handlerException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) { 
  17.         String message = JSON.toJSONString(CommonResult.error(-100,"系统错误 (限流熔断业务逻辑)")); 
  18.         return new SentinelClientHttpResponse(message); 
  19.     } 
  20.     //异常降级业务逻辑 
  21.     public static SentinelClientHttpResponse handleFallback(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) { 
  22.         String message = JSON.toJSONString(CommonResult.error(-100,"系统错误 (异常降级业务逻辑)")); 
  23.         return new SentinelClientHttpResponse(message); 
  24.     } 

3. 接口定义

下面就是我们使用的代码,可能写得稍微有点复杂,我来解释一下。首先我是通过 RestTemplate 访问 stock-service 服务的 /getStockDetail 接口然后将接口的返回数据解析,通过CommonResult 实例对象进行接收, 如果失败就返回错误信息。

  1. @Autowired 
  2. private RestTemplate restTemplate; 
  3. @GetMapping("/hello2"
  4. public CommonResult<OrderModel> hello2() { 
  5.   ParameterizedTypeReference<CommonResult<StockModel>> typeRef = 
  6.     new ParameterizedTypeReference<CommonResult<StockModel>>() { 
  7.   }; 
  8.   ResponseEntity<CommonResult<StockModel>> 
  9.     forEntity = restTemplate.exchange("http://stock-service/getStockDetail", HttpMethod.GET, 
  10.                                       HttpEntity.EMPTY, typeRef); 
  11.   OrderModel orderModel = new OrderModel(); 
  12.   orderModel.setId(100); 
  13.   orderModel.setCode("100-100"); 
  14.   if (Objects.equals(forEntity.getStatusCode(), HttpStatus.OK) && Objects.nonNull(forEntity.getBody())) { 
  15.     CommonResult<StockModel> result = forEntity.getBody(); 
  16.     if (result.getCode() != 1) { 
  17.       return CommonResult.error(null, result.getCode(), result.getMessage()); 
  18.     } 
  19.     orderModel.setStockModel(result.getData()); 
  20.   } 
  21.   return CommonResult.success(orderModel); 

4. 流控触发

如果我们频繁的访问我们的接口 /hello2 就会出现限流的逻辑

~ curl http://127.0.0.1:8066/hello2

{"code":1,"message":"this is a success message","data":{"id":100,"code":"100-100","stockModel":{"id":1,"code":"STOCK==>1000"}}}

~ curl http://127.0.0.1:8066/hello2

{"code":-100,"message":"系统错误 (限流熔断业务逻辑)","data":null}

整合 OpenFegin

1. 导入 openfeign 依赖

Sentinel 整合 Openfeign 需要导入 spring-cloud-starter-openfeign

  1. <dependency> 
  2.   <groupId>org.springframework.cloud</groupId> 
  3.   <artifactId>spring-cloud-starter-openfeign</artifactId> 
  4. </dependency> 

 

2. YML 配置

Sentinel 整合 Openfeign 需要开启对 feign 的支持,配置如下:

  1. feign: 
  2.   sentinel: 
  3.     enabled: true 

注意:启动类上要增加 @EnableFeignClients 来配置 Openfeign 的启用

3. 调用代码

Feign 接口调服务 stock-service 的 /getStockDetail 接口,如果触发流控规则就会执行 FallbackFactory 中返回 StockFeign 的本地存根方法。

  1. @FeignClient(name = "stock-service", fallbackFactory = StockFeignFallbackFactory.class) 
  2. public interface StockFeign { 
  3.     @GetMapping("/getStockDetail"
  4.     CommonResult<StockModel> getStockDetail(); 

StockFeignFallbackFactory 类是服务降级的处理。

  1. @Component 
  2. public class StockFeignFallbackFactory implements FallbackFactory<StockFeign> { 
  3.     private Logger log = LoggerFactory.getLogger(StockFeignFallbackFactory.class); 
  4.     @Override 
  5.     public StockFeign create(Throwable throwable) { 
  6.         return new StockFeign() { 
  7.             @Override 
  8.             public CommonResult<StockModel> getStockDetail() { 
  9.                 log.error("调用查询库存详情降级", throwable); 
  10.                 return CommonResult.error(null, -100, "调用查询库存详情降级"); 
  11.             } 
  12.         }; 
  13.     } 

Controller 调用代码

  1. @Autowired 
  2. private StockFeign stockFeign; 
  3. @GetMapping("/hello1"
  4. public CommonResult<OrderModel> hello() { 
  5.   CommonResult<StockModel> result = stockFeign.getStockDetail(); 
  6.   if (result.getCode() != 1) { 
  7.     return CommonResult.error(null, result.getCode(), result.getMessage()); 
  8.   } 
  9.   StockModel stockDetail = result.getData(); 
  10.   OrderModel orderModel = new OrderModel(); 
  11.   orderModel.setStockModel(stockDetail); 
  12.   return CommonResult.success(orderModel); 

4. 业务执行

如果我们多次访问,Sentinel 就会触发降级策略。然后执行 StockFeignFallbackFactory 的本地存根方法返回

源码地址

gitee: https://gitee.com/zhengsh/excavator

参考

https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html#_spring_cloud_alibaba_sentinel

https://segmentfault.com/a/1190000019070557

 

责任编辑:武晓燕 来源: 运维开发故事
相关推荐

2022-05-29 21:38:11

限流熔断流量

2022-05-03 19:38:15

限流微服务Sentinel

2024-02-04 10:08:34

2023-09-18 14:39:02

2021-05-21 08:30:26

Sentinel GateWay 微服务

2022-05-09 07:35:48

动态集群限流

2021-03-30 10:46:42

SpringBoot计数器漏桶算法

2020-08-03 08:04:04

限流算法Sentinel

2021-03-16 08:31:59

微服务Sentinel雪崩效应

2021-03-22 08:06:59

SpringBootSentinel项目

2019-07-09 12:30:50

开源技术 软件

2023-04-26 09:16:17

2021-11-04 10:11:02

Sentinel网关限流

2023-04-06 08:52:54

Sentinel分布式系统

2023-09-06 15:22:26

限流Java

2023-11-20 10:09:59

2024-04-03 09:18:03

Redis数据结构接口防刷

2023-01-17 09:41:26

Redis接口限流

2023-09-25 15:34:14

2022-11-16 17:16:41

spring限流
点赞
收藏

51CTO技术栈公众号