重新整理 .net core 實踐篇————polly失敗重試[三十四]

不問前世發表於2021-07-02

前言

簡單整理一下polly 重試。

正文

在開發程式中一般都有一個重試幫助類,那麼polly同樣有這個功能。

polly 元件包:

polly 功能包

polly.Extensions.Http 專門針對http的擴充套件包

Miscrosoft.Extension.Http.Polly 看到這個名字,那麼99%是針對官方.net core的擴充套件包,是HttpClientFactory 的擴充套件。

polly有下面一些功能:

  1. 失敗重試

  2. 服務熔斷

  3. 超時處理

  4. 艙壁處理

  5. 快取策略

  6. 失敗降級

  7. 組合策略

其他都好理解,但是艙壁處理 是什麼呢?

這個是限流功能,服務定義最大的流量和佇列,避免請求量過大而崩潰。

組合策略,就是對上面的功能可以自由組合。

polly 使用步驟:

  1. 定於要處理的異常型別和返回值

  2. 定義要處理的工作

  3. 使用定義的策略來執行程式碼

polly的原始碼:

https://github.com/App-vNext/Polly

polly 針對http的擴充套件包:

https://github.com/App-vNext/Polly.Extensions.Http

適合polly 重試的場景:

1.服務"失敗"是短暫的,可自愈的。

針對這種http請求,無狀態的是非常適用的。

2.服務是"冪等"的,重複呼叫不會產生副作用

這個冪等可以簡單為多次執行,並不會影響到最初達到的效果。

比如說個人查詢,查詢多次的話,效果是相同的。具體冪等可以百度一下,不過覺的看下https://baike.baidu.com/item/%E5%B9%82%E7%AD%89/8600688?fr=aladdin就夠了,因為有些人講的神乎其神。

具體場景:

  1. 服務閃斷

  2. 部分節點不可用

在使用重試過程中,最好達到下面幾個要求:

  1. 設定失敗次數

  2. 設定有步長策略的失敗等待間隔

  3. 設定降級響應

  4. 設定斷路器

前面說過polly 是針對httpClientFactory 的擴充套件,那麼其融合性其實是非常好的。

比如說,grpc當監聽到AddTransientHttpErrorPolicy錯誤的時候,那麼可以啟動對應的策略進行重試,RetryAsync就是polly的擴充套件,裡面設定重試次數20次。

services.AddGrpcClient<Helloworld.Greeter.GreeterClient>(options =>
{
	options.Address = new Uri("https://localhost:5001");
}).ConfigurePrimaryHttpMessageHandler(provider =>
{
	var handle = new SocketsHttpHandler();
	handle.SslOptions.RemoteCertificateValidationCallback = (a, b, c, d) => true;
	return handle;
}).AddTransientHttpErrorPolicy(p=>p.RetryAsync(20));

看下AddTransientHttpErrorPolicy,其實個人感覺RetryAsync倒是沒有必要去看,看AddTransientHttpErrorPolicy 是為了知道啥時候會觸發這個重試,以及知道如何去定義我們自己的Policy。

AddTransientHttpErrorPolicy:

public static IHttpClientBuilder AddTransientHttpErrorPolicy(
	this IHttpClientBuilder builder, 
	Func<PolicyBuilder<HttpResponseMessage>, IAsyncPolicy<HttpResponseMessage>> configurePolicy)
{
	if (builder == null)
	{
		throw new ArgumentNullException(nameof(builder));
	}

	if (configurePolicy == null)
	{
		throw new ArgumentNullException(nameof(configurePolicy));
	}
	
	var policyBuilder = HttpPolicyExtensions.HandleTransientHttpError();

	// Important - cache policy instances so that they are singletons per handler.
	var policy = configurePolicy(policyBuilder);

	builder.AddHttpMessageHandler(() => new PolicyHttpMessageHandler(policy));
	return builder;
}

從名字中可以看到HandleTransientHttpError就是處理異常的。

public static PolicyBuilder<HttpResponseMessage> HandleTransientHttpError()
{
  return Policy<HttpResponseMessage>.Handle<HttpRequestException>().OrTransientHttpStatusCode();
}

這裡表示HttpRequestException 會觸發,或者OrTransientHttpStatusCode一些狀態碼下會觸發,看下OrTransientHttpStatusCode。

OrTransientHttpStatusCode:

public static PolicyBuilder<HttpResponseMessage> OrTransientHttpStatusCode(
  this PolicyBuilder<HttpResponseMessage> policyBuilder)
{
  if (policyBuilder == null)
	throw new ArgumentNullException(nameof (policyBuilder));
  return policyBuilder.OrResult(HttpPolicyExtensions.TransientHttpStatusCodePredicate);
}

檢視HttpPolicyExtensions.TransientHttpStatusCodePredicate:

 private static readonly Func<HttpResponseMessage, bool> TransientHttpStatusCodePredicate = (Func<HttpResponseMessage, bool>) (response => response.StatusCode >= HttpStatusCode.InternalServerError || response.StatusCode == HttpStatusCode.RequestTimeout);

這裡面表示 HttpStatusCode.InternalServerError(500)和 HttpStatusCode.RequestTimeout(408) 會進行重試。

同樣可以設定間隔時間進行重試:

services.AddGrpcClient<Helloworld.Greeter.GreeterClient>(options =>
{
	options.Address = new Uri("https://localhost:5001");
}).ConfigurePrimaryHttpMessageHandler(provider =>
{
	var handle = new SocketsHttpHandler();
	handle.SslOptions.RemoteCertificateValidationCallback = (a, b, c, d) => true;
	return handle;
}).AddTransientHttpErrorPolicy(p=>p.WaitAndRetryAsync(20,i=>TimeSpan.FromSeconds(2)));

還可以使用WaitAndRetryForever 表示一直重試,直到成功,看需求。

同樣也可以自定義一些狀態或者一些情況,做一些事情:

var reg = services.AddPolicyRegistry();

reg.Add("retryforever", Policy.HandleResult<HttpResponseMessage>(message =>
{
	return message.StatusCode == System.Net.HttpStatusCode.Created;
}).RetryForever());

services.AddHttpClient("GreeterClient").AddPolicyHandlerFromRegistry("retryforever");

上面表示針對GreeterClient客戶端,增加一些retryforever的處理策略。

後面會介紹這種策略架子是如何實現的,在細節篇。

那通過Polic就可以針對不同場景,進行定義不同的策略,做出一些相應。看專案需求,這裡就不多介紹了,每個專案都不一樣。

下一節polly熔斷。

相關文章