.NET Core 3.0之深入源码理解HttpClientFactory之实战
写在前面
前面两篇文章透过源码角度,理解了HttpClientFactory的内部实现,当我们在项目中使用时,总会涉及以下几个问题:
- HttpClient超时处理以及重试机制
- HttpClient熔断器模式的实现
- HttpClient日志记录与追踪链
接下来我们将从使用角度对上述问题作出说明。
详细介绍
以下代码参考了MSDN,因为代码里展示的GitHub接口确实可以调通,省的我再写一个接口出来测试了。
HttpClient超时处理和重试机制
在此之前,我们需要了解一下Polly这个库,Polly是一款基于.NET的弹性及瞬间错误处理库, 它允许开发人员以顺畅及线程安全的方式执行重试(Retry),断路器(Circuit),超时(Timeout),隔板隔离(Bulkhead Isolation)及后背策略(Fallback)。
以下代码描述了在.NET Core 3.0中如何使用超时机制。
Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10))
那么如何将其注册到对应的HttpClient实例呢,有很多种方式:
- 通过AddPolicyHandler注册
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
}).AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)));
- 声明Policy注册对象,并将超时策略对象添加进去
var registry = services.AddPolicyRegistry();
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));
registry.Add("regular", timeout);
调用方式
services.AddHttpClient("github", c =>[code]{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");// GitHub API versioning
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); // GitHub requires a user-agent
}).AddPolicyHandlerFromRegistry("regular")[/code]
Polly重试也很简单
var policyRegistry = services.AddPolicyRegistry();[code]
policyRegistry.Add("MyHttpRetry",HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(3
,retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)
)));[/code]
这里的重试设置是在第一次调用失败后,还会有三次机会继续重试,每个请求的时间间隔是指数级延迟。
重试功能除了可以使用Polly实现外,还可以使用DelegatingHandler,DelegatingHandler继承自HttpMessageHandler,用于”处理请求、响应回复“,本质上就是一组HttpMessageHandler的有序组合,可以视为是一个“双向管道”。
此处主要展示DelegatingHandler的使用方式,在实际使用中,仍然建议使用Polly重试。
private class RetryHandler : DelegatingHandler[code]{
public int RetryCount { get; set; } = 5;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
for (var i = 0; i < RetryCount; i++)
{
try
{
return await base.SendAsync(request, cancellationToken);
}
catch (HttpRequestException) when (i == RetryCount - 1)
{
throw;
}
catch (HttpRequestException)
{
// 五十毫秒后重试
await Task.Delay(TimeSpan.FromMilliseconds(50));
}
}
}
}[/code]
注册方式如下:
services.AddHttpClient("github", c =>[code]{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); // GitHub API versioning
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); // GitHub requires a user-agent
})
.AddHttpMessageHandler(() => new RetryHandler());[/code]
HttpClient熔断器模式的实现
如果非常了解Polly库的使用,那么熔断器模式的实现也会非常简单,
var policyRegistry = services.AddPolicyRegistry();[code]
policyRegistry.Add("MyCircuitBreaker",HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: 10,durationOfBreak: TimeSpan.FromSeconds(30)));[/code]
这里的熔断器设置规则是在连续10次请求失败后,会暂停30秒。这个地方可以写个扩展方法注册到IServiceCollection中。
HttpClient日志记录与追踪链
日志记录这块与追踪链,我们一般会通过request.Header实现,而在微服务中,十分关注相关调用方的信息及其获取,一般的做法是通过增加请求Id的方式来确定请求及其相关日志信息。
实现思路是增加一个DelegatingHandler实例,用以记录相关的日志以及请求链路
public class TraceEntryHandler : DelegatingHandler[code]{
private TraceEntry TraceEntry { get; set; }
public TraceEntryHandler(TraceEntry traceEntry)
{
this.TraceEntry = traceEntry;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.TryAddWithoutValidation("X-TRACE-CID", this.TraceEntry.ClientId);
request.Headers.TryAddWithoutValidation("X-TRACE-RID", this.TraceEntry.RequestId);
request.Headers.TryAddWithoutValidation("X-TRACE-SID", this.TraceEntry.SessionId);
if (this.TraceEntry.SourceIP.IsNullOrEmpty())
{
request.Headers.TryAddWithoutValidation("X-TRACE-IP", this.TraceEntry.SourceIP);
}
if (this.TraceEntry.SourceUserAgent.IsNullOrEmpty())
{
request.Headers.TryAddWithoutValidation("X-TRACE-UA", this.TraceEntry.SourceUserAgent);
}
if (this.TraceEntry.UserId.IsNullOrEmpty())
{
request.Headers.TryAddWithoutValidation("X-TRACE-UID", this.TraceEntry.UserId);
}
return base.SendAsync(request, cancellationToken);
}
}[/code]
我在查找相关资料的时候,发现有个老外使用CorrelationId组件实现,作为一种实现方式,我决定要展示一下,供大家选择:
public class CorrelationIdDelegatingHandler : DelegatingHandler[code]{
private readonly ICorrelationContextAccessor correlationContextAccessor;
private readonly IOptions<CorrelationIdOptions> options;
public CorrelationIdDelegatingHandler(
ICorrelationContextAccessor correlationContextAccessor,
IOptions<CorrelationIdOptions> options)
{
this.correlationContextAccessor = correlationContextAccessor;
this.options = options;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains(this.options.Value.Header))
{
request.Headers.Add(this.options.Value.Header, correlationContextAccessor.CorrelationContext.CorrelationId);
}
// Else the header has already been added due to a retry.
return base.SendAsync(request, cancellationToken);
}
}[/code]
参考链接:
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.0
https://rehansaeed.com/optimally-configuring-asp-net-core-httpclientfactory/
- .NET Core 3.0之深入源码理解HttpClientFactory(一)
- .NET Core 3.0之深入源码理解Configuration(三)
- .NET Core 3.0之深入源码理解Host(二)
- .NET Core 3.0之深入源码理解ObjectPool(二)
- .NET Core 3.0之深入源码理解Kestrel的集成与应用(二)
- .NET Core 3.0之深入源码理解Kestrel的集成与应用(一)
- .NET Core 3.0之深入源码理解Configuration(一)
- .NET Core 3.0之深入源码理解Configuration(二)
- 简约之美Jodd-http--深入源码理解http协议
- 【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程...
- 简约之美Jodd-http--深入源码理解http协议
- 不简单的工厂:实际体验 .NET Core 2.1 新生物 HttpClientFactory
- Android 源码系列之<四>从源码的角度深入理解LayoutInflater.Factory之主题切换(上)
- .NET Core 3. aec 0之深入源码理解ObjectPool(一)
- 工厂参观记:.NET Core 中 HttpClientFactory 如何解决 HttpClient 臭名昭著的问题
- Android 源码系列之<五>从源码的角度深入理解LayoutInflater.Factory之主题切换(中)
- .NET Core 2.1中HttpClientFactory的最佳实践记录
- 简约之美Jodd-http--深入源码理解http协议
- 在 .NET Core 中结合 HttpClientFactory 使用 Polly(上篇)
- 在 .NET Core 中结合 HttpClientFactory 使用 Polly(中篇)