在微服務架構(gòu)中一個會被經(jīng)常提及的概念就是“服務的熔斷與限流”。而之所以如此頻繁的提及這個概念邑闺,是因為在高并發(fā)場景下檩赢,瞬間的流量洪峰很容易超出微服務中各個系統(tǒng)的最大承受能力结序,從而造成服務的整體不可用沼本。
所以在設計高并發(fā)場景下的微服務架構(gòu)時噩峦,要根據(jù)服務所能承受的最大流量制定限流策略,從而保證在高并發(fā)場景下服務的穩(wěn)定性抽兆。今天的文章就和大家聊一聊關(guān)于限流的內(nèi)容识补,包括常見的限流算法及目前微服務架構(gòu)中主要的限流框架。
限流的概念
先介紹下什么是限流辫红?其實日常生活中的限流場景隨處可見凭涂,例如北京地鐵早高峰,每天都是人山人海贴妻,如果大家一起蜂擁進站切油,將很容易造成站內(nèi)擁堵,所以地鐵站一般都會進站口設置像迷宮一樣的柵欄名惩,大家轉(zhuǎn)圈圈分批進站澎胡,這就是一種典型的限流場景。
那么在微服務中的限流具體是指什么呢娩鹉?從字面上看攻谁,限流限的是流量,在不同場景下流量的定義是不同的弯予,可以是QPS(每秒請求數(shù))戚宦、TPS(每秒事務處理數(shù)),也可以指純粹的網(wǎng)絡流量(如網(wǎng)卡通過的字節(jié)數(shù))等锈嫩。
但我們通常所說的限流受楼,是指限制達到系統(tǒng)的并發(fā)請求數(shù),使得系統(tǒng)能夠在自身能力允許的情況下正常處理部分用戶的請求呼寸,而對超出自身處理能力的用戶請求則予以拒絕艳汽,從而保證系統(tǒng)的穩(wěn)定運行。
限流不可避免的會造成用戶請求失敗或變慢的情況对雪,從而在一定程度上影響用戶體驗河狐,所以限流策略的制定需要以系統(tǒng)壓測的結(jié)果為參考,并在用戶體驗與系統(tǒng)穩(wěn)定性之間進行平衡取舍慌植。
限流的必要性甚牲?
前面我們提到限流的主要目的是為了保證系統(tǒng)的穩(wěn)定性义郑。在日常的業(yè)務中蝶柿,如果遇到像雙十一之類的促銷活動,或者遇到爬蟲等不正常的流量等情況非驮,用戶流量突增交汤,但后端服務的處理能力是有限的,如果不能有效處理突發(fā)流量,那么后端服務就很容易被打垮芙扎。
可以設想這樣一個場景:"某服務單節(jié)點可以承受的QPS是1000星岗,該服務共有5個節(jié)點,日常情況下服務的QPS為3000"戒洼。那么正常情況下該服務毫無壓力,根據(jù)負載均衡配置3000/5=600俏橘,每個節(jié)點的日常QPS才600左右。
直到某一天圈浇,老板突然搞了一波促銷寥掐,系統(tǒng)的整體QPS達到了8000。此時每個節(jié)點的平均承載QPS為1600磷蜀,節(jié)點A率先扛不住直接掛了召耘,此時集群中還剩下4個節(jié)點,每個節(jié)點的平均承載QPS將達到2000褐隆,于是污它,剩下的4個節(jié)點也一臺接一臺掛了,整個服務就此雪崩庶弃。
而如果我們的系統(tǒng)有限流機制衫贬,那么情況將會如何發(fā)展呢?
系統(tǒng)整體QPS達到8000虫埂,但由于集群整體限流了5000祥山,所以超出集群承受力的那3000個請求將被拒絕,系統(tǒng)則會正常處理5000個用戶請求掉伏,這是對集群整體限流的情況缝呕。而對于各個節(jié)點來說,由于我的承受力只是1000QPS斧散,那么超出1000的部分也將被拒絕供常。這樣雖然損失了部分用戶請求,但保證了整個系統(tǒng)的穩(wěn)定性鸡捐,也給開發(fā)運維留下了系統(tǒng)擴容時間栈暇。
由此可見,限流對于系統(tǒng)的自我保護是非常重要的存在箍镜。那么限流具體怎么做呢源祈?接下來我們總結(jié)下常見的限流算法。
常見的限流算法
常見的限流算法主要有:計數(shù)器色迂、固定窗口香缺,滑動窗口、漏桶歇僧、令牌桶图张。接下來我們分別介紹下這幾種限流算法。
<計數(shù)器限流>
計數(shù)器限流是最簡單粗暴的一種限流算法,例如系統(tǒng)能同時處理100個請求祸轮,那么可以在保存一個計數(shù)器兽埃,處理一個請求,計數(shù)器加一适袜,一個請求處理完畢后計數(shù)器減一柄错。每次請求進來的時候,先看一眼計數(shù)器的值苦酱,如果超過閥值則直接拒絕鄙陡。
在具體實現(xiàn)時,如果該計數(shù)器是存在單機內(nèi)存中躏啰,那么就實現(xiàn)了單機限流趁矾;而如果存在例如Redis中,集群中的所有節(jié)點依次為限流依據(jù)给僵,那么就算實現(xiàn)了集群限流算法毫捣。
優(yōu)點:實現(xiàn)簡單,單機例如諸如Java的Atomic等原子類就能實現(xiàn)帝际,集群則通過Redis的incr操作就能快速實現(xiàn)蔓同。
缺點:計數(shù)器限流無法應對突發(fā)的流量增長。例如我們允許的閥值是1W蹲诀,此時計數(shù)器的值是0斑粱,那么當1W個請求瞬間全部打進來的時候,很可能服務就頂不住了脯爪。這是因為流量的緩緩增加和一下子涌入则北,對系統(tǒng)所產(chǎn)生的壓力是不一樣的。
況且一般限流都是限制在指定時間間隔內(nèi)的訪問量痕慢,而不是全時段服務的總體處理能力尚揣,所以計數(shù)器限流不太適合高并發(fā)場景下的限流實現(xiàn)。
<固定窗口限流>
相對于計數(shù)器來說掖举,固定窗口限流是以一段時間窗口內(nèi)的訪問量作為限流的依據(jù)快骗,計數(shù)器每過一個時間窗口就自動重置。其規(guī)則如下:
請求次數(shù)小于閥值塔次,允許訪問方篮,計數(shù)器加1;
請求次數(shù)大于閥值励负,拒絕訪問藕溅;
本時間窗口過了之后,計數(shù)器自動清零熄守;
固定窗口限流雖然看起來挺完美蜈垮,但是它有固定窗口臨界的問題。例如系統(tǒng)每秒允許1000個請求裕照,假如第一個時間窗口的間隔是0~1秒攒发,但在第0.55秒處一下子涌入了1000個請求,過了1秒后計數(shù)清零晋南,此時在1.05秒的時候又一下子涌入了1000個請求惠猿。
此時雖然在固定時間窗口內(nèi)的計數(shù)沒有超過閥值,但在全局看來0.55秒~1.05秒這0.5秒內(nèi)一下子卻涌入了2000個請求负间,而這對于閥值為1000/s的系統(tǒng)來說是不可承受的偶妖。如下圖所示:
而為了解決這個問題,衍生出了滑動窗口限流的算法政溃!
<滑動窗口限流>
滑動窗口限流解決了固定窗口臨界值的問題趾访,可以保證任意時間窗口內(nèi)都不會超過限流閥值。相對于固定窗口董虱,滑動窗口除了需要引入計數(shù)器外扼鞋,還需要額外記錄時間窗口內(nèi)每個請求到達的時間點。
以時間窗口為1秒為例愤诱,規(guī)則如下:
記錄每次請求的時間云头;
統(tǒng)計每次請求的時間向前推1秒這個時間窗口內(nèi)的請求數(shù),且1秒前的數(shù)據(jù)可以刪除淫半;
統(tǒng)計的請求數(shù)小于閥值則記錄該請求的時間溃槐,并允許通過,反之則拒絕該請求科吭;
雖然看起來很OK昏滴,但是滑動窗口也無法解決短時間之內(nèi)集中流量的沖擊。例如每秒限制1000個請求对人,但是有可能存在前5毫秒的時候影涉,閥值就被打滿的情況,理想情況下每10毫秒來100個請求规伐,那么系統(tǒng)對流量的處理就會更加平滑蟹倾。
但在真實場景中是很難控制請求的頻率的。所以為了解決時間窗口類算法的痛點猖闪,又出現(xiàn)了漏桶算法鲜棠。
<漏桶限流>
漏桶算法的基本思想是,流量持續(xù)進入漏桶中培慌,底部則定速處理請求豁陆,如果流量進入的速率高于底部請求被處理的速率,且當桶中的流量超過桶的大小時吵护,流量就會被溢出盒音。具體如下圖所示:
漏桶算法的特點是寬進嚴出表鳍,無論請求的速率有多大,底部的處理速度都勻速進行祥诽。這種算法的特點有點類似于消息隊列的處理機制譬圣,一般來說漏桶算法也是由隊列來實現(xiàn)的。
但漏桶算法的這種特點雄坪,實際上即是它的優(yōu)點也是缺點厘熟。有時候面對突發(fā)流量,我們往往會希望在保持系統(tǒng)穩(wěn)定的同時维哈,能更快地處理用戶請求以提升用戶體驗绳姨,而不是按部就班的佛系工作。在這種情況下又出現(xiàn)了令牌桶這樣的限流算法阔挠,它在應對突發(fā)流量時飘庄,可以比漏桶算法更加激進。
<令牌桶限流>
令牌桶與漏桶的原理類似购撼,只是漏桶是底部勻速處理竭宰,而令牌桶則是定速的向桶里塞入令牌,然后請求只有拿到了令牌才會被服務器處理份招。具體規(guī)則如下:
定速的向桶中放入令牌切揭;
令牌數(shù)量超過桶的限制,則丟棄锁摔;
請求來了先向桶中索取令牌廓旬,索取成功則通過被處理,否則拒絕谐腰;
可以看出令牌桶在應對突發(fā)流量時孕豹,不會想漏桶那樣勻速的處理,而是在短時間內(nèi)請求可以同時取走桶中的令牌十气,并及時的被服務器處理励背。所以在應對突發(fā)流量的場景下,令牌桶表現(xiàn)更強砸西。
限流算法總結(jié)
經(jīng)過上述的描述叶眉,好像漏桶、令牌桶比時間窗口類算法好多了芹枷,那么時間窗口類算法是不是就沒啥用了呢衅疙?
其實并不是,雖然漏桶鸳慈、令牌桶對比時間窗口類算法對流量的整形效果更好饱溢,但是它們也有各自的缺點,例如令牌桶走芋,假如系統(tǒng)上線時沒有預熱绩郎,那么可能會出現(xiàn)由于此時桶中還沒有令牌潘鲫,而導致請求被誤殺的情況;而漏桶中由于請求是暫存在桶中的肋杖,所以請求什么時候能被處理溉仑,則是有延時的,這并不符合互聯(lián)網(wǎng)業(yè)務低延時的要求兽愤。
所以令牌桶、漏桶算法更適合阻塞式限流的場景挪圾,即后臺任務類的限流浅萧。而基于時間窗口的限流則更適合互聯(lián)網(wǎng)實施業(yè)務限流的場景,即能處理快速處理哲思,不能處理及時響應調(diào)用方洼畅,避免請求出現(xiàn)過長的等待時間。
微服務限流組件
如果你有興趣實際上也是可以自己實現(xiàn)一個限流組件的棚赔,只不過這種輪子已經(jīng)早有人造好了帝簇。目前市面上比較流行的限流組件主要有:Google Guava提供的限流工具類“RateLimiter”、阿里開源的Sentinel靠益。
其中Google Guava提供的限流工具類“RateLimiter”丧肴,是基于令牌桶實現(xiàn)的,并且擴展了算法胧后,支持了預熱功能芋浮。而阿里的Sentinel中的勻速限流策略,就是采用了漏桶算法壳快。