上一篇說了微服務上的限流,用的是guava的RateLimit做的簡單的限流,本篇說用阿里的sentinel 做微服務的限流。
sentinel 是阿里2018年開源的一個開源項目,具體中文文檔:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D 。
簡單來說 Sentinel就是干三件事: 限流、熔斷、降級,來保證服務高可用,不會崩掉(你本身不會崩掉,也不會因為依賴的其他服務崩掉把你帶死)。
一、簡單的限流使用場景
流控
實驗項目
1,引依賴(微服務上)
找到最新版的maven依賴,加到nb-order-api服務
<!--阿里巴巴sentinel--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> <version>1.7.1</version> </dependency>
2,定義資源
最常見的就是你要保護的方法,或者一段邏輯。
手動編碼方式,下面代碼中紅色部分就是定義資源try-catch中間就是要保護的邏輯
* 創建訂單 * @param info * @param * @return */ //注解生效需在啟動類配置@EnableGlobalMethodSecurity(prePostEnabled = true) //@PreAuthorize("#oauth2.hasScope('write')") @PreAuthorize("hasRole('ROLE_ADMIN')") @PostMapping public OrderInfo create(@RequestBody OrderInfo info,@AuthenticationPrincipal String username){ try(Entry entry = SphU.entry("createOrder")){ //資源名稱-createOrder // 被保護的邏輯 log.info("獲取到username = {}",username); }catch (BlockException ex){ // 處理被流控的邏輯 log.info("blocked!"); } //查詢價格 //PriceInfo price = restTemplate.getForObject("http://localhost:9080/prices/"+info.getProductId(),PriceInfo.class); //log.info("price is "+price.getPrice()); return info; }
3,定義規則
針對某個定義的資源(上面的createOrder),定義一個規則,規則有很多種:流控規則、熔斷規則、降級規則,都可以對定義的資源定義規則,這里做一個流控規則的演示。
新建sentinel的配置類
package com.nb.security; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * sentinel規則,硬編碼方式 */ @Component public class SentinelConfig implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { //系統啟動好以后執行 List<FlowRule> rules = new ArrayList<>(); FlowRule rule = new FlowRule(); rule.setResource("createOrder");//資源名稱 rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(1);//設置QPS=1,每秒1個請求 rules.add(rule); FlowRuleManager.loadRules(rules); } }
實驗:啟動訂單服務,用postman直接請求創建訂單服務 http://localhost:9060/orders ,先跳過網關以方便做實驗
查看打印日志可以看出,每秒只能有一個請求通過,其他的則走了catch限流邏輯,這里就可以自定義處理。
3,sentinel日志
實驗運行之后,會生成一些日志文件,在windows上的目錄是:C:\Users\dev\logs\csp
sentinel-record.log.2020-03-16.0 , 可以看出sentinel定義的規則
2020-03-16 09:02:21.729 INFO [FlowRuleManager] Flow rules loaded: {} 2020-03-16 09:02:21.732 INFO App name resolved: com.nb.security.NbOrderApiApplication 2020-03-16 09:02:21.734 INFO [SentinelConfig] Application type resolved: 0 2020-03-16 09:02:21.736 INFO [MetricWriter] Creating new MetricWriter, singleFileSize=52428800, totalFileCount=6 2020-03-16 09:02:21.738 INFO [DynamicSentinelProperty] Config will be updated to: [FlowRule{resource=createOrder, limitApp=default, grade=1, count=1.0, strategy=0, refResource=null, controlBehavior=0, warmUpPeriodSec=10, maxQueueingTimeMs=500, clusterMode=false, clusterConfig=null, controller=null}] 2020-03-16 09:02:21.741 INFO [FlowRuleManager] Flow rules received: {createOrder=[FlowRule{resource=createOrder, limitApp=default, grade=1, count=1.0, strategy=0, refResource=null, controlBehavior=0, warmUpPeriodSec=10, maxQueueingTimeMs=500, clusterMode=false, clusterConfig=null, controller=com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController@2e2b9f53}]} 2020-03-16 09:03:24.012 INFO [InitExecutor] Found init func: com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit 2020-03-16 09:03:24.017 INFO [InitExecutor] Executing com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit with order 2147483647 2020-03-16 09:03:24.019 INFO Add child <sentinel_default_context> to node <machine-root> 2020-03-16 09:03:24.023 INFO [SlotChainProvider] Global slot chain builder resolved: com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder 2020-03-16 09:03:24.030 INFO Add child <createOrder> to node <sentinel_default_context> 2020-03-16 09:03:24.032 INFO [AuthorityRuleManager] Load authority rules: {} 2020-03-16 09:03:24.037 INFO [SystemRuleManager] Current system check status: false, highestSystemLoad: 1.797693e+308, highestCpuUsage: 1.797693e+308, maxRt: 9223372036854775807, maxThread: 9223372036854775807, maxQps: 1.797693e+308 2020-03-16 09:03:24.039 INFO [DegradeRuleManager] Degrade rules loaded: {} 2020-03-16 09:03:24.042 INFO [MetricExtensionProvider] MetricExtension resolved, size=0 2020-03-16 09:03:25.743 INFO [MetricWriter] New metric file created: C:\Users\dev\logs\csp\com-nb-security-NbOrderApiApplication-metrics.log.2020-03-16 2020-03-16 09:03:25.744 INFO [MetricWriter] New metric index file created: C:\Users\dev\logs\csp\com-nb-security-NbOrderApiApplication-metrics.log.2020-03-16.idx
com-nb-security-NbOrderApiApplication-metrics.log.2020-03-16 可以看出流量變化情況
|--timestamp-|------date time----|--resource-|p |block|s |e|rt
1584320604000|2020-03-16 09:03:24|createOrder|1|0|1|0|14|0|0|0
1584320610000|2020-03-16 09:03:30|createOrder|1|0|1|0|0|0|0|0
1584320611000|2020-03-16 09:03:31|createOrder|1|0|1|0|0|0|0|0
1584320713000|2020-03-16 09:05:13|createOrder|1|0|1|0|1|0|0|0
1584320716000|2020-03-16 09:05:16|createOrder|1|1|1|0|1|0|0|0
1584320718000|2020-03-16 09:05:18|createOrder|1|0|1|0|0|0|0|0
1584320719000|2020-03-16 09:05:19|createOrder|1|0|1|0|1|0|0|0
1584320722000|2020-03-16 09:05:22|createOrder|1|1|1|0|0|0|0|0
1584320723000|2020-03-16 09:05:23|createOrder|1|0|1|0|0|0|0|0
1584320724000|2020-03-16 09:05:24|createOrder|1|0|1|0|0|0|0|0
其中 p
代表通過的請求, block
代表被阻止的請求, s
代表成功執行完成的請求個數, e
代表用戶自定義的異常, rt
代表平均響應時長。
4,啟動 Sentinel 控制台
下載 Dashboard jar包 :https://github.com/alibaba/Sentinel/releases ,是一個springboot項目。
啟動jar包: 其中 -Dserver.port=8888
用於指定 Sentinel 控制台端口為 8888
java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.1.jar
默認用戶名 密碼都是sentinel ,如需修改請參照官方文檔
首頁 ,默認是他自己
5,客戶端接入控制台
有兩種方式,一種是通過制定JVM參數的形式,這種springboot不友好這里就不說了。
如果是springboot項目就應該這么做,通過Spring Cloud Alibaba Sentinel配置:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
引依賴(不需要上面說的 sentinel-transport-simple-http的依賴了)
<!-- https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>0.9.0.RELEASE</version>
</dependency>
使用注解定義資源
配置控制台信息 application.yml
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: localhost:8888
這里的 spring.cloud.sentinel.transport.port
端口配置會在應用對應的機器上啟動一個 Http Server,該 Server 會與 Sentinel 控制台做交互。比如 Sentinel 控制台添加了一個限流規則,會把規則數據 push 給這個 Http Server 接收,Http Server 再將規則注冊到 Sentinel 中
啟動order-api,http://localhost:8888 就可以訪問sentinel控制台了
二,熔斷降級
為什么要有熔斷降級?
服務雪崩舉例:
比如A是訂單服務,它會調用商品服務B,調用價格服務C,調用用戶服務D。
服務一切正常的時候是沒問題的,假設用戶服務D因為網絡抖動或者壓力變大,導致響應時間變慢,原來是10毫秒,現在變成了100、1000毫秒。
這時候A服務調D服務的時候,當前線程就會卡住了,線程就會等待D服務的響應。等待的時候繼續有請求進來調用A服務,A服務就會開新的線程去調D服務,D服務沒響應,就會在這等着。這樣請求A服務的繼續進來,請求就一直的等着,到了一定閾值后,就沒辦法新建線程了,所有線程都占滿了,此時A服務也失去響應了。此時有依賴A服務的服務也不行了,引起連鎖反應。根本的問題是D服務不行了,這樣導致跟D有依賴關系的所有的服務都出問題。這就是服務雪崩。
熔斷:
這時候就需要用熔斷的方式,保證某一個服務出問題,不會把直接或者間接依賴它的所有的服務都帶死。
原理就是在所有的服務前面加一個熔斷器,熔斷器平常處於關閉的狀態,熔斷器一旦發現后面服務(服務D)有問題不可用了,就打開熔斷器。此時服務A再調用服務D的時候,到熔斷器這就直接返回了。
請求不會到達服務D,服務A這就不會因為服務D響應變慢而有線程堆積,等待,這樣就解決了服務雪崩的問題。
熔斷器機制示意
1,當一個請求過來
如果熔斷器是關閉的,請求就放給后面的業務父服務。
如果業務服務出問題(拋異常、網絡抖動、壓力大響應時間變長、服務超時),在熔斷器這都會做一個統計,比如從熔斷器放過去10個請求,5個超時,3個報錯,熔斷器都會進行統計。
可以在自己配置熔斷器什么情況從關閉狀態變為打開狀態。如果觸發了熔斷器打開狀態,請求到熔斷器這就直接返回回去了。
2,如果是因為網絡抖動 或者 瞬時壓力變大導致的熔斷器打開,等網絡恢復或者后面壓力變小了,其實是又可以正常提供服務的,就需要有個機制讓熔斷器知道后面的服務恢復正常了,重新讓熔斷器回到關閉狀態。
機制就是:熔斷器這會有一個定時任務,在一定的時間周期內,到了時間周期,就將熔斷器轉為【半打開狀態】,會放一個請求給后面的業務服務,如果業務服務可以成功處理,就從半打開狀態變為關閉狀態。
如果放過去的這一個請求仍然是失敗狀態,熔斷器就回到打開狀態。過一個時間周期,再做一次這么的循環,一旦有一個請求成功了,就轉為關閉狀態。
降級:
熔斷器打開的時候,指定一個更簡化的業務邏輯處理。
比如,后面的服務是一個推薦的服務,正常的推薦是根據用戶的用戶畫像,算出推薦的商品,但是這個服務需要實時計算的,如果壓力大變慢了,熔斷器處於打開狀態后,可以指定一個預先設計好的簡化的邏輯,一個商品列表,靜態的。當熔斷器打開的時候就直接返回這個列表,用戶還可以看到一個商品列表,但是就不是根據用戶推薦給他的,服務降級了。
在sentinel指定熔斷降級規則
1,通過編碼方式
調用創建訂單,跨省
熔斷異常信息
降級
一般通過控制台指定各種規則,那就可以去掉 SentinelConfig配置了。
代碼 :https://github.com/lhy1234/springcloud-security/tree/chapt-6-6-sentinel-ratelimit
歡迎關注個人公眾號一起交流學習: