Polly:尊重与自定义异常通信的 Retry-After?
Polly: Honoring Retry-After that is communicated with a custom exception?
所以我必须使用一个库,该库本质上对可能选择限制流量的远程系统执行 POST。如果是这样,它 returns 429 和特定的秒数在 Retry-After header...
throw new ThrottledException(retryAfterSeconds);
如何设置 Polly 策略来捕获此自定义异常,然后在 exception.RetryAfter
秒后重试?
好的,这比需要的要棘手一些,但这只是因为
在这种情况下,重试通过类型为 SigsThrottledException
的自定义异常进行通信,它有一个包含以秒为单位的请求退避时间的字段。
var policy = Policy
.Handle<SigsThrottledException>(e => e.RetryAfterInSeconds > 0)
.WaitAndRetryAsync(
retryCount: retries,
sleepDurationProvider: (i, e, ctx) =>
{
var ste = (SigsThrottledException)e;
return TimeSpan.FromSeconds((double)ste.RetryAfterInSeconds);
},
onRetryAsync: async (e, ts, i, ctx) =>
{
// Do something here
};);
这是一个如何使用政策的例子。你不能只是 。你必须明确地使用它。
[TestMethod]
public async Task SigsPollyRetriesOnThrottle()
{
var retryResponse = new HttpResponseMessage
{
StatusCode = (HttpStatusCode)429,
Content = new StringContent("{}"),
};
retryResponse.Headers.Add("Retry-After", "1");
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.SetupSequence<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(retryResponse)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
});
var client = new HttpClient(mockMessageHandler.Object);
// Retry once after waiting 1 second
var retryPolicy = Policy
.Handle<SigsThrottledException>(e => e.RetryAfterInSeconds > 0)
.WaitAndRetryAsync(
retryCount: 1,
sleepDurationProvider: (i, e, ctx) =>
{
var ste = (SigsThrottledException)e;
return TimeSpan.FromSeconds((double)ste.RetryAfterInSeconds);
},
onRetryAsync: async (e, ts, i, ctx) =>
{
// Do something here
};);
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var response = await retryPolicy.ExecuteAsync(async () =>
{
Uri substrateurl = new Uri("https://substrate.office.com/");
return await SIGSClient.Instance.PostAsync(client, substrateurl, new UserInfo(), "faketoken", new Signal(), Guid.NewGuid()).ConfigureAwait(false);
}
);
Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
stopWatch.Stop();
Assert.IsTrue(stopWatch.ElapsedMilliseconds > 1000); // Make sure we actually waited at least a second
}
所以我必须使用一个库,该库本质上对可能选择限制流量的远程系统执行 POST。如果是这样,它 returns 429 和特定的秒数在 Retry-After header...
throw new ThrottledException(retryAfterSeconds);
如何设置 Polly 策略来捕获此自定义异常,然后在 exception.RetryAfter
秒后重试?
好的,这比需要的要棘手一些,但这只是因为
在这种情况下,重试通过类型为 SigsThrottledException
的自定义异常进行通信,它有一个包含以秒为单位的请求退避时间的字段。
var policy = Policy
.Handle<SigsThrottledException>(e => e.RetryAfterInSeconds > 0)
.WaitAndRetryAsync(
retryCount: retries,
sleepDurationProvider: (i, e, ctx) =>
{
var ste = (SigsThrottledException)e;
return TimeSpan.FromSeconds((double)ste.RetryAfterInSeconds);
},
onRetryAsync: async (e, ts, i, ctx) =>
{
// Do something here
};);
这是一个如何使用政策的例子。你不能只是
[TestMethod]
public async Task SigsPollyRetriesOnThrottle()
{
var retryResponse = new HttpResponseMessage
{
StatusCode = (HttpStatusCode)429,
Content = new StringContent("{}"),
};
retryResponse.Headers.Add("Retry-After", "1");
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.SetupSequence<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(retryResponse)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
});
var client = new HttpClient(mockMessageHandler.Object);
// Retry once after waiting 1 second
var retryPolicy = Policy
.Handle<SigsThrottledException>(e => e.RetryAfterInSeconds > 0)
.WaitAndRetryAsync(
retryCount: 1,
sleepDurationProvider: (i, e, ctx) =>
{
var ste = (SigsThrottledException)e;
return TimeSpan.FromSeconds((double)ste.RetryAfterInSeconds);
},
onRetryAsync: async (e, ts, i, ctx) =>
{
// Do something here
};);
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var response = await retryPolicy.ExecuteAsync(async () =>
{
Uri substrateurl = new Uri("https://substrate.office.com/");
return await SIGSClient.Instance.PostAsync(client, substrateurl, new UserInfo(), "faketoken", new Signal(), Guid.NewGuid()).ConfigureAwait(false);
}
);
Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
stopWatch.Stop();
Assert.IsTrue(stopWatch.ElapsedMilliseconds > 1000); // Make sure we actually waited at least a second
}