微服務(wù)限流熔斷是什么,怎么做,不做行不行?
“在分布式應(yīng)用中,最常見(jiàn)的問(wèn)題是什么呢?”
“一個(gè)分布式應(yīng)用部署上去后,還要關(guān)注什么?”

“這服務(wù)的遠(yuǎn)程調(diào)用依賴(lài)似乎有點(diǎn)多...”
前言
在 《微服務(wù)的戰(zhàn)爭(zhēng):級(jí)聯(lián)故障和雪崩》中有提到,在一個(gè)分布式應(yīng)用中,最常見(jiàn),最有危險(xiǎn)性之一的點(diǎn)就是級(jí)聯(lián)故障所造成的雪崩,而其對(duì)應(yīng)的解決方案為根據(jù)特定的規(guī)則/規(guī)律進(jìn)行流量控制和熔斷降級(jí),避免請(qǐng)求發(fā)生堆積,保護(hù)自身應(yīng)用,也防止服務(wù)提供方進(jìn)一步過(guò)載。

簡(jiǎn)單來(lái)講就是,要控制訪(fǎng)問(wèn)量的流量,要防各類(lèi)調(diào)用的強(qiáng)/弱依賴(lài),才能保護(hù)好應(yīng)用程序的底線(xiàn)。
訴求,期望
訴求:作為一個(gè)業(yè)務(wù),肯定是希望自家的應(yīng)用程序,能夠全年無(wú)休,最低限度也要有個(gè) 4 個(gè) 9,一出突發(fā)性大流量,在資源貧乏的窗口期,就馬上能夠自動(dòng)恢復(fù)。
期望:萬(wàn)丈高樓平地起,我們需要對(duì)應(yīng)用程序進(jìn)行流量控制、熔斷降級(jí)。確保在特定的規(guī)則下,系統(tǒng)能夠進(jìn)行容錯(cuò),只處理自己力所能及的請(qǐng)求。若有更一步訴求,再自動(dòng)擴(kuò)縮容,提高系統(tǒng)資源上限。
解決方案
要如何解決這個(gè)問(wèn)題,或者說(shuō)是達(dá)到這個(gè)目的呢?
咱們可以關(guān)注到問(wèn)題的核心點(diǎn)是 “系統(tǒng)沒(méi)有任何的保護(hù)的情況下”,因此方向上就是讓系統(tǒng),讓?xiě)?yīng)用程序有流量控制的保護(hù)。一般含以下幾個(gè)方面:
來(lái)自端控制:在業(yè)務(wù)/流量網(wǎng)關(guān)處內(nèi)置流量控制、熔斷降級(jí)的外部插件,起到端控制的效果。
來(lái)自集群/節(jié)點(diǎn)控制:在統(tǒng)一框架中內(nèi)建流量控制、熔斷降級(jí)的處理邏輯,起到集群/節(jié)點(diǎn)控制的效果。
來(lái)自 Mesh 控制:通過(guò) ServiceMesh 來(lái)實(shí)現(xiàn)流量控制、熔斷降級(jí)。侵入性小,能帶來(lái)多種控制模式,但有利有弊。
以上的多種方式均可與內(nèi)部的治理平臺(tái)打通,且流量控制、熔斷降級(jí)是不止面試應(yīng)用程序的,就看資源埋點(diǎn)上如何設(shè)計(jì)、注入。常見(jiàn)有如下幾種角度:
資源的調(diào)用關(guān)系:例如遠(yuǎn)程調(diào)用,像是面向 HTTP、SQL、Redis、RPC 等調(diào)用均,另外針對(duì)文件句柄控制也可以。
運(yùn)行指標(biāo):例如 QPS、線(xiàn)程池、系統(tǒng)負(fù)載等。
流量控制
在資源不變的情況下,系統(tǒng)所能提供的處理能力是有限的。而系統(tǒng)所面對(duì)的請(qǐng)求所到來(lái)的時(shí)間和量級(jí)往往是隨機(jī)且不可控的。
因此就會(huì)存在可能出現(xiàn)突發(fā)性流量,而系統(tǒng)在沒(méi)有任何的保護(hù)的情況下,很有可能會(huì)在數(shù)分鐘內(nèi)就無(wú)法提供正常服務(wù)。常見(jiàn)的前導(dǎo)現(xiàn)象是先出現(xiàn)調(diào)用延遲,接著持續(xù)出現(xiàn)飽和度上升,最終假死。

流量控制一般常見(jiàn)的有兩種方式,分別是:基于 QPS、基于并發(fā)隔離。
基于 QPS
最常用的流量控制場(chǎng)景,就是基于 QPS 來(lái)做流控,在一定的時(shí)間窗口內(nèi)按照特定的規(guī)則達(dá)到所設(shè)定的閾值則進(jìn)行調(diào)控:

案例
在本文中借助 sentinel-golang 來(lái)實(shí)現(xiàn)案例所需的訴求,代碼如下:
import?(
?...
?sentinel?"github.com/alibaba/sentinel-golang/api"
?"github.com/alibaba/sentinel-golang/core/base"
?"github.com/alibaba/sentinel-golang/core/flow"
?"github.com/alibaba/sentinel-golang/util"
)
func?main()?{
?_?=?sentinel.InitDefault()
?_,?_?=?flow.LoadRules([]*flow.Rule{
??{
???Resource:???????????????"控制吃煎魚(yú)的速度",
???Threshold:??????????????60,
???ControlBehavior:????????flow.Reject,
??},
?})
?...
?e,?b?:=?sentinel.Entry("控制吃煎魚(yú)的速度",?sentinel.WithTrafficType(base.Inbound))
?if?b?!=?nil?{
?????//?Blocked
?}?else?{
?????//?Passed
?????e.Exit()
?}
}
總的來(lái)講,上述規(guī)則結(jié)果就是 1s 內(nèi)允許通過(guò) 60 個(gè)請(qǐng)求,超出的請(qǐng)求的處理策略為直接拒絕(Reject)。
首先我們初始化了 Sentinel 并定義資源(Resource)為 “控制吃煎魚(yú)的速度”。其 Threshold 配置為 3,也就是 QPS 的閾值為 3,統(tǒng)計(jì)窗口未設(shè)置默認(rèn)值為 1s,ControlBehavior 控制的行為為直接拒絕。
而在滿(mǎn)足閾值條件后,常見(jiàn)的處理策略還有勻速排隊(duì)(Throttling),勻速排隊(duì)方式會(huì)嚴(yán)格控制請(qǐng)求通過(guò)的間隔時(shí)間,也就是讓請(qǐng)求以均勻的速度通過(guò)。
基于并發(fā)隔離
基于資源訪(fǎng)問(wèn)的并發(fā)協(xié)程數(shù)來(lái)控制對(duì)資源的訪(fǎng)問(wèn)數(shù)量,主要是控制對(duì)資源訪(fǎng)問(wèn)的最大協(xié)程數(shù),避免因?yàn)橘Y源的異常導(dǎo)致協(xié)程耗盡。

這類(lèi)情況,Go 語(yǔ)言在設(shè)計(jì)上常??梢允褂脜f(xié)程池來(lái)進(jìn)行控制,但設(shè)計(jì)總是趕不上計(jì)劃的,且不同場(chǎng)景情況可能不同,因此作為一個(gè)日常功能也是非常有存在的必要性。
熔斷降級(jí)
在分布式應(yīng)用中,隨著不斷地業(yè)務(wù)拆分,遠(yuǎn)程調(diào)用逐漸變得越來(lái)越多。且在微服務(wù)盛興的情況下,一個(gè)小業(yè)務(wù)拆出七八個(gè)服務(wù)的也常有。
此時(shí)就會(huì)出現(xiàn)一個(gè)經(jīng)典的問(wèn)題,那就是客戶(hù)端的一個(gè)普通調(diào)用,很有可能就要經(jīng)過(guò)好幾個(gè)服務(wù),而一個(gè)服務(wù)又有可能遠(yuǎn)程調(diào)用外部 HTTP、SQL、Redis、RPC 等,調(diào)用鏈會(huì)特別的長(zhǎng)。
若其中一個(gè)調(diào)用流程出現(xiàn)了問(wèn)題,且沒(méi)有進(jìn)行調(diào)控,就會(huì)出現(xiàn)級(jí)聯(lián)故障,最終導(dǎo)致系統(tǒng)雪崩:

服務(wù) D 所依賴(lài)的外部接口出現(xiàn)了故障,而他并沒(méi)有做任何的控制,因此擴(kuò)散到了所有調(diào)用到他的服務(wù),自然也就包含服務(wù) B,因此最終出現(xiàn)系統(tǒng)雪崩。
這種最經(jīng)典的是出現(xiàn)在默認(rèn) Go http client 調(diào)用沒(méi)有設(shè)置 Timeout,從而只要出現(xiàn)一次故障,就足矣讓記住這類(lèi) “坑”,畢竟崩的 ”慢“,錯(cuò)誤日志還多。
(via: 《微服務(wù)的戰(zhàn)爭(zhēng):級(jí)聯(lián)故障和雪崩》)
目的和措施
為了解決上述問(wèn)題所帶來(lái)的災(zāi)難,在分布式應(yīng)用中常需要對(duì)服務(wù)依賴(lài)進(jìn)行熔斷降級(jí)。在存在問(wèn)題時(shí),暫時(shí)切斷內(nèi)部調(diào)用,避免局部不穩(wěn)定因素導(dǎo)致整個(gè)分布式系統(tǒng)的雪崩。
而熔斷降級(jí)作為保護(hù)服務(wù)自身的手段,通常是在客戶(hù)端進(jìn)行規(guī)則配置和熔斷識(shí)別:

常見(jiàn)的有三種熔斷降級(jí)措施:慢調(diào)用比例策略、錯(cuò)誤比例策略、錯(cuò)誤計(jì)數(shù)策略。
慢調(diào)用比例
在所設(shè)定的時(shí)間窗口內(nèi),慢調(diào)用的比例大于所設(shè)置的閾值,則對(duì)接下來(lái)訪(fǎng)問(wèn)的請(qǐng)求進(jìn)行自動(dòng)熔斷。
錯(cuò)誤比例
在所設(shè)定的時(shí)間窗口內(nèi),調(diào)用的訪(fǎng)問(wèn)錯(cuò)誤比例大于所設(shè)置的閾值,則對(duì)接下來(lái)訪(fǎng)問(wèn)的請(qǐng)求進(jìn)行自動(dòng)熔斷。
錯(cuò)誤計(jì)數(shù)
在所設(shè)定的時(shí)間窗口內(nèi),調(diào)用的訪(fǎng)問(wèn)錯(cuò)誤次數(shù)大于所設(shè)置的閾值,則對(duì)接下來(lái)訪(fǎng)問(wèn)的請(qǐng)求進(jìn)行自動(dòng)熔斷。
實(shí)踐案例
知道流量控制、熔斷降級(jí)的基本概念和功能后,在現(xiàn)實(shí)環(huán)境中應(yīng)該如何結(jié)合項(xiàng)目進(jìn)行使用呢。最常見(jiàn)的場(chǎng)景是可針對(duì)業(yè)務(wù)服務(wù)的 HTTP 路由進(jìn)行流量控制,以 HTTP 路由作為資源埋點(diǎn),這樣子就可以實(shí)現(xiàn)接口級(jí)的調(diào)控了。

還可以增強(qiáng)其功能特性,針對(duì)參數(shù)也進(jìn)行多重匹配。常會(huì)有這種限流訴求:針對(duì) HTTP GET /eddycjy/info 且 language 為 go 的情況下進(jìn)行限流。
另外還可以針對(duì) HTTP 調(diào)用封裝統(tǒng)一方法,進(jìn)行默認(rèn)的熔斷注入,實(shí)現(xiàn)多重保障。
而結(jié)合系統(tǒng)負(fù)載、服務(wù) QPS 等,可以對(duì)限流熔斷的規(guī)則數(shù)據(jù)源進(jìn)行實(shí)時(shí)調(diào)控,再結(jié)合 Watch 機(jī)制,就能夠比較平滑的實(shí)現(xiàn)自適應(yīng)限流熔斷的接入。
總結(jié)
在分布式應(yīng)用中,限流熔斷是非常重要的一環(huán),越早開(kāi)始做越有益處。
但需要注意的是,不同公司的業(yè)務(wù)模型多多少少有些不一樣,所針對(duì)的匹配維度多少有些不同,因此需要提前進(jìn)行業(yè)務(wù)調(diào)研。
在做業(yè)務(wù)的限流熔斷時(shí),注意把度量指標(biāo)的打點(diǎn)做上,這樣子后續(xù)就能夠結(jié)合 Prometheus+Grafana+Alertmanager 做一系列的趨勢(shì)圖,熔斷告警,自動(dòng)擴(kuò)縮容等相關(guān)工作了,會(huì)是一個(gè)很好的助力。
推薦閱讀
