雙11的高并發(fā)流量是如何抗住,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
10余年品牌的成都網(wǎng)站建設公司,成百上千企業(yè)網(wǎng)站設計經(jīng)驗.價格合理,可準確把握網(wǎng)頁設計訴求.提供定制網(wǎng)站建設、商城網(wǎng)站制作、微信小程序開發(fā)、響應式網(wǎng)站等服務,我們設計的作品屢獲殊榮,是您值得信賴的專業(yè)網(wǎng)站設計公司。服務等級協(xié)議
我們常說的 N 個 9,就是對 SLA 的一個描述。SLA 全稱是 Service Level Agreement,翻譯為服務水平協(xié)議,也稱服務等級協(xié)議,它表明了公有云提供服務的等級以及質(zhì)量。
例如阿里云對外承諾的就是一個服務周期內(nèi)集群服務可用性不低于 99.99%,如果低于這個標準,云服務公司就需要賠償客戶的損失。
做到 4 個 9 夠好了嗎
對互聯(lián)網(wǎng)公司來說,SLA 就是網(wǎng)站或者 API 服務可用性的一個保證。
9 越多代表全年服務可用時間越長服務更可靠,4 個 9 的服務可用性,聽起來已經(jīng)很高了,但對于實際的業(yè)務場景,這個值可能并不夠。
我們來做一個簡單的計算,假設一個核心鏈路依賴 20 個服務,強依賴同時沒有配置任何降級,并且這 20 個服務的可用性達到 4 個 9,也就是 99.99%。
那這個核心鏈路的可用性只有 99.99 的 20 次方=99.8%,如果有 10 億次請求則有 3,000,000 次的失敗請求,理想狀況下,每年還是有 17 小時服務不可用。這是一個理想的估算,在實際的生產(chǎn)環(huán)境中,由于服務發(fā)布,宕機等各種各樣的原因,情況肯定會比這個更差。對于一些比較敏感的業(yè)務,比如金融,或是對服務穩(wěn)定要求較高的行業(yè),比如訂單或者支付業(yè)務,這樣的情況是不能接受的。微服務的雪崩效應
除了對服務可用性的追求,微服務架構一個繞不過去的問題就是服務雪崩。在一個調(diào)用鏈路上,微服務架構各個服務之間組成了一個松散的整體,牽一發(fā)而動全身,服務雪崩是一個多級傳導的過程。首先是某個服務提供者不可用,由于大量超時等待,繼而導致服務調(diào)用者不可用,并且在整個鏈路上傳導,繼而導致系統(tǒng)癱瘓。限流降級怎么做
如同上面我們分析的,在大規(guī)模微服務架構的場景下,避免服務出現(xiàn)雪崩,要減少停機時間,要盡可能的提高服務可用性。提高服務可用性,可以從很多方向入手,比如緩存、池化、異步化、負載均衡、隊列和降級熔斷等手段。緩存以及隊列等手段,增加系統(tǒng)的容量。限流和降級則是關心在到達系統(tǒng)瓶頸時系統(tǒng)的響應,更看重穩(wěn)定性。
緩存和異步等提高系統(tǒng)的戰(zhàn)力,限流降級關注的是防御。限流和降級,具體實施方法可以歸納為八字箴言,分別是限流,降級,熔斷和隔離。限流和降級
限流顧名思義,提前對各個類型的請求設置最高的 QPS 閾值,若高于設置的閾值則對該請求直接返回,不再調(diào)用后續(xù)資源。限流需要結合壓測等,了解系統(tǒng)的最高水位,也是在實際開發(fā)中應用最多的一種穩(wěn)定性保障手段。降級則是當服務器壓力劇增的情況下,根據(jù)當前業(yè)務情況及流量對一些服務和頁面有策略的降級,以此釋放服務器資源以保證核心任務的正常運行。從降級配置方式上,降級一般可以分為主動降級和自動降級。主動降級是提前配置,自動降級則是系統(tǒng)發(fā)生故障時,如超時或者頻繁失敗,自動降級。其中,自動降級,又可以分為以下策略:超時降級
失敗次數(shù)降級
故障降級
熔斷隔離
如果某個目標服務調(diào)用慢或者有大量超時,此時熔斷該服務的調(diào)用,對于后續(xù)調(diào)用請求,不在繼續(xù)調(diào)用目標服務,直接返回,快速釋放資源。熔斷一般需要設置不同的恢復策略,如果目標服務情況好轉則恢復調(diào)用。服務隔離與前面的三個略有區(qū)別,我們的系統(tǒng)通常提供了不止一個服務,但是這些服務在運行時是部署在一個實例,或者一臺物理機上面的。如果不對服務資源做隔離,一旦一個服務出現(xiàn)了問題,整個系統(tǒng)的穩(wěn)定性都會受到影響!服務隔離的目的就是避免服務之間相互影響。
一般來說,隔離要關注兩方面,一個是在哪里進行隔離,另外一個是隔離哪些資源。何處隔離:一次服務調(diào)用,涉及到的是服務提供方和調(diào)用方,我們所指的資源,也是兩方的服務器等資源,服務隔離通常可以從提供方和調(diào)用方兩個方面入手。
隔離什么:廣義的服務隔離,不僅包括服務器資源,還包括數(shù)據(jù)庫分庫,緩存,索引等,這里我們只關注服務層面的隔離。
降級和熔斷的區(qū)別
服務降級和熔斷在概念上比較相近,通過兩個場景,談談我自己的理解。熔斷,一般是停止服務:典型的就是股市的熔斷,如果大盤不受控制,直接休市,不提供服務,是保護大盤的一種方式。
降級,通常是有備用方案:從北京到濟南,下雨導致航班延誤,我可以乘坐高鐵,如果高鐵票買不到,也可以乘坐汽車或者開車過去。
兩者的區(qū)別:降級一般是主動的,有預見性的,熔斷通常是被動的,服務 A 降級以后,一般會有服務 B 來代替,而熔斷通常是針對核心鏈路的處理。
在實際開發(fā)中,熔斷的下一步通常就是降級。常用限流算法設計
剛才講了限流的概念,那么怎樣判斷系統(tǒng)到達設置的流量閾值了?這就需要一些限流策略來支持,不同的限流算法有不同的特點,平滑程度也不同。計數(shù)器法
計數(shù)器法是限流算法里最簡單也是最容易實現(xiàn)的一種算法。假設一個接口限制一分鐘內(nèi)的訪問次數(shù)不能超過 100 個,維護一個計數(shù)器,每次有新的請求過來,計數(shù)器加一。這時候判斷,如果計數(shù)器的值小于限流值,并且與上一次請求的時間間隔還在一分鐘內(nèi),允許請求通過,否則拒絕請求,如果超出了時間間隔,要將計數(shù)器清零。
public class CounterLimiter {
//初始時間
private static long startTime = System.currentTimeMillis();
//初始計數(shù)值
private static final AtomicInteger ZERO = new AtomicInteger(0);
//時間窗口限制
private static final long interval = 10000;
//限制通過請求
private static int limit = 100;
//請求計數(shù)
private AtomicInteger requestCount = ZERO;
//獲取限流
public boolean tryAcquire() {
long now = System.currentTimeMillis();
//在時間窗口內(nèi)
if (now < startTime + interval) {
//判斷是否超過大請求
if (requestCount.get() < limit) {
requestCount.incrementAndGet();
return true;
}
return false;
} else {
//超時重置
startTime = now;
requestCount = ZERO;
return true;
}
}
}
計數(shù)器限流可以比較容易的應用在分布式環(huán)境中,用一個單點的存儲來保存計數(shù)值,比如用 Redis,并且設置自動過期時間,這時候就可以統(tǒng)計整個集群的流量,并且進行限流。計數(shù)器方式的缺點是不能處理臨界問題,或者說限流策略不夠平滑。假設在限流臨界點的前后,分別發(fā)送 100 個請求,實際上在計數(shù)器置 0 前后的極短時間里,處理了 200 個請求,這是一個瞬時的高峰,可能會超過系統(tǒng)的限制。計數(shù)器限流允許出現(xiàn) 2*permitsPerSecond 的突發(fā)流量,可以使用滑動窗口算法去優(yōu)化,具體不展開。漏桶算法
假設我們有一個固定容量的桶,桶底部可以漏水(忽略氣壓等,不是物理問題),并且這個漏水的速率可控的,那么我們可以通過這個桶來控制請求速度,也就是漏水的速度。我們不關心流進來的水,也就是外部請求有多少,桶滿了之后,多余的水會溢出。漏桶算法的示意圖如下:
將算法中的水換成實際應用中的請求,可以看到漏桶算法從入口限制了請求的速度。使用漏桶算法,我們可以保證接口會以一個常速速率來處理請求,所以漏桶算法不會出現(xiàn)臨界問題。這里簡單實現(xiàn)一下,也可以使用 Guava 的 SmoothWarmingUp 類,可以更好的控制漏桶算法:
public class LeakyLimiter {
//桶的容量
private int capacity;
//漏水速度
private int ratePerMillSecond;
//水量
private double water;
//上次漏水時間
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() {
//當前時間
long currentTime = System.currentTimeMillis();
if (currentTime > lastLeakTime) {
//距上次漏水的時間間隔
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;
}
}
}
}
令牌桶算法
漏桶是控制水流入的速度,令牌桶則是控制留出,通過控制 Token,調(diào)節(jié)流量。假設一個大小恒定的桶,桶里存放著令牌(Token)。桶一開始是空的,現(xiàn)在以一個固定的速率往桶里填充,直到達到桶的容量,多余的令牌將會被丟棄。如果令牌不被消耗,或者被消耗的速度小于產(chǎn)生的速度,令牌就會不斷地增多,直到把桶填滿。后面再產(chǎn)生的令牌就會從桶中溢出。
最后桶中可以保存的大令牌數(shù)永遠不會超過桶的大小,每當一個請求過來時,就會嘗試從桶里移除一個令牌,如果沒有令牌的話,請求無法通過。
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ū)別在于漏桶算法能夠強行限制數(shù)據(jù)的傳輸速率,而令牌桶算法在能夠限制數(shù)據(jù)的平均傳輸速率外,還允許某種程度的突發(fā)傳輸。在令牌桶算法中,只要令牌桶中存在令牌,那么就允許突發(fā)地傳輸數(shù)據(jù)直到達到用戶配置的門限,因此它適合于具有突發(fā)特性的流量。漏桶和令牌桶的比較
漏桶和令牌桶算法實現(xiàn)可以一樣,但是方向是相反的,對于相同的參數(shù)得到的限流效果是一樣的。主要區(qū)別在于令牌桶允許一定程度的突發(fā),漏桶主要目的是平滑流入速率,考慮一個臨界場景,令牌桶內(nèi)積累了 100 個 Token,可以在一瞬間通過。但是因為下一秒產(chǎn)生 Token 的速度是固定的,所以令牌桶允許出現(xiàn)瞬間出現(xiàn) permitsPerSecond 的流量,但是不會出現(xiàn) 2*permitsPerSecond 的流量,漏桶的速度則始終是平滑的。使用 RateLimiter 實現(xiàn)限流
Google 開源工具包 Guava 提供了限流工具類 RateLimiter,該類基于令牌桶算法實現(xiàn)流量限制,使用方便。RateLimiter 使用的是令牌桶的流控算法,RateLimiter 會按照一定的頻率往桶里扔令牌,線程拿到令牌才能執(zhí)行。比如你希望自己的應用程序 QPS 不要超過 1000,那么 RateLimiter 設置 1000 的速率后,就會每秒往桶里扔 1000 個令牌,看下方法的說明:RateLimter 提供的 API 可以直接應用,其中 acquire 會阻塞,類似 JUC 的信號量 Semphore,tryAcquire 方法則是非阻塞的:
public class RateLimiterTest {
public static void main(String[] args) throws InterruptedException {
//允許10個,permitsPerSecond
RateLimiter limiter = RateLimiter.create(10);
for(int i=1;i<20;i++){
if (limiter.tryAcquire(1)){
System.out.println("第"+i+"次請求成功");
}else{
System.out.println("第"+i+"次請求拒絕");
}
}
}
}
總結
本文從服務可用性開始,分析了在業(yè)務高峰期通過限流降級保障服務高可用的重要性。看完上述內(nèi)容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注創(chuàng)新互聯(lián)-成都網(wǎng)站建設公司行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。