- 👏作者簡介:大家好,我是愛吃芝士的土豆倪,24屆校招生Java選手,很高興認識大家
- 📕系列專欄:Spring源碼、JUC源碼、Kafka原理、分布式技術原理
- 🔥如果感覺博主的文章還不錯的話,請👍三連支持👍一下博主哦
- 🍂博主正在努力完成2023計劃中:源碼溯源,一探究竟
- 📝聯系方式:nhs19990716,加我進群,大家一起學習,一起進步,一起對抗互聯網寒冬👀
文章目錄
- 限流的作用
- 限流的算法
- 計數器
- 滑動窗口
- 漏桶
- 令牌桶
- 限流的實現
- Sentinel
- 服務熔斷
- demo
- Sentinel中的流量控制
對于后端來說,我們需要提供一些接口去進行交互,比如登陸注冊,支付下單等這樣的一些功能,所以我們需要搭建一個整體的架構,早期的話可能沒那么復雜,就是通過ssm框架組成的架構,然后通過部署tomcat來提供一個端口,隨著整個并發量上去之后,我們需要去提供高性能的服務。
當我們的客戶端,用戶量的訪問大了之后,對于后端系統的并發量會更高,并發量越高對于后端整個處理的能力就是一個挑戰,因為我們需要保證,用戶從10個到10000個的時候,我們需要提供給用戶的是 它的響應時間不能超過多少。因為用戶的操作是存在一個心理預期的,這就是所謂的用戶體驗。其次就是系統是否能夠支撐這么大的量,是否會掛掉,掛掉之后網站無法提供對外服務的情況下,你無法產生商業價值。
比如說雙十一零點的時候,那一瞬間瞬時的流量一定會大于正常時間的吞吐量,所以需要采用一定的機制來限流
限流的作用
- 保護系統避免被瞬時流量沖垮
- 預防惡意請求(如果自己公司不提供安全的話,可以去買高防的服務器)
- 針對請求進行限制
服務器能支撐的連接數是多少
接口的處理能力(QPS/TPS),可以使用Jmeter來測試平均響應時間
資源限制(cpu(線程池)、內存、網絡資源)
- 如何控制流量
限流的指標(可以容納的流量、已經容納的流量、可以接受的流量) 閾值(基于這個指標)
限流的過程(通過算法來實現)
限流的結果(處理策略)
限流的算法
計數器
(Zookeeper:RequestThrottle 限流閥)、線程池大小、連接數大小
滑動窗口
實際上發送方和接收方都維護了一個滑動窗口
當發送端發送了一個數據包,等到接收端接收到后,接收端窗口開始滑動,發送端需要等待返回后才能滑動
其限流的核心就是流量只能在這個窗口里面,但是在tcp里面,其窗口是可以靈活的擴大的,其會根據當前網絡擁擠的程度來決定窗口大小。
最大只能發送五個(閾值),超過了就不能發送了
所以說io通信是一個阻塞通信其實就是這樣,基于數據包處理的結果,等到這個數據返回,如果數據一直不返回,那么這個時候請求是阻塞的。
漏桶
(用來控制傳輸速率的)本質上控制的是發送者的速度
流入水滴的速率代表的是請求,而滴出水滴的速度代表是處理的請求,不管來的請求有多少,但是能夠處理的請求就這么多。
漏桶算法的特點:
- 水的流出速度是固定的
- 桶的大小也是固定的
令牌桶
其恒定的生成速率決定了并發數,假如說我每秒生成10個令牌,那么我的qps 就是 10
令牌桶對比漏桶的區別是,其能夠處理瞬時突發流量,而不像漏桶一樣,流出的速度是固定的。
令牌桶的設計:
-
桶的大小
-
令牌標記
-
定時任務生成令牌
-
提供令牌獲取的接口
限流的實現
Semphore 信號量
分布式限流
其大小怎么計算呢?通過壓測來進行計算
//單機實現
//令牌桶算法RateLimiter rateLimiter=RateLimiter.create(10); //TPS=10public void doRequest(){if(rateLimiter.tryAcquire()){ //獲取令牌System.out.println("success");}else{System.out.println("failed");}}
// 令牌桶不需要釋放,處理完后自動丟棄
Sentinel
Sentinel 是阿里中間件團隊開源的,面向分布式服務架構的輕量級高可用流量控制組件,主要以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度來幫助用戶保護服務的穩定性。
其中對于Sentinel最重要的兩個東西:
- 資源(需要被保護的東西)
- 規則(限流的規則/熔斷規則)
所有的流量經過這個組件后,會根據這個規則去控制這個流量來進行處理。
而熔斷是一個這樣的概念:
上圖存在很多相互調用的情況,里面存在很多服務調用的鏈路,當Service D出現故障,會導致G F阻塞,間接的又會導致A B阻塞,如果特別多的請求阻塞在這里之后,會占用特別多的資源,內存、服務器、cpu的資源,這時候會導致服務器因為大量的資源被占用而導致其他問題。
當出現這種情況的時候,會觸發一種熔斷的方式,熔斷以后會觸發降級。
服務熔斷
Sentinel的服務熔斷有兩種方式:基于響應時間的熔斷和基于異常比率的熔斷。基于響應時間的熔斷是根據服務的響應時間來判斷是否需要熔斷,當服務的響應時間超過設定的閾值時,觸發熔斷。基于異常比率的熔斷是根據服務的異常比率來判斷是否需要熔斷,當服務的異常比率超過設定的閾值時,觸發熔斷。這兩種方式可以根據具體的業務場景和需求來選擇使用。
demo
public static void main(String[] arg) {initFlowRule(); //初始化限流規則while(true){//ResourceName表示資源,控制訪問流量的點/*try(Entry entry=SphU.entry("helloWorld")){System.out.println("hello world");}catch (BlockException e){System.out.println("被拒絕");}*/if (SphO.entry("helloWorld")) {System.out.println("Hello World");SphO.exit();// 釋放}}}private static void initFlowRule(){List<FlowRule> rules=new ArrayList<>();FlowRule flowRule=new FlowRule();flowRule.setResource("helloWorld"); //針對那個資源設置規則flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//QPS或者并發數flowRule.setCount(5); //QPS=5rules.add(flowRule);FlowRuleManager.loadRules(rules);}
Demo 運行之后,我們可以在日志 ~/logs/csp/${appName}-metrics.log.xxx 里看到下面的輸出:`
-timestamp- -date time - -resource- 5表示, 通過的請求, block: 被拒絕的請
求 ,
1600608724000|2023-09-20 21:32:04|helloWorld|5|6078|5|0|5|0|0|0
1600608725000|2023-09-20 21:32:05|helloWorld|5|32105|5|0|0|0|0|0
1600608726000|2023-09-20 21:32:06|helloWorld|5|41084|5|0|0|0|0|0
1600608727000|2023-09-20 21:32:07|helloWorld|5|72211|5|0|0|0|0|0
1600608728000|2023-09-20 21:32:08|helloWorld|5|60828|5|0|0|0|0|0
1600608729000|2023-09-20 21:32:09|helloWorld|5|41696|5|0|0|0|0|0
@RestController
public class SentinelController {@AutowiredTestService testService;@GetMapping("/hello/{name}")public String sayHello(@PathVariable("name") String name){return testService.doTest(name);}
}@Service
public class TestService {@SentinelResource(value = "doTest",blockHandler ="blockHandler",fallback = "fallback") //聲明限流的資源public String doTest(String name){return "hello , "+name;}public String blockHandler(String name, BlockException e){ //降級,限流觸發的return "被限流了";}// 降級和限流是不一樣的,限流可以觸發降級,降級是因為已經被觸發了// 降級是第三方業務調用的時候,針對下游的一個返回// 限流是針對當前服務訪問的限制// 這兩個配置只會調用一個public String fallback(String name){ //降級,熔斷觸發的return "被降級了";}}@SpringBootApplication
public class SpringbootSentinelApplication {public static void main(String[] args) {initFlowRule();SpringApplication.run(SpringbootSentinelApplication.class, args);}private static void initFlowRule(){List<FlowRule> rules=new ArrayList<>();FlowRule flowRule=new FlowRule();flowRule.setResource("doTest"); //針對那個資源設置規則flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//QPS或者并發數flowRule.setCount(5); //QPS=5rules.add(flowRule);FlowRuleManager.loadRules(rules);}}
Sentinel中的流量控制
維度 + 規則 + 資源
不管是通過什么樣的方式,其核心原理就是,監控應用流量的qps或者并發線程數的指標,然后去判斷這些指標的閾值去對流量進行控制,防止瞬時流量高峰導致系統被壓垮。
SphU.entry(resourceName) ->
正常,則通過
被限制,拋出 FlowException( FlowException extends BlockException)
同一個資源也可以創建不同的規則,這個主要是通過List rules=new ArrayList<>();實現的,如果有多個規則,會去遍歷,如果發現那個規則被觸發則就執行,否則就順利通過。
一個規則由什么組成?
- resource 資源
- count 閾值
- grade 類型(基于QPS 還是 并發線程數)
- limitApp,針對的調用來源.
- strategy , 調用關系限流
- controlBehavior . (直接拒絕, 冷啟動,勻速排隊)