前言
簡單整理一下熔斷與限流,跟上一節息息相關。
正文
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這種拒絕異常,一但出現異常,第二個策略就捕獲到了,然后給出一些友好的提示。
結
下一節,網關。