SpringCloudAlibaba隨筆目錄
二、SpringCloudAlibaba項目之Nacos搭建及服務注冊
三、SpringCloudAlibaba項目之生產者與消費者
四、SpringCloudAlibaba項目之Ribbon負載均衡
五、SpringCloudAlibaba項目之OpenFeign遠程調用
六、SpringCloudAlibaba項目之Nacos-config配置中心
七、SpringCloudAlibaba項目之Sentinel流量控制
八、SpringCloudAlibaba項目之Seata分布式事務
九、SpringCloudAlibaba項目之GateWay網關
十、SpringCloudAlibaba項目之SkyWalking鏈路追蹤
SpringCloudAlibaba項目之Sentinel流量控制
1、Sentinel簡介
Sentinel是阿里開源的項目,提供了流量控制、熔斷降級、系統負載保護等多個維度來保障服務之間的穩定性。
官網:https://github.com/alibaba/Sentinel/wiki
Sentinel主要特性:
2、Sentinel與Hystrix的區別
關於Sentinel與Hystrix的區別見:https://yq.aliyun.com/articles/633786/
總體來說:
Hystrix常用的線程池隔離會造成線程上下切換的overhead比較大;Hystrix使用的信號量隔離對某個資源調用的並發數進行控制,效果不錯,但是無法對慢調用進行自動降級;Sentinel通過並發線程數的流量控制提供信號量隔離的功能;
此外,Sentinel支持的熔斷降級維度更多,可對多種指標進行流控、熔斷,且提供了實時監控和控制面板,功能更為強大。
Sentinel 的所有規則都可以在內存態中動態地查詢及修改,修改之后立即生效。同時 Sentinel 也提供相關 API,供您來定制自己的規則策略。
Sentinel 支持以下幾種規則:流量控制規則、熔斷降級規則、系統保護規則、來源訪問控制規則 和 熱點參數規則。
3、使用Sentinel 核心庫體驗流量控制
我們先使用springboot應用程序,快速搭建使用,體驗流控效果。
官網:https://github.com/alibaba/Sentinel/wiki/%E6%96%B0%E6%89%8B%E6%8C%87%E5%8D%97#%E5%85%AC%E7%BD%91-demo
pom.xml中添加依賴
<!-- springweb 啟動依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- sentinel 核心庫 --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> <version>1.8.2</version> </dependency>
SentinelController接口:
/** * Sentinel接口流控測試 */ @RestController public class SentinelController { public static final String RESOURCE_NAME = "sentinel";//資源名 //進行Sentinel流控 @RequestMapping(value = "/sentinel") public String sentinelTest(){ Entry entry = null; try { //sentinel針對資源進行限制 entry = SphU.entry(RESOURCE_NAME); //被保護的業務邏輯 String str = "Sentinel接口正常"; System.out.println("====" + str + "===="); return str; } catch (BlockException e) { e.printStackTrace(); //資源訪問阻止,被限流或被降級 //進行相應的處理操作 System.out.println("Sentinel接口被流控了"); return "Sentinel接口被流控了"; }catch (Exception e){ // 若需要配置降級規則,需要通過這種方式記錄業務異常 Tracer.traceEntry(e,entry); }finally { if(entry != null){ entry.exit(); } } return null; } /** * 定義規則 * * spring的初始化方法 */ @PostConstruct private static void initFlowRules(){ //流控規則 List<FlowRule> rules = new ArrayList<>(); //流控 FlowRule rule = new FlowRule(); //設置受保護的資源 rule.setResource(RESOURCE_NAME); // 設置流控規則 QPS rule.setGrade(RuleConstant.FLOW_GRADE_QPS); //設置受保護資源的閾值 // Set limit QPS to 20. rule.setCount(1); rules.add(rule); //加載配置好的規則 FlowRuleManager.loadRules(rules); } }
訪問地址:http://localhost:8080/sentinel
1秒鍾之內訪問一次正常,如果超過一次將被流控
4、@SentinelResource 注解方式定義資源
pom.xml文件添加依賴
<!-- 使用 @SentinelResource 注解依賴 --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-annotation-aspectj</artifactId> <version>1.8.2</version> </dependency>
SentinelAspectConfiguration配置bean
/** * 若您的應用使用了 Spring AOP(無論是 Spring Boot 還是傳統 Spring 應用), * 您需要通過配置的方式將 SentinelResourceAspect 注冊為一個 Spring Bean: */ @Configuration public class SentinelAspectConfiguration { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } }
SentinelController:
/** * Sentinel接口流控測試 */ @RestController public class SentinelController { public static final String USER_RESOURCE_NAME = "user";//資源名 /** * 定義規則 * * spring的初始化方法 */ @PostConstruct private static void initFlowRules(){ //流控規則 List<FlowRule> rules = new ArrayList<>();//流控 FlowRule rule2 = new FlowRule(); //設置受保護的資源 rule2.setResource(USER_RESOURCE_NAME); // 設置流控規則 QPS rule2.setGrade(RuleConstant.FLOW_GRADE_QPS); //設置受保護資源的閾值 // Set limit QPS to 20. rule2.setCount(1); rules.add(rule2); //加載配置好的規則 FlowRuleManager.loadRules(rules); } /** * 使用@SentinelResource進行Sentinel流控 * @SentinelResource注解改善接口鍾資源定義和被流控降級后的處理方法 * 使用方法:1、添加依賴 * 2、配置bean-SentinelResourceAspect * value:定義流控資源 * blockHandler:設置流控降級后的處理方法(默認該方法必須聲明在同一個類) * 如果不想在同一個類中,可以使用 blockHandlerClass 指定,但是方法必須是static * fallback:當接口出現異常,就可以交給fallback指定的方法進行處理 * 如果不想在同一個類中,可以使用 fallbackClass 指定,但是方法必須是static * * 注意:如果blockHandler和fallback方法同時指定了,則blockHandler優先級更高 * @param id * @return */ @RequestMapping(value = "/user") @SentinelResource(value = USER_RESOURCE_NAME,blockHandler = "blockHandlerForUserTest",fallback = "fallbackForUserTest") public User userTest(String id){ int a = 1/0; return new User("張三"); } /** * userTest流控降級后的處理方法 * 注意: * 1、一定要是public * 2、返回值一定要和源方法(userTest)保證一致,包含源方法的參數 * 3、可以在參數最后添加BlockException,可以區分是什么規則的處理方法 * @param id * @param ex * @return */ public User blockHandlerForUserTest(String id,BlockException ex){ ex.printStackTrace(); return new User("流控!"); } /** * userTest異常后的處理方法 * 注意: * 1、一定要是public * 2、返回值一定要和源方法(userTest)保證一致,包含源方法的參數 * 3、可以在參數最后添加Throwable,可以區分是什么異常 * @param id * @param e * @return */ public User fallbackForUserTest(String id,Throwable e){ e.printStackTrace(); return new User("異常處理!"); } }
特別地,若 blockHandler 和 fallback 都進行了配置,則被限流降級而拋出 BlockException
時只會進入 blockHandler
處理邏輯。若未配置 blockHandler
、fallback
和 defaultFallback
,則被限流降級時會將 BlockException
直接拋出(若方法本身未定義 throws BlockException 則會被 JVM 包裝一層 UndeclaredThrowableException
)。
訪問地址:http://localhost:8080/user
5、服務降級規則體驗
/** * Sentinel接口流控測試 */ @RestController public class SentinelController { public static final String DEGRADE_RESOURCE_NAME = "degrade";//降級資源名 /** * 定義服務降級規則 * * spring的初始化方法 */ @PostConstruct private static void initDegradeRules(){ //降級規則 List<DegradeRule> DegradeRules = new ArrayList<>(); //流控 DegradeRule degradeRule = new DegradeRule(); //設置受保護的資源 degradeRule.setResource(DEGRADE_RESOURCE_NAME); // 設置規則測率: 異常數 degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT); //設置異常數 degradeRule.setCount(2); degradeRule.setTimeWindow(10);//10秒內發生的異常 degradeRule.setMinRequestAmount(2);//最小請求數 DegradeRules.add(degradeRule); //加載配置好的規則 DegradeRuleManager.loadRules(DegradeRules); } @RequestMapping("/degrade") @SentinelResource(value = DEGRADE_RESOURCE_NAME,entryType = EntryType.IN,blockHandler = "blockHandlerForDegrade") public User degrade(String id) throws InternalException { // 異常數/比例 throw new RuntimeException("異常"); //慢調用比例 /*TimeUnit.SECONDS.sleep(1); return new User("正常");*/ } /** * degrade服務降級的處理方法 * * @param id * @param ex * @return */ public User blockHandlerForDegrade(String id,BlockException ex){ ex.printStackTrace(); return new User("降級處理!"); } }
訪問地址:http://localhost:8080/degrade
6、控制台部署
控制台文檔:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0
github下載地址(下載對應版本):https://github.com/alibaba/Sentinel/releases
組件版本關系:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
從 release 頁面 下載最新版本的控制台 jar 包,使用如下命令啟動控制台:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
其中 -Dserver.port=8080
用於指定 Sentinel 控制台端口為 8080。
從 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登錄功能,默認用戶名和密碼都是 sentinel
。可以參考 鑒權模塊文檔 配置用戶名和密碼。
注:若您的應用為 Spring Boot 或 Spring Cloud 應用,您可以通過 Spring 配置文件來指定配置,詳情請參考 Spring Cloud Alibaba Sentinel 文檔。
7、客戶端接入控制台(整合SpringCloud Alibaba)
pom.xml文件添加依賴
<!-- sentinel 依賴 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
application.properties
# 應用名稱
spring.application.name=service-sentinel
# 應用服務 WEB 訪問端口
server.port=8081
# Sentinel 控制台地址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8858
# 取消Sentinel控制台懶加載
# 默認情況下 Sentinel 會在客戶端首次調用的時候進行初始化,開始向控制台發送心跳包
# 配置 sentinel.eager=true 時,取消Sentinel控制台懶加載功能
spring.cloud.sentinel.eager=true
# 如果有多套網絡,又無法正確獲取本機IP,則需要使用下面的參數設置當前機器可被外部訪問的IP地址,供admin控制台使用
# spring.cloud.sentinel.transport.client-ip=
OrderController
/** * 訂單服務 */ @RestController @RequestMapping("/order") public class OrderController { /** * 新增訂單 * @return */ @RequestMapping("/addOrder") public String addOrder(){ System.out.println("訂單新增成功"); return "訂單服務-訂單新增成功"; } }
整合效果:
8、BlockException統一異常處理
MyBlockExceptionHandler異常處理類:
注意:想使用BlockException統一異常處理時,不能添加@SentinelResource
/** * BlockException統一異常處理 */ @Component public class MyBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { System.out.println("資源規則的詳細信息:" + e.getRule()); JSONObject resultObj = new JSONObject(); if(e instanceof FlowException){ resultObj.put("code",100); resultObj.put("msg","接口限流"); }if(e instanceof DegradeException){ resultObj.put("code",101); resultObj.put("msg","服務降級"); }if(e instanceof ParamFlowException){ resultObj.put("code",102); resultObj.put("msg","熱點參數限流"); }if(e instanceof SystemBlockException){ resultObj.put("code",103); resultObj.put("msg","觸發系統保護規則"); }if(e instanceof AuthorityException){ resultObj.put("code",104); resultObj.put("msg","授權規則不通過"); } //返回json數據 response.setStatus(500); response.setCharacterEncoding("UTF-8"); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.getWriter().write(resultObj.toString()); } }
控制台添加流控規則
測試效果:
9、整合openFeign降級
pom.xml添加依賴
<!-- nacos 服務注冊發現(客戶端)依賴 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- sentinel 流量控制依賴 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!-- openfeign 遠程調用依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
application.properties添加配置
# openfeign整合sentinel
feign.sentinel.enabled=true
StockOpenFeignServiceFallback服務降級實現類
@Component public class StockOpenFeignServiceFallback implements StockOpenFeignService{ @Override public String subStock(String id) { return "subStock-服務降級了!"; } @Override public String addStock() { return "addStock-服務降級了!"; } }
StockOpenFeignService接口添加服務降級實現類
/** * 庫存服務接口 * name:指定調用rest接口所對應的服務名 * path:指定調用rest接口所在的StockController指定的@RequestMapping */ @FeignClient(name = "service-stock",path = "stock",fallback = StockOpenFeignServiceFallback.class) public interface StockOpenFeignService { //聲明需要調用的rest接口對應的方法 /** * 庫存扣減 * @return */ @RequestMapping("/subStock/{id}") //@RequestLine("GET /subStock") //feign的原生注解 String subStock(@PathVariable("id") String id); //String subStock(@Param("id") String id); //@PathVariable換成@Param /** * 庫存新增 * @return */ @RequestMapping("/addStock") //@RequestLine("GET /addStock") //feign的原生注解 String addStock(); }
訪問地址:http://localhost:8082/order/addOrder
10、Sentinel規則持久化
一旦我們重啟應用,Sentinel規則將消失,生產環境需要將配置規則進行持久化,將限流配置規則持久化進Nacos保存,只要刷新到某個被流控的rest地址,sentinel控制台的流控規則就能看到,只要Nacos里面的配置不刪除,針對Sentinel上的流控規則持續有效。
pom.xml文件添加依賴
<!-- sentinel 規則持久化依賴 --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
application.properties
spring.application.name=service-sentinel
spring.cloud.nacos.discovery.server-addr= 127.0.0.1:8848
spring.cloud.sentinel.transport.dashboard=localhost:8080
#默認8719,假如被占用了會自動從8719開始依次+1掃描。直至找到未被占用的端口
spring.cloud.sentinel.transport.port= 8719
spring.cloud.sentinel.datasource.ds1.nacos.server-addr= 127.0.0.1:8848
spring.cloud.sentinel.datasource.ds1.nacos.dataId= service-sentinel
spring.cloud.sentinel.datasource.ds1.nacos.groupId= DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds1.nacos.data-type= json
spring.cloud.sentinel.datasource.ds1.nacos.rule-type= flow
添加Nacos規則
[
{
"resource":"/order/addOrder",
"limitApp":"default",
"grade":1,
"count":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":false
}
]
limitApp: 來源應用
grade: 閾值類型,0表示線程,1表示QPS
count: 單機閾值
strategy: 流控模式,0表示直接,1表示關聯,2表示鏈路
controlBehavior: 流控效果,0表示快速失敗,1表示Warm Up,2表示排隊等待
clusterMode: 是否集群