使用 Sentinel 實現(xiàn)接口限流
本文轉(zhuǎn)載自微信公眾號「運維開發(fā)故事」,作者老鄭。轉(zhuǎn)載本文請聯(lián)系運維開發(fā)故事公眾號。
在前面一篇文章我已經(jīng)對 Sentinel 做了一個簡單的介紹,相信大家對 Sentinel 有一個簡單的了解,本次主要是講 Sentinel 的使用。在 sentinel-dashboard 配置流控規(guī)則,以及使用 Sentinel 整合 RestTemplate、OpenFeign 進行流控使用(建議網(wǎng)頁版閱讀)。
安裝 sentinel dashboard
我使用的 sentinel 版本是: sentinel-dashboard-1.8.0
啟動控制臺命令:
- java -jar sentinel-dashboard-1.8.0.jar
默認啟動的是 8080 端口, 登錄賬號和密碼默認都是: sentinel。 如果需要修改啟動端口可以在啟動命令前面加 -Dserver.port=9999 進行修改。
使用介紹
通常我們在項目中對于 Sentinel 最常用的場景,就是默認的流控對接口的訪問添加流控規(guī)則。Sentinel 也提供了對于 RestTemplate 、OpenFegin 的支持。
簡單案例
1. 導(dǎo)入依賴
如果我們需要使用 Sentinel ,首先我們需要在業(yè)務(wù)服務(wù)中,導(dǎo)入 Sentinel 客戶端的依賴。下面是 Maven 的 pom 依賴。 我們可以直接使用 spring-coud-starter-alibaba-sentinel 進行快速整合。
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
- </dependency>
對于 spring-cloud-alibaba 相關(guān)的版本依賴信息如下:
- <properties>
- <spring-boot.version>2.3.10.RELEASE</spring-boot.version>
- <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
- <spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version>
- </properties>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-dependencies</artifactId>
- <version>${spring-boot.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>${spring-cloud.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-alibaba-dependencies</artifactId>
- <version>${spring-cloud-alibaba.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
2. YML 配置
我們在業(yè)務(wù)服務(wù)中導(dǎo)入了依賴過后,我們需要修改 application.yml 文件讓服務(wù)啟動過后自動注冊到 sentinel-dashboard 服務(wù)上。
- spring:
- cloud:
- sentinel:
- transport:
- port: 8719
- dashboard: localhost:8080
3. 測試接口定義
首先我們需要定義對外開放的接口。
- @RestController
- public class HelloController {
- @GetMapping("/hello")
- public String hello () {
- return "OK";
- }
- }
4. 通過控制臺配置流控規(guī)則
注意:如果已經(jīng)啟動 snetinel-dashboard 后并且啟動業(yè)務(wù)服務(wù),在 sentinel-dashboard 后臺還是沒有服務(wù)的話,我們可以先訪問一下業(yè)務(wù)服務(wù)的接口,然后在刷新snetinel-dashboard 觀察是否正常。如果還是不正常請考慮 sentinel 的 client 版本和 dashboard 是否匹配。
首先選擇自己對應(yīng)服務(wù)展開,然后選擇【簇點鏈路】 菜單。選擇需要流控的接口 /hello 然后選擇 【流控】按鈕進行流控配置
我們可以配置, 我們選擇【閥值類型】選擇【QPS】,然后設(shè)置【單機閥值】 填入 1 。表示該接口每秒鐘只能接受一個 QPS ,如果超過閾值過后就會觸發(fā) 【流控】默認 Sentinel 返回 Blocked by Sentinel (flow limiting)
5. 流控規(guī)則觸發(fā)
如果我們需要觸發(fā)流控規(guī)則我們頻繁訪問 /hello 接口即可。
- ~ curl http://127.0.0.1:8066/hello
- OK% ~ curl http://127.0.0.1:8066/hello
- ~ curl http://127.0.0.1:8066/hello
- Blocked by Sentinel (flow limiting)%
通過上面的結(jié)果我們可以看到當(dāng)單位時間內(nèi)超過閾值過后, 就會觸發(fā) flow limit
整合 RestTemplate
1. YML 配置
Sentinel 整合 Resttemplate 除了需要導(dǎo)入 spring-cloud-starter-alibaba-sentinel 開需要開啟 Sentinel 對 Resttemplate 的支持。
- resttemplate:
- sentinel:
- enabled: true
2. 創(chuàng)建 RestTemplate
如果 RestTemplate 在使用的時候需要使用到 Sentinel 的流控規(guī)則,首先需要在創(chuàng)建 RestTemplate 的時候添加 @SentinelRestTemplate 注解。注意: SentinelExceptionHandler 中的方法都是 static 方法
- @Configuration
- public class RestTemplateConfig {
- @Bean
- @ConditionalOnMissingBean(RestTemplate.class)
- @LoadBalanced
- @SentinelRestTemplate(
- blockHandler = "handlerException", blockHandlerClass = SentinelExceptionHandler.class,
- fallback = "handleFallback", fallbackClass = SentinelExceptionHandler.class)
- public RestTemplate restTemplate() {
- return new RestTemplate();
- }
- }
- // 異常處理類
- public class SentinelExceptionHandler {
- //限流熔斷業(yè)務(wù)邏輯
- public static SentinelClientHttpResponse handlerException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
- String message = JSON.toJSONString(CommonResult.error(-100,"系統(tǒng)錯誤 (限流熔斷業(yè)務(wù)邏輯)"));
- return new SentinelClientHttpResponse(message);
- }
- //異常降級業(yè)務(wù)邏輯
- public static SentinelClientHttpResponse handleFallback(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
- String message = JSON.toJSONString(CommonResult.error(-100,"系統(tǒng)錯誤 (異常降級業(yè)務(wù)邏輯)"));
- return new SentinelClientHttpResponse(message);
- }
- }
3. 接口定義
下面就是我們使用的代碼,可能寫得稍微有點復(fù)雜,我來解釋一下。首先我是通過 RestTemplate 訪問 stock-service 服務(wù)的 /getStockDetail 接口然后將接口的返回數(shù)據(jù)解析,通過CommonResult
- @Autowired
- private RestTemplate restTemplate;
- @GetMapping("/hello2")
- public CommonResult<OrderModel> hello2() {
- ParameterizedTypeReference<CommonResult<StockModel>> typeRef =
- new ParameterizedTypeReference<CommonResult<StockModel>>() {
- };
- ResponseEntity<CommonResult<StockModel>>
- forEntity = restTemplate.exchange("http://stock-service/getStockDetail", HttpMethod.GET,
- HttpEntity.EMPTY, typeRef);
- OrderModel orderModel = new OrderModel();
- orderModel.setId(100);
- orderModel.setCode("100-100");
- if (Objects.equals(forEntity.getStatusCode(), HttpStatus.OK) && Objects.nonNull(forEntity.getBody())) {
- CommonResult<StockModel> result = forEntity.getBody();
- if (result.getCode() != 1) {
- return CommonResult.error(null, result.getCode(), result.getMessage());
- }
- orderModel.setStockModel(result.getData());
- }
- return CommonResult.success(orderModel);
- }
4. 流控觸發(fā)
如果我們頻繁的訪問我們的接口 /hello2 就會出現(xiàn)限流的邏輯
~ 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":"系統(tǒng)錯誤 (限流熔斷業(yè)務(wù)邏輯)","data":null}
整合 OpenFegin
1. 導(dǎo)入 openfeign 依賴
Sentinel 整合 Openfeign 需要導(dǎo)入 spring-cloud-starter-openfeign
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-openfeign</artifactId>
- </dependency>
2. YML 配置
Sentinel 整合 Openfeign 需要開啟對 feign 的支持,配置如下:
- feign:
- sentinel:
- enabled: true
注意:啟動類上要增加 @EnableFeignClients 來配置 Openfeign 的啟用
3. 調(diào)用代碼
Feign 接口調(diào)服務(wù) stock-service 的 /getStockDetail 接口,如果觸發(fā)流控規(guī)則就會執(zhí)行 FallbackFactory 中返回 StockFeign 的本地存根方法。
- @FeignClient(name = "stock-service", fallbackFactory = StockFeignFallbackFactory.class)
- public interface StockFeign {
- @GetMapping("/getStockDetail")
- CommonResult<StockModel> getStockDetail();
- }
StockFeignFallbackFactory 類是服務(wù)降級的處理。
- @Component
- public class StockFeignFallbackFactory implements FallbackFactory<StockFeign> {
- private Logger log = LoggerFactory.getLogger(StockFeignFallbackFactory.class);
- @Override
- public StockFeign create(Throwable throwable) {
- return new StockFeign() {
- @Override
- public CommonResult<StockModel> getStockDetail() {
- log.error("調(diào)用查詢庫存詳情降級", throwable);
- return CommonResult.error(null, -100, "調(diào)用查詢庫存詳情降級");
- }
- };
- }
- }
Controller 調(diào)用代碼
- @Autowired
- private StockFeign stockFeign;
- @GetMapping("/hello1")
- public CommonResult<OrderModel> hello() {
- CommonResult<StockModel> result = stockFeign.getStockDetail();
- if (result.getCode() != 1) {
- return CommonResult.error(null, result.getCode(), result.getMessage());
- }
- StockModel stockDetail = result.getData();
- OrderModel orderModel = new OrderModel();
- orderModel.setStockModel(stockDetail);
- return CommonResult.success(orderModel);
- }
4. 業(yè)務(wù)執(zhí)行
如果我們多次訪問,Sentinel 就會觸發(fā)降級策略。然后執(zhí)行 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