C# - Polly를 이용한 클라이언트 측의 요청 재시도
지난 글에서,
C# - 서버 측의 요청 제어 (Microsoft.AspNetCore.RateLimiting)
; https://www.sysnet.pe.kr/2/0/13362
Microsoft.AspNetCore.RateLimiting을 사용해 요청 제어를 하는 방법을 설명했는데요, 이런 서비스를 이용하는 클라이언트는 반대로 제한이 걸린 서비스에 대한 호출을 재시도해야 합니다.
파이썬의 경우, 이럴 때
Tenacity,
backoff 등의 패키지를 이용해 간단한 특성 처리를 할 수 있는데요,
import openai # for OpenAI API calls
from tenacity import (
retry,
stop_after_attempt,
wait_random_exponential,
) # for exponential backoff
@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))
def completion_with_backoff(**kwargs):
return openai.Completion.create(**kwargs)
completion_with_backoff(model="text-davinci-002", prompt="Once upon a time,")
닷넷의 경우에는 Polly 라이브러리를 이용해,
Polly
; https://www.nuget.org/packages/polly/
App-vNext/Polly
; https://github.com/App-vNext/Polly#installing-via-the-net-sdk
Implement HTTP call retries with exponential backoff with IHttpClientFactory and Polly policies
; https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly
(비록 특성을 지정하는 것처럼 간단하게는 아니지만) 유사하게 구현할 수 있습니다.
예를 들어, 지수 단위로 대기 시간이 늘어나는 Polly 정책은 (
문서에서 나온 것 중에서) 다음과 같이 코딩할 수 있습니다.
// Retry a specified number of times, using a function to
// calculate the duration to wait between retries based on
// the current retry attempt (allows for exponential back-off)
// In this case will wait for
// 2 ^ 1 = 2 seconds then
// 2 ^ 2 = 4 seconds then
// 2 ^ 3 = 8 seconds then
// 2 ^ 4 = 16 seconds then
// 2 ^ 5 = 32 seconds
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
이것을 적용해 지난 글에서 다룬 Rate Limit가 걸린 서비스를 호출하는 예제 클라이언트 코드를 다음과 같이 재작성할 수 있습니다.
using Polly;
using System.Net;
namespace ConsoleApp1;
internal class Program
{
public const int MaxRequests = 7;
static async Task<int> Main(string[] args)
{
var retryPolicy = Policy
.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.ServiceUnavailable)
.RetryAsync(5, (result, retryCount, context) =>
{
Console.WriteLine($"Retry {retryCount} of {context.PolicyKey} at {DateTime.Now}");
Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
});
var client = new HttpClient();
var context = new Context();
var requests = new Task<HttpResponseMessage>[MaxRequests];
for (int i = 0; i < MaxRequests; i++)
{
requests[i] = retryPolicy.ExecuteAsync(async ctx =>
{
return await client.GetAsync("http://localhost:5203/WeatherForecast");
}, context);
}
await Task.WhenAll(requests);
for (int i = 0; i < MaxRequests; i++)
{
Console.WriteLine($"{i}: {requests[i].Result.StatusCode}");
}
return 0;
}
}
Retry 1 of AsyncRetryPolicy`1-5df14b8b at 2023-06-01 오후 2:58:14
Retry 2 of AsyncRetryPolicy`1-5df14b8b at 2023-06-01 오후 2:58:16
0: OK
1: OK
2: OK
3: OK
4: OK
5: OK
6: OK
간단하죠? ^^ 추가적으로 Polly의 다양한 사용법은 다음의 글을 읽어보시면 도움이 되실 것입니다.
C# - Polly를 사용하여 탄력적인 응용 프로그램 만들기
; https://jacking75.github.io/NET_lib_polly/
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]