Spring Cloud Alibaba:Sentinel實現(xiàn)熔斷與限流
Spring Cloud Alibaba 致力于提供微服務開發(fā)的一站式解決方案塑娇,Sentinel 作為其核心組件之一澈侠,具有熔斷與限流等一系列服務保護功能,本文將對其用法進行詳細介紹埋酬。
隨著微服務的流行哨啃,服務和服務之間的穩(wěn)定性變得越來越重要。 Sentinel 以流量為切入點写妥,從流量控制拳球、熔斷降級、系統(tǒng)負載保護等多個維度保護服務的穩(wěn)定性珍特。
Sentinel具有如下特性:
豐富的應用場景:承接了阿里巴巴近 10 年的雙十一大促流量的核心場景祝峻,例如秒殺,可以實時熔斷下游不可用應用扎筒;
完備的實時監(jiān)控:同時提供實時的監(jiān)控功能莱找。可以在控制臺中看到接入應用的單臺機器秒級數(shù)據(jù)嗜桌,甚至 500 臺以下規(guī)模的集群的匯總運行情況奥溺;
廣泛的開源生態(tài):提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud骨宠、Dubbo浮定、gRPC 的整合;
完善的 SPI 擴展點:提供簡單易用层亿、完善的 SPI 擴展點壶唤。您可以通過實現(xiàn)擴展點,快速的定制邏輯棕所。
Sentinel控制臺是一個輕量級的控制臺應用闸盔,它可用于實時查看單機資源監(jiān)控及集群資源匯總,并提供了一系列的規(guī)則管理功能琳省,如流控規(guī)則迎吵、降級規(guī)則、熱點規(guī)則等针贬。
我們先從官網下載Sentinel击费,這里下載的是sentinel-dashboard-1.6.3.jar文件,下載地址:https://github.com/alibaba/Sentinel/releases
下載完成后在命令行輸入如下命令運行Sentinel控制臺:
java -jar sentinel-dashboard-1.6.3.jarCopy to clipboardErrorCopied
Sentinel控制臺默認運行在8080端口上桦他,登錄賬號密碼均為sentinel蔫巩,通過如下地址可以進行訪問:http://localhost:8080
Sentinel控制臺可以查看單臺機器的實時監(jiān)控數(shù)據(jù)。
這里我們創(chuàng)建一個sentinel-service模塊,用于演示Sentinel的熔斷與限流功能圆仔。
在pom.xml中添加相關依賴垃瞧,這里我們使用Nacos作為注冊中心,所以需要同時添加Nacos的依賴:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>Copy to clipboardErrorCopied
在application.yml中添加相關配置坪郭,主要是配置了Nacos和Sentinel控制臺的地址:
server:
? port: 8401
spring:
? application:
? ? name: sentinel-service
? cloud:
? ? nacos:
? ? ? discovery:
? ? ? ? server-addr: localhost:8848 #配置Nacos地址
? ? sentinel:
? ? ? transport:
? ? ? ? dashboard: localhost:8080 #配置sentinel dashboard地址
? ? ? ? port: 8719
service-url:
? user-service: http://nacos-user-service
management:
? endpoints:
? ? web:
? ? ? exposure:
? ? ? ? include: '*'Copy to clipboardErrorCopied
Sentinel Starter 默認為所有的 HTTP 服務提供了限流埋點个从,我們也可以通過使用@SentinelResource來自定義一些限流行為。
創(chuàng)建RateLimitController類
用于測試熔斷和限流功能歪沃。
/**
* 限流功能
* Created by macro on 2019/11/7.
*/@RestController@RequestMapping("/rateLimit")publicclassRateLimitController{/**
? ? * 按資源名稱限流嗦锐,需要指定限流處理邏輯
? ? */@GetMapping("/byResource")@SentinelResource(value="byResource",blockHandler="handleException")publicCommonResultbyResource(){returnnewCommonResult("按資源名稱限流",200);}/**
? ? * 按URL限流,有默認的限流處理邏輯
? ? */@GetMapping("/byUrl")@SentinelResource(value="byUrl",blockHandler="handleException")publicCommonResultbyUrl(){returnnewCommonResult("按url限流",200);}publicCommonResulthandleException(BlockExceptionexception){returnnewCommonResult(exception.getClass().getCanonicalName(),200);}}Copy to clipboardErrorCopied
我們可以根據(jù)@SentinelResource注解中定義的value(資源名稱)來進行限流操作沪曙,但是需要指定限流處理邏輯奕污。
流控規(guī)則可以在Sentinel控制臺進行配置,由于我們使用了Nacos注冊中心液走,我們先啟動Nacos和sentinel-service菊值;
由于Sentinel采用的懶加載規(guī)則,需要我們先訪問下接口育灸,Sentinel控制臺中才會有對應服務信息腻窒,我們先訪問下該接口:http://localhost:8401/rateLimit/byResource
在Sentinel控制臺配置流控規(guī)則,根據(jù)@SentinelResource注解的value值:
快速訪問上面的接口磅崭,可以發(fā)現(xiàn)返回了自己定義的限流處理信息:
我們還可以通過訪問的URL來限流儿子,會返回默認的限流處理信息。
在Sentinel控制臺配置流控規(guī)則砸喻,使用訪問的URL:
多次訪問該接口柔逼,會返回默認的限流處理結果:http://localhost:8401/rateLimit/byUrl
我們可以自定義通用的限流處理邏輯,然后在@SentinelResource中指定割岛。
創(chuàng)建CustomBlockHandler類用于自定義限流處理邏輯:
/**
* Created by macro on 2019/11/7.
*/publicclassCustomBlockHandler{publicCommonResulthandleException(BlockExceptionexception){returnnewCommonResult("自定義限流信息",200);}}Copy to clipboardErrorCopied
在RateLimitController中使用自定義限流處理邏輯:
/**
* 限流功能
* Created by macro on 2019/11/7.
*/@RestController@RequestMapping("/rateLimit")publicclassRateLimitController{/**
? ? * 自定義通用的限流處理邏輯
? ? */@GetMapping("/customBlockHandler")@SentinelResource(value="customBlockHandler",blockHandler="handleException",blockHandlerClass=CustomBlockHandler.class)publicCommonResultblockHandler(){returnnewCommonResult("限流成功",200);}}Copy to clipboardErrorCopied
Sentinel 支持對服務間調用進行保護愉适,對故障應用進行熔斷操作,這里我們使用RestTemplate來調用nacos-user-service服務所提供的接口來演示下該功能癣漆。
首先我們需要使用@SentinelRestTemplate來包裝下RestTemplate實例:
/**
* Created by macro on 2019/8/29.
*/@ConfigurationpublicclassRibbonConfig{@Bean@SentinelRestTemplatepublicRestTemplaterestTemplate(){returnnewRestTemplate();}}Copy to clipboardErrorCopied
添加CircleBreakerController類维咸,定義對nacos-user-service提供接口的調用:
/**
* 熔斷功能
* Created by macro on 2019/11/7.
*/@RestController@RequestMapping("/breaker")publicclassCircleBreakerController{privateLoggerLOGGER=LoggerFactory.getLogger(CircleBreakerController.class);@AutowiredprivateRestTemplaterestTemplate;@Value("${service-url.user-service}")privateStringuserServiceUrl;@RequestMapping("/fallback/{id}")@SentinelResource(value="fallback",fallback="handleFallback")publicCommonResultfallback(@PathVariableLongid){returnrestTemplate.getForObject(userServiceUrl+"/user/{1}",CommonResult.class,id);}@RequestMapping("/fallbackException/{id}")@SentinelResource(value="fallbackException",fallback="handleFallback2",exceptionsToIgnore={NullPointerException.class})publicCommonResultfallbackException(@PathVariableLongid){if(id==1){thrownewIndexOutOfBoundsException();}elseif(id==2){thrownewNullPointerException();}returnrestTemplate.getForObject(userServiceUrl+"/user/{1}",CommonResult.class,id);}publicCommonResulthandleFallback(Longid){UserdefaultUser=newUser(-1L,"defaultUser","123456");returnnewCommonResult<>(defaultUser,"服務降級返回",200);}publicCommonResulthandleFallback2(@PathVariableLongid,Throwablee){LOGGER.error("handleFallback2 id:{},throwable class:{}",id,e.getClass());UserdefaultUser=newUser(-2L,"defaultUser2","123456");returnnewCommonResult<>(defaultUser,"服務降級返回",200);}}Copy to clipboardErrorCopied
啟動nacos-user-service和sentinel-service服務:
由于我們并沒有在nacos-user-service中定義id為4的用戶,所有訪問如下接口會返回服務降級結果:http://localhost:8401/breaker/fallback/4
{
? ? "data": {
? ? ? ? "id": -1,
? ? ? ? "username": "defaultUser",
? ? ? ? "password": "123456"
? ? },
? ? "message": "服務降級返回",
? ? "code": 200
}Copy to clipboardErrorCopied
由于我們使用了exceptionsToIgnore參數(shù)忽略了NullPointerException惠爽,所以我們訪問接口報空指針時不會發(fā)生服務降級:http://localhost:8401/breaker/fallbackException/2
Sentinel也適配了Feign組件癌蓖,我們使用Feign來進行服務間調用時,也可以使用它來進行熔斷婚肆。
首先我們需要在pom.xml中添加Feign相關依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>Copy to clipboardErrorCopied
在application.yml中打開Sentinel對Feign的支持:
feign:
? sentinel:
? ? enabled: true #打開sentinel對feign的支持Copy to clipboardErrorCopied
在應用啟動類上添加@EnableFeignClients啟動Feign的功能租副;
創(chuàng)建一個UserService接口,用于定義對nacos-user-service服務的調用:
/**
* Created by macro on 2019/9/5.
*/@FeignClient(value="nacos-user-service",fallback=UserFallbackService.class)publicinterfaceUserService{@PostMapping("/user/create")CommonResultcreate(@RequestBodyUseruser);@GetMapping("/user/{id}")CommonResult<User>getUser(@PathVariableLongid);@GetMapping("/user/getByUsername")CommonResult<User>getByUsername(@RequestParamStringusername);@PostMapping("/user/update")CommonResultupdate(@RequestBodyUseruser);@PostMapping("/user/delete/{id}")CommonResultdelete(@PathVariableLongid);}Copy to clipboardErrorCopied
創(chuàng)建UserFallbackService類實現(xiàn)UserService接口较性,用于處理服務降級邏輯:
/**
* Created by macro on 2019/9/5.
*/@ComponentpublicclassUserFallbackServiceimplementsUserService{@OverridepublicCommonResultcreate(Useruser){UserdefaultUser=newUser(-1L,"defaultUser","123456");returnnewCommonResult<>(defaultUser,"服務降級返回",200);}@OverridepublicCommonResult<User>getUser(Longid){UserdefaultUser=newUser(-1L,"defaultUser","123456");returnnewCommonResult<>(defaultUser,"服務降級返回",200);}@OverridepublicCommonResult<User>getByUsername(Stringusername){UserdefaultUser=newUser(-1L,"defaultUser","123456");returnnewCommonResult<>(defaultUser,"服務降級返回",200);}@OverridepublicCommonResultupdate(Useruser){returnnewCommonResult("調用失敗用僧,服務被降級",500);}@OverridepublicCommonResultdelete(Longid){returnnewCommonResult("調用失敗结胀,服務被降級",500);}}Copy to clipboardErrorCopied
在UserFeignController中使用UserService通過Feign調用nacos-user-service服務中的接口:
/**
* Created by macro on 2019/8/29.
*/@RestController@RequestMapping("/user")publicclassUserFeignController{@AutowiredprivateUserServiceuserService;@GetMapping("/{id}")publicCommonResultgetUser(@PathVariableLongid){returnuserService.getUser(id);}@GetMapping("/getByUsername")publicCommonResultgetByUsername(@RequestParamStringusername){returnuserService.getByUsername(username);}@PostMapping("/create")publicCommonResultcreate(@RequestBodyUseruser){returnuserService.create(user);}@PostMapping("/update")publicCommonResultupdate(@RequestBodyUseruser){returnuserService.update(user);}@PostMapping("/delete/{id}")publicCommonResultdelete(@PathVariableLongid){returnuserService.delete(id);}}Copy to clipboardErrorCopied
調用如下接口會發(fā)生服務降級,返回服務降級處理信息:http://localhost:8401/user/4
{
? ? "data": {
? ? ? ? "id": -1,
? ? ? ? "username": "defaultUser",
? ? ? ? "password": "123456"
? ? },
? ? "message": "服務降級返回",
? ? "code": 200
}Copy to clipboardErrorCopied
默認情況下责循,當我們在Sentinel控制臺中配置規(guī)則時糟港,控制臺推送規(guī)則方式是通過API將規(guī)則推送至客戶端并直接更新到內存中。一旦我們重啟應用沼死,規(guī)則將消失。下面我們介紹下如何將配置規(guī)則進行持久化崔赌,以存儲到Nacos為例意蛀。
首先我們直接在配置中心創(chuàng)建規(guī)則,配置中心將規(guī)則推送到客戶端健芭;
Sentinel控制臺也從配置中心去獲取配置信息县钥。
先在pom.xml中添加相關依賴:
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId></dependency>Copy to clipboardErrorCopied
修改application.yml配置文件,添加Nacos數(shù)據(jù)源配置:
spring:
? cloud:
? ? sentinel:
? ? ? datasource:
? ? ? ? ds1:
? ? ? ? ? nacos:
? ? ? ? ? ? server-addr: localhost:8848
? ? ? ? ? ? dataId: ${spring.application.name}-sentinel
? ? ? ? ? ? groupId: DEFAULT_GROUP
? ? ? ? ? ? data-type: json
? ? ? ? ? ? rule-type: flowCopy to clipboardErrorCopied
在Nacos中添加配置:
添加配置信息如下:
[
? ? {
? ? ? ? "resource": "/rateLimit/byUrl",
? ? ? ? "limitApp": "default",
? ? ? ? "grade": 1,
? ? ? ? "count": 1,
? ? ? ? "strategy": 0,
? ? ? ? "controlBehavior": 0,
? ? ? ? "clusterMode": false
? ? }
]Copy to clipboardErrorCopied
相關參數(shù)解釋:
resource:資源名稱慈迈;
limitApp:來源應用若贮;
grade:閾值類型,0表示線程數(shù)痒留,1表示QPS谴麦;
count:單機閾值;
strategy:流控模式伸头,0表示直接匾效,1表示關聯(lián),2表示鏈路恤磷;
controlBehavior:流控效果面哼,0表示快速失敗,1表示Warm Up扫步,2表示排隊等待魔策;
clusterMode:是否集群。
發(fā)現(xiàn)Sentinel控制臺已經有了如下限流規(guī)則:
快速訪問測試接口河胎,可以發(fā)現(xiàn)返回了限流處理信息:
Spring Cloud Alibaba 官方文檔:https://github.com/alibaba/spring-cloud-alibaba/wiki
springcloud-learning
├── nacos-user-service -- 注冊到nacos的提供User對象CRUD接口的服務
└── sentinel-service -- sentinel功能測試服務