前言
簡單整理一下熔斷與限流,跟上一節息息相關。
正文
polly 的策略型別分為兩類:
-
被動策略(異常處理、結果處理)
-
主動策略(超時處理、斷路器、艙壁隔離、快取)
熔斷和限流通過下面主動策略來實現:
-
降級響應
-
失敗重試
-
斷路器
-
艙壁隔離
Policy 型別 | 狀態 | 說明 |
---|---|---|
CircuitBreaker(斷路器) | 有狀態 | 共享失敗率,以決定是否熔斷 |
Bulkhead(艙壁隔離) | 有狀態 | 共享容量使用情況,以決定是否執行動作 |
Cache(快取) | 有狀態 | 共享快取的物件,以決定是否命中 |
其他策略 | 無狀態 |
先來看一下熔斷,什麼是熔斷呢?
熔斷就是讓我們的上游伺服器一段時間內對下游伺服器不進行呼叫。
這裡解釋一下上游伺服器和下游伺服器,比如說A呼叫B,那麼A就是上游伺服器,B就是下游伺服器。
那麼為什麼要熔斷呢?比如說A呼叫B,現在A呼叫B 10次有8次是錯誤的,那麼這個時候就要想一件事,程式碼沒有變過,那麼肯定是量變成了質變。
這時候B之所以不可用,那麼是因為請求太多了,處理不過來(比如記憶體升高了,io 99%了等)。
那麼這個時候A就不進行呼叫了,隔一段時間後再進行呼叫。也就是A對B的這條線進行了熔斷處理。
services.AddHttpClient("GreeterClient").AddPolicyHandler(Policy<HttpResponseMessage>
.Handle<HttpRequestException>().CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: 10,
durationOfBreak: TimeSpan.FromSeconds(10),
onBreak: (r, t) =>
{
// 熔斷的時候處理事件
},
onReset: () =>
{
// 恢復的時候的處理
},onHalfOpen: () =>
{
// 恢復之前進行處理
}));
CircuitBreakerAsync 表示斷路器,這個用來實現熔斷的。
handledEventsAllowedBeforeBreaking 表示失敗10次,進行熔斷。
durationOfBreak 熔斷的事件
其他幾個事件上面做了備註。
其實上面這種不常用,因為限制比較死,比如說10次就熔斷。
一般都是百分比來計算的。
services.AddHttpClient("GreeterClient").AddPolicyHandler(Policy<HttpResponseMessage>
.Handle<HttpRequestException>().AdvancedCircuitBreakerAsync(
failureThreshold:0.8,
samplingDuration:TimeSpan.FromSeconds(10),
minimumThroughput:100,
durationOfBreak: TimeSpan.FromSeconds(10),
onBreak: (r, t) =>
{
// 熔斷的時候處理事件
},
onReset: () =>
{
// 恢復的時候的處理
}, onHalfOpen: () =>
{
// 恢復之前進行處理
}));
failureThreshold 表示失敗的比例
samplingDuration 表示失敗的時間
failureThreshold 和 samplingDuration一般是組合起來用的,表示是10秒內失敗次數要有80%就會熔斷。
minimumThroughput 表示10秒類必須有100個請求才會出根據其他的條件進行熔斷判斷。
上面就是熔斷了,那麼什麼是服務降級呢?
網上的一段話是這樣的:服務降級是指 當伺服器壓力劇增的情況下,根據實際業務情況及流量,對一些服務和頁面有策略的不處理或換種簡單的方式處理,從而釋放伺服器資源以保證核心業務正常運作或高效運作。
這段話的感覺好像是關閉某些服務一樣,而且比較繞。
個人理解是服務降級是指降低了原有的服務體驗,都屬於服務降級。
熔斷其實也是一種服務降級,但是單純的熔斷就降級的有點厲害了。
比如我去買東西,然後店直接關門了,服務體驗下降了,體驗降得比較厲害。
但是如果去買東西,店沒有關閉,而是有一排服務員,告訴你現在因為供銷商沒到貨買不到了,這體驗是不是好點,這也是服務降級。
那麼就看下第二種情況的服務降級怎麼實現吧。
var breakPolicy = Policy<HttpResponseMessage>
.Handle<HttpRequestException>().AdvancedCircuitBreakerAsync(
failureThreshold: 0.8,
samplingDuration: TimeSpan.FromSeconds(10),
minimumThroughput: 100,
durationOfBreak: TimeSpan.FromSeconds(10),
onBreak: (r, t) =>
{
// 熔斷的時候處理事件
},
onReset: () =>
{
// 恢復的時候的處理
}, onHalfOpen: () =>
{
// 恢復之前進行處理
});
var message = new HttpResponseMessage()
{
Content = new StringContent("不要慌,老闆沒有跑路,只是和老婆的妹妹出去了,要等一下!")
};
var fallback = Policy<HttpResponseMessage>.Handle<BrokenCircuitException>().FallbackAsync(message);
var fallbackBreak = Policy.WrapAsync(fallback, breakPolicy);
services.AddHttpClient("GreeterClient").AddPolicyHandler(fallbackBreak);
上面程式碼就是當熔斷後,過來的請求會有BrokenCircuitException異常,那麼捕獲到BrokenCircuitException異常,那麼就告訴使用者店沒有倒閉,等一下就好。
這種就是比較優雅的降級。
上面這種var fallbackBreak = Policy.WrapAsync(fallback, breakPolicy); 就是將幾個policy組合在一起,然後給某個或者某些HttpClient 增加該組合策略,比如Policy.WrapAsync(fallback, breakPolicy,xxx,yyy)等。
接下來解釋一下限流。
為什麼要限流呢? 如果沒有限流其實熔斷的意義是不大的。
為什麼這麼說呢? 比如說公司有1百萬請求,然後這個時候熔斷了,但是這100w請求還在啊,只有恢復服務,伺服器又要進行熔斷,一下子就瞬間爆炸。
var bulk = Policy.BulkheadAsync<HttpResponseMessage>(
maxParallelization:30,
maxQueuingActions:20,
onBulkheadRejectedAsync:context=>Task.CompletedTask);
var message = new HttpResponseMessage()
{
Content = new StringContent("你沒有搶到號碼,下次再來。")
};
var fallbackBulk = Policy<HttpResponseMessage>.Handle<BulkheadRejectedException>().FallbackAsync(message);
var fallbackBreak = Policy.WrapAsync(bulk, fallbackBulk);
上面這個就是限流了。
maxParallelization 表示可以併發處理30個請求,maxQueuingActions表示如果併發處於30,那麼會加入到佇列中,佇列中最大能存20個。
如果佇列中也存不下,那麼就會丟擲BulkheadRejectedException這種拒絕異常,一但出現異常,第二個策略就捕獲到了,然後給出一些友好的提示。
結
下一節,閘道器。