作者:邴越,某電商平臺(tái)架構(gòu)師,曾任阿里巴巴中臺(tái)資深開(kāi)發(fā)工程師,云棲社區(qū)專(zhuān)家,關(guān)注分布式系統(tǒng)和高可用架構(gòu)。
十多年的宜陽(yáng)網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營(yíng)銷(xiāo)型網(wǎng)站建設(shè)的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整宜陽(yáng)建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)建站從事“宜陽(yáng)網(wǎng)站設(shè)計(jì)”,“宜陽(yáng)網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。前言
今年雙 11 全民購(gòu)物狂歡節(jié)進(jìn)入第十一個(gè)年頭,1 分 36 秒,交易額沖到 100 億 !比 2018 年快了近 30 秒,比 2017 年快了近 1 分半!這個(gè)速度再次刷新天貓雙 11 成交總額破 100 億的紀(jì)錄。
那么如何抗住雙 11 高并發(fā)流量?接下來(lái)讓我們一起來(lái)聊聊高可用的“大殺器”限流降級(jí)技術(shù)。
服務(wù)等級(jí)協(xié)議
我們常說(shuō)的 N 個(gè) 9,就是對(duì) SLA 的一個(gè)描述。SLA 全稱(chēng)是 Service Level Agreement,翻譯為服務(wù)水平協(xié)議,也稱(chēng)服務(wù)等級(jí)協(xié)議,它表明了公有云提供服務(wù)的等級(jí)以及質(zhì)量。
例如阿里云對(duì)外承諾的就是一個(gè)服務(wù)周期內(nèi)集群服務(wù)可用性不低于 99.99%,如果低于這個(gè)標(biāo)準(zhǔn),云服務(wù)公司就需要賠償客戶的損失。
做到 4 個(gè) 9 夠好了嗎
對(duì)互聯(lián)網(wǎng)公司來(lái)說(shuō),SLA 就是網(wǎng)站或者 API 服務(wù)可用性的一個(gè)保證。
9 越多代表全年服務(wù)可用時(shí)間越長(zhǎng)服務(wù)更可靠,4 個(gè) 9 的服務(wù)可用性,聽(tīng)起來(lái)已經(jīng)很高了,但對(duì)于實(shí)際的業(yè)務(wù)場(chǎng)景,這個(gè)值可能并不夠。
我們來(lái)做一個(gè)簡(jiǎn)單的計(jì)算,假設(shè)一個(gè)核心鏈路依賴(lài) 20 個(gè)服務(wù),強(qiáng)依賴(lài)同時(shí)沒(méi)有配置任何降級(jí),并且這 20 個(gè)服務(wù)的可用性達(dá)到 4 個(gè) 9,也就是 99.99%。
那這個(gè)核心鏈路的可用性只有 99.99 的 20 次方=99.8%,如果有 10 億次請(qǐng)求則有 3,000,000 次的失敗請(qǐng)求,理想狀況下,每年還是有 17 小時(shí)服務(wù)不可用。
這是一個(gè)理想的估算,在實(shí)際的生產(chǎn)環(huán)境中,由于服務(wù)發(fā)布,宕機(jī)等各種各樣的原因,情況肯定會(huì)比這個(gè)更差。
對(duì)于一些比較敏感的業(yè)務(wù),比如金融,或是對(duì)服務(wù)穩(wěn)定要求較高的行業(yè),比如訂單或者支付業(yè)務(wù),這樣的情況是不能接受的。
微服務(wù)的雪崩效應(yīng)
除了對(duì)服務(wù)可用性的追求,微服務(wù)架構(gòu)一個(gè)繞不過(guò)去的問(wèn)題就是服務(wù)雪崩。
在一個(gè)調(diào)用鏈路上,微服務(wù)架構(gòu)各個(gè)服務(wù)之間組成了一個(gè)松散的整體,牽一發(fā)而動(dòng)全身,服務(wù)雪崩是一個(gè)多級(jí)傳導(dǎo)的過(guò)程。
首先是某個(gè)服務(wù)提供者不可用,由于大量超時(shí)等待,繼而導(dǎo)致服務(wù)調(diào)用者不可用,并且在整個(gè)鏈路上傳導(dǎo),繼而導(dǎo)致系統(tǒng)癱瘓。
限流降級(jí)怎么做
如同上面我們分析的,在大規(guī)模微服務(wù)架構(gòu)的場(chǎng)景下,避免服務(wù)出現(xiàn)雪崩,要減少停機(jī)時(shí)間,要盡可能的提高服務(wù)可用性。
提高服務(wù)可用性,可以從很多方向入手,比如緩存、池化、異步化、負(fù)載均衡、隊(duì)列和降級(jí)熔斷等手段。
緩存以及隊(duì)列等手段,增加系統(tǒng)的容量。限流和降級(jí)則是關(guān)心在到達(dá)系統(tǒng)瓶頸時(shí)系統(tǒng)的響應(yīng),更看重穩(wěn)定性。
緩存和異步等提高系統(tǒng)的戰(zhàn)力,限流降級(jí)關(guān)注的是防御。限流和降級(jí),具體實(shí)施方法可以歸納為八字箴言,分別是限流,降級(jí),熔斷和隔離。
限流和降級(jí)
限流顧名思義,提前對(duì)各個(gè)類(lèi)型的請(qǐng)求設(shè)置最高的 QPS 閾值,若高于設(shè)置的閾值則對(duì)該請(qǐng)求直接返回,不再調(diào)用后續(xù)資源。
限流需要結(jié)合壓測(cè)等,了解系統(tǒng)的最高水位,也是在實(shí)際開(kāi)發(fā)中應(yīng)用最多的一種穩(wěn)定性保障手段。
降級(jí)則是當(dāng)服務(wù)器壓力劇增的情況下,根據(jù)當(dāng)前業(yè)務(wù)情況及流量對(duì)一些服務(wù)和頁(yè)面有策略的降級(jí),以此釋放服務(wù)器資源以保證核心任務(wù)的正常運(yùn)行。
從降級(jí)配置方式上,降級(jí)一般可以分為主動(dòng)降級(jí)和自動(dòng)降級(jí)。主動(dòng)降級(jí)是提前配置,自動(dòng)降級(jí)則是系統(tǒng)發(fā)生故障時(shí),如超時(shí)或者頻繁失敗,自動(dòng)降級(jí)。
其中,自動(dòng)降級(jí),又可以分為以下策略:
超時(shí)降級(jí)
失敗次數(shù)降級(jí)
故障降級(jí)
在系統(tǒng)設(shè)計(jì)中,降級(jí)一般是結(jié)合系統(tǒng)配置中心,通過(guò)配置中心進(jìn)行推送,下面是一個(gè)典型的降級(jí)通知設(shè)計(jì)。
熔斷隔離
如果某個(gè)目標(biāo)服務(wù)調(diào)用慢或者有大量超時(shí),此時(shí)熔斷該服務(wù)的調(diào)用,對(duì)于后續(xù)調(diào)用請(qǐng)求,不在繼續(xù)調(diào)用目標(biāo)服務(wù),直接返回,快速釋放資源。
熔斷一般需要設(shè)置不同的恢復(fù)策略,如果目標(biāo)服務(wù)情況好轉(zhuǎn)則恢復(fù)調(diào)用。歡迎大家關(guān)注我的公種浩【程序員追風(fēng)】,文章都會(huì)在里面更新,整理的資料也會(huì)放在里面。
服務(wù)隔離與前面的三個(gè)略有區(qū)別,我們的系統(tǒng)通常提供了不止一個(gè)服務(wù),但是這些服務(wù)在運(yùn)行時(shí)是部署在一個(gè)實(shí)例,或者一臺(tái)物理機(jī)上面的。
如果不對(duì)服務(wù)資源做隔離,一旦一個(gè)服務(wù)出現(xiàn)了問(wèn)題,整個(gè)系統(tǒng)的穩(wěn)定性都會(huì)受到影響!服務(wù)隔離的目的就是避免服務(wù)之間相互影響。
一般來(lái)說(shuō),隔離要關(guān)注兩方面,一個(gè)是在哪里進(jìn)行隔離,另外一個(gè)是隔離哪些資源。
何處隔離:一次服務(wù)調(diào)用,涉及到的是服務(wù)提供方和調(diào)用方,我們所指的資源,也是兩方的服務(wù)器等資源,服務(wù)隔離通常可以從提供方和調(diào)用方兩個(gè)方面入手。
隔離什么:廣義的服務(wù)隔離,不僅包括服務(wù)器資源,還包括數(shù)據(jù)庫(kù)分庫(kù),緩存,索引等,這里我們只關(guān)注服務(wù)層面的隔離。
降級(jí)和熔斷的區(qū)別
服務(wù)降級(jí)和熔斷在概念上比較相近,通過(guò)兩個(gè)場(chǎng)景,談?wù)勎易约旱睦斫狻?/p>
熔斷,一般是停止服務(wù):典型的就是股市的熔斷,如果大盤(pán)不受控制,直接休市,不提供服務(wù),是保護(hù)大盤(pán)的一種方式。
降級(jí),通常是有備用方案:從北京到濟(jì)南,下雨導(dǎo)致航班延誤,我可以乘坐高鐵,如果高鐵票買(mǎi)不到,也可以乘坐汽車(chē)或者開(kāi)車(chē)過(guò)去。
兩者的區(qū)別:降級(jí)一般是主動(dòng)的,有預(yù)見(jiàn)性的,熔斷通常是被動(dòng)的,服務(wù) A 降級(jí)以后,一般會(huì)有服務(wù) B 來(lái)代替,而熔斷通常是針對(duì)核心鏈路的處理。
在實(shí)際開(kāi)發(fā)中,熔斷的下一步通常就是降級(jí)。
常用限流算法設(shè)計(jì)
剛才講了限流的概念,那么怎樣判斷系統(tǒng)到達(dá)設(shè)置的流量閾值了?這就需要一些限流策略來(lái)支持,不同的限流算法有不同的特點(diǎn),平滑程度也不同。
計(jì)數(shù)器法
計(jì)數(shù)器法是限流算法里最簡(jiǎn)單也是最容易實(shí)現(xiàn)的一種算法。
假設(shè)一個(gè)接口限制一分鐘內(nèi)的訪問(wèn)次數(shù)不能超過(guò) 100 個(gè),維護(hù)一個(gè)計(jì)數(shù)器,每次有新的請(qǐng)求過(guò)來(lái),計(jì)數(shù)器加一。
這時(shí)候判斷,如果計(jì)數(shù)器的值小于限流值,并且與上一次請(qǐng)求的時(shí)間間隔還在一分鐘內(nèi),允許請(qǐng)求通過(guò),否則拒絕請(qǐng)求,如果超出了時(shí)間間隔,要將計(jì)數(shù)器清零。
public?class?CounterLimiter?{ ????//初始時(shí)間 ????private?static?long?startTime?=?System.currentTimeMillis(); ????//初始計(jì)數(shù)值 ????private?static?final?AtomicInteger?ZERO?=?new?AtomicInteger(0); ????//時(shí)間窗口限制 ????private?static?final?long?interval?=?10000; ????//限制通過(guò)請(qǐng)求 ????private?static?int?limit?=?100; ????//請(qǐng)求計(jì)數(shù) ????private?AtomicInteger?requestCount?=?ZERO; ????//獲取限流 ????public?boolean?tryAcquire()?{ ????????long?now?=?System.currentTimeMillis(); ????????//在時(shí)間窗口內(nèi) ????????if?(now?計(jì)數(shù)器限流可以比較容易的應(yīng)用在分布式環(huán)境中,用一個(gè)單點(diǎn)的存儲(chǔ)來(lái)保存計(jì)數(shù)值,比如用 Redis,并且設(shè)置自動(dòng)過(guò)期時(shí)間,這時(shí)候就可以統(tǒng)計(jì)整個(gè)集群的流量,并且進(jìn)行限流。
計(jì)數(shù)器方式的缺點(diǎn)是不能處理臨界問(wèn)題,或者說(shuō)限流策略不夠平滑。歡迎大家關(guān)注我的公種浩【程序員追風(fēng)】,文章都會(huì)在里面更新,整理的資料也會(huì)放在里面。
假設(shè)在限流臨界點(diǎn)的前后,分別發(fā)送 100 個(gè)請(qǐng)求,實(shí)際上在計(jì)數(shù)器置 0 前后的極短時(shí)間里,處理了 200 個(gè)請(qǐng)求,這是一個(gè)瞬時(shí)的高峰,可能會(huì)超過(guò)系統(tǒng)的限制。
計(jì)數(shù)器限流允許出現(xiàn) 2*permitsPerSecond 的突發(fā)流量,可以使用滑動(dòng)窗口算法去優(yōu)化,具體不展開(kāi)。
漏桶算法
假設(shè)我們有一個(gè)固定容量的桶,桶底部可以漏水(忽略氣壓等,不是物理問(wèn)題),并且這個(gè)漏水的速率可控的,那么我們可以通過(guò)這個(gè)桶來(lái)控制請(qǐng)求速度,也就是漏水的速度。
我們不關(guān)心流進(jìn)來(lái)的水,也就是外部請(qǐng)求有多少,桶滿了之后,多余的水會(huì)溢出。
漏桶算法的示意圖如下:
將算法中的水換成實(shí)際應(yīng)用中的請(qǐng)求,可以看到漏桶算法從入口限制了請(qǐng)求的速度。
使用漏桶算法,我們可以保證接口會(huì)以一個(gè)常速速率來(lái)處理請(qǐng)求,所以漏桶算法不會(huì)出現(xiàn)臨界問(wèn)題。
這里簡(jiǎn)單實(shí)現(xiàn)一下,也可以使用 Guava 的 SmoothWarmingUp 類(lèi),可以更好的控制漏桶算法:
public?class?LeakyLimiter?{ ????//桶的容量 ????private?int?capacity; ????//漏水速度 ????private?int?ratePerMillSecond; ????//水量 ????private?double?water; ????//上次漏水時(shí)間 ????private?long?lastLeakTime; ????public?LeakyLimiter(int?capacity,?int?ratePerMillSecond)?{ ????????this.capacity?=?capacity; ????????this.ratePerMillSecond?=?ratePerMillSecond; ????????this.water?=?0; ????} ????//獲取限流 ????public?boolean?tryAcquire()?{ ????????//執(zhí)行漏水,更新剩余水量 ????????refresh(); ????????//嘗試加水,水滿則拒絕 ????????if?(water?+?1?>?capacity)?{ ????????????return?false; ????????} ????????water?=?water?+?1; ????????return?true; ????} ????private?void?refresh()?{ ????????//當(dāng)前時(shí)間 ????????long?currentTime?=?System.currentTimeMillis(); ????????if?(currentTime?>?lastLeakTime)?{ ????????????//距上次漏水的時(shí)間間隔 ????????????long?millisSinceLastLeak?=?currentTime?-?lastLeakTime; ????????????long?leaks?=?millisSinceLastLeak?*?ratePerMillSecond; ????????????//允許漏水 ????????????if?(leaks?>?0)?{ ????????????????//已經(jīng)漏光 ????????????????if?(water?<=?leaks)?{ ????????????????????water?=?0; ????????????????}?else?{ ????????????????????water?=?water?-?leaks; ????????????????} ????????????????this.lastLeakTime?=?currentTime; ????????????} ????????} ????} }令牌桶算法
漏桶是控制水流入的速度,令牌桶則是控制留出,通過(guò)控制 Token,調(diào)節(jié)流量。
假設(shè)一個(gè)大小恒定的桶,桶里存放著令牌(Token)。桶一開(kāi)始是空的,現(xiàn)在以一個(gè)固定的速率往桶里填充,直到達(dá)到桶的容量,多余的令牌將會(huì)被丟棄。
如果令牌不被消耗,或者被消耗的速度小于產(chǎn)生的速度,令牌就會(huì)不斷地增多,直到把桶填滿。后面再產(chǎn)生的令牌就會(huì)從桶中溢出。
最后桶中可以保存的大令牌數(shù)永遠(yuǎn)不會(huì)超過(guò)桶的大小,每當(dāng)一個(gè)請(qǐng)求過(guò)來(lái)時(shí),就會(huì)嘗試從桶里移除一個(gè)令牌,如果沒(méi)有令牌的話,請(qǐng)求無(wú)法通過(guò)。
public?class?TokenBucketLimiter?{ ????private?long?capacity; ????private?long?windowTimeInSeconds; ????long?lastRefillTimeStamp; ????long?refillCountPerSecond; ????long?availableTokens; ????public?TokenBucketLimiter(long?capacity,?long?windowTimeInSeconds)?{ ????????this.capacity?=?capacity; ????????this.windowTimeInSeconds?=?windowTimeInSeconds; ????????lastRefillTimeStamp?=?System.currentTimeMillis(); ????????refillCountPerSecond?=?capacity?/?windowTimeInSeconds; ????????availableTokens?=?0; ????} ????public?long?getAvailableTokens()?{ ????????return?this.availableTokens; ????} ????public?boolean?tryAcquire()?{ ????????//更新令牌桶 ????????refill(); ????????if?(availableTokens?>?0)?{ ????????????--availableTokens; ????????????return?true; ????????}?else?{ ????????????return?false; ????????} ????} ????private?void?refill()?{ ????????long?now?=?System.currentTimeMillis(); ????????if?(now?>?lastRefillTimeStamp)?{ ????????????long?elapsedTime?=?now?-?lastRefillTimeStamp; ????????????int?tokensToBeAdded?=?(int)?((elapsedTime?/?1000)?*?refillCountPerSecond); ????????????if?(tokensToBeAdded?>?0)?{ ????????????????availableTokens?=?Math.min(capacity,?availableTokens?+?tokensToBeAdded); ????????????????lastRefillTimeStamp?=?now; ????????????} ????????} ????} }這兩種算法的主要區(qū)別在于漏桶算法能夠強(qiáng)行限制數(shù)據(jù)的傳輸速率,而令牌桶算法在能夠限制數(shù)據(jù)的平均傳輸速率外,還允許某種程度的突發(fā)傳輸。
在令牌桶算法中,只要令牌桶中存在令牌,那么就允許突發(fā)地傳輸數(shù)據(jù)直到達(dá)到用戶配置的門(mén)限,因此它適合于具有突發(fā)特性的流量。
漏桶和令牌桶的比較
漏桶和令牌桶算法實(shí)現(xiàn)可以一樣,但是方向是相反的,對(duì)于相同的參數(shù)得到的限流效果是一樣的。
主要區(qū)別在于令牌桶允許一定程度的突發(fā),漏桶主要目的是平滑流入速率,考慮一個(gè)臨界場(chǎng)景,令牌桶內(nèi)積累了 100 個(gè) Token,可以在一瞬間通過(guò)。
但是因?yàn)橄乱幻氘a(chǎn)生 Token 的速度是固定的,所以令牌桶允許出現(xiàn)瞬間出現(xiàn) permitsPerSecond 的流量,但是不會(huì)出現(xiàn) 2*permitsPerSecond 的流量,漏桶的速度則始終是平滑的。
使用 RateLimiter 實(shí)現(xiàn)限流
Google 開(kāi)源工具包 Guava 提供了限流工具類(lèi) RateLimiter,該類(lèi)基于令牌桶算法實(shí)現(xiàn)流量限制,使用方便。
RateLimiter 使用的是令牌桶的流控算法,RateLimiter 會(huì)按照一定的頻率往桶里扔令牌,線程拿到令牌才能執(zhí)行。歡迎大家關(guān)注我的公種浩【程序員追風(fēng)】,文章都會(huì)在里面更新,整理的資料也會(huì)放在里面。
比如你希望自己的應(yīng)用程序 QPS 不要超過(guò) 1000,那么 RateLimiter 設(shè)置 1000 的速率后,就會(huì)每秒往桶里扔 1000 個(gè)令牌,看下方法的說(shuō)明:
RateLimter 提供的 API 可以直接應(yīng)用,其中 acquire 會(huì)阻塞,類(lèi)似 JUC 的信號(hào)量 Semphore,tryAcquire 方法則是非阻塞的:
public?class?RateLimiterTest?{ ????public?static?void?main(String[]?args)?throws?InterruptedException?{ ????????//允許10個(gè),permitsPerSecond ????????RateLimiter?limiter?=?RateLimiter.create(10); ????????for(int?i=1;i<20;i++){ ????????????if?(limiter.tryAcquire(1)){ ????????????????System.out.println("第"+i+"次請(qǐng)求成功"); ????????????}else{ ????????????????System.out.println("第"+i+"次請(qǐng)求拒絕"); ????????????} ????????} ????} }總結(jié)
本文從服務(wù)可用性開(kāi)始,分析了在業(yè)務(wù)高峰期通過(guò)限流降級(jí)保障服務(wù)高可用的重要性。
接下來(lái)分別探討了限流,降級(jí),熔斷,隔離的概念和應(yīng)用,并且介紹了常用的限流策略。
創(chuàng)新互聯(lián)www.cdcxhl.cn,專(zhuān)業(yè)提供香港、美國(guó)云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開(kāi)啟,新人活動(dòng)云服務(wù)器買(mǎi)多久送多久。
標(biāo)題名稱(chēng):63分59秒1000億,如何抗住雙11高并發(fā)流量?-創(chuàng)新互聯(lián)
網(wǎng)址分享:http://weahome.cn/article/dsghsc.html