.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中如何使用超时机制。
1: Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10))
那么如何将其注册到对应的HttpClient实例呢,有很多种方式:
- 通过AddPolicyHandler注册
1: services.AddHttpClient("github", c =>
2: {
3: c.BaseAddress = new Uri("https://api.github.com/");
4:
5: c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
6: c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
7: }).AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)));
- 声明Policy注册对象,并将超时策略对象添加进去
1: var registry = services.AddPolicyRegistry();
2: var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));
3: registry.Add("regular", timeout);
调用方式
1: services.AddHttpClient("github", c =>
2: {
3: c.BaseAddress = new Uri("https://api.github.com/");
4:
5: c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); // GitHub API versioning
6: c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); // GitHub requires a user-agent
7: }).AddPolicyHandlerFromRegistry("regular")
Polly重试也很简单
1: var policyRegistry = services.AddPolicyRegistry();
2:
3: policyRegistry.Add("MyHttpRetry",HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(
3
,retryAttempt => TimeSpan.FromSeconds(
Math.Pow(2, retryAttempt)
)));
这里的重试设置是在第一次调用失败后,还会有三次机会继续重试,每个请求的时间间隔是指数级延迟。
重试功能除了可以使用Polly实现外,还可以使用DelegatingHandler,DelegatingHandler继承自HttpMessageHandler,用于”处理请求、响应回复“,本质上就是一组HttpMessageHandler的有序组合,可以视为是一个“双向管道”。
此处主要展示DelegatingHandler的使用方式,在实际使用中,仍然建议使用Polly重试。
1: private class RetryHandler : DelegatingHandler
2: {
3: public int RetryCount { get; set; } = 5;
4:
5: protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
6: {
7: for (var i = 0; i < RetryCount; i++)
8: {
9: try
10: {
11: return await base.SendAsync(request, cancellationToken);
12: }
13: catch (HttpRequestException) when (i == RetryCount - 1)
14: {
15: throw;
16: }
17: catch (HttpRequestException)
18: {
19: // 五十毫秒后重试
20: await Task.Delay(TimeSpan.FromMilliseconds(50));
21: }
22: }
23: }
24: }
注册方式如下:
1: services.AddHttpClient("github", c =>
2: {
3: c.BaseAddress = new Uri("https://api.github.com/");
4:
5: c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); // GitHub API versioning
6: c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); // GitHub requires a user-agent
7: })
8: .AddHttpMessageHandler(() => new RetryHandler());
HttpClient熔断器模式的实现
如果非常了解Polly库的使用,那么熔断器模式的实现也会非常简单,
1: var policyRegistry = services.AddPolicyRegistry();
2:
3: policyRegistry.Add("MyCircuitBreaker",HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: ,durationOfBreak: TimeSpan.FromSeconds()));
这里的熔断器设置规则是在连续10次请求失败后,会暂停30秒。这个地方可以写个扩展方法注册到IServiceCollection中。
HttpClient日志记录与追踪链
日志记录这块与追踪链,我们一般会通过request.Header实现,而在微服务中,十分关注相关调用方的信息及其获取,一般的做法是通过增加请求Id的方式来确定请求及其相关日志信息。
实现思路是增加一个DelegatingHandler实例,用以记录相关的日志以及请求链路
1: public class TraceEntryHandler : DelegatingHandler
2: {
3: private TraceEntry TraceEntry { get; set; }
4:
5: public TraceEntryHandler(TraceEntry traceEntry)
6: {
7: this.TraceEntry = traceEntry;
8: }
9:
10: protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
11: {
12: request.Headers.TryAddWithoutValidation("X-TRACE-CID", this.TraceEntry.ClientId);
13: request.Headers.TryAddWithoutValidation("X-TRACE-RID", this.TraceEntry.RequestId);
14: request.Headers.TryAddWithoutValidation("X-TRACE-SID", this.TraceEntry.SessionId);
15: if (this.TraceEntry.SourceIP.IsNullOrEmpty())
16: {
17: request.Headers.TryAddWithoutValidation("X-TRACE-IP", this.TraceEntry.SourceIP);
18: }
19:
20: if (this.TraceEntry.SourceUserAgent.IsNullOrEmpty())
21: {
22: request.Headers.TryAddWithoutValidation("X-TRACE-UA", this.TraceEntry.SourceUserAgent);
23: }
24:
25: if (this.TraceEntry.UserId.IsNullOrEmpty())
26: {
27: request.Headers.TryAddWithoutValidation("X-TRACE-UID", this.TraceEntry.UserId);
28: }
29:
30: return base.SendAsync(request, cancellationToken);
31: }
32: }
我在查找相关资料的时候,发现有个老外使用CorrelationId组件实现,作为一种实现方式,我决定要展示一下,供大家选择:
1: public class CorrelationIdDelegatingHandler : DelegatingHandler
2: {
3: private readonly ICorrelationContextAccessor correlationContextAccessor;
4: private readonly IOptions<CorrelationIdOptions> options;
5:
6: public CorrelationIdDelegatingHandler(
7: ICorrelationContextAccessor correlationContextAccessor,
8: IOptions<CorrelationIdOptions> options)
9: {
10: this.correlationContextAccessor = correlationContextAccessor;
11: this.options = options;
12: }
13:
14: protected override Task<HttpResponseMessage> SendAsync(
15: HttpRequestMessage request,
16: CancellationToken cancellationToken)
17: {
18: if (!request.Headers.Contains(this.options.Value.Header))
19: {
20: request.Headers.Add(this.options.Value.Header, correlationContextAccessor.CorrelationContext.CorrelationId);
21: }
22:
23: // Else the header has already been added due to a retry.
24:
25: return base.SendAsync(request, cancellationToken);
26: }
27: }
参考链接:
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之深入源码理解HttpClientFactory(二)
写在前面 上一篇文章讨论了通过在ConfigureServices中调用services.AddHttpClient()方法,并基于此进一步探讨了DefaultHttpClientFactory是 ...
- .NET Core 3.0之深入源码理解HttpClientFactory(一)
写在前面 创建HttpClient实例的时候,在内部会创建HttpMessageHandler链,我们知道HttpMessageHandler是负责建立连接的抽象处理程序,所以HttpClient的维 ...
- .NET Core 3.0之深入源码理解Startup的注册及运行
原文:.NET Core 3.0之深入源码理解Startup的注册及运行 写在前面 开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程 ...
- VUEJS2.0源码理解--优
VUEJS2.0源码理解 http://jiongks.name/blog/vue-code-review/#pingback-112428
- 从路由原理出发,深入阅读理解react-router 4.0的源码
react-router等前端路由的原理大致相同,可以实现无刷新的条件下切换显示不同的页面.路由的本质就是页面的URL发生改变时,页面的显示结果可以根据URL的变化而变化,但是页面不会刷新.通过前 ...
- 基于SpringBoot的Environment源码理解实现分散配置
前提 org.springframework.core.env.Environment是当前应用运行环境的公开接口,主要包括应用程序运行环境的两个关键方面:配置文件(profiles)和属性.Envi ...
- jQuery 2.0.3 源码分析Sizzle引擎解析原理
jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...
- Caffe源码理解2:SyncedMemory CPU和GPU间的数据同步
目录 写在前面 成员变量的含义及作用 构造与析构 内存同步管理 参考 博客:blog.shinelee.me | 博客园 | CSDN 写在前面 在Caffe源码理解1中介绍了Blob类,其中的数据成 ...
- springboot2.0.3源码篇 - 自动配置的实现,发现也不是那么复杂
前言 开心一刻 女儿: “妈妈,你这么漂亮,当年怎么嫁给了爸爸呢?” 妈妈: “当年你爸不是穷嘛!‘ 女儿: “穷你还嫁给他!” 妈妈: “那时候刚刚毕业参加工作,领导对我说,他是我的扶贫对象,我年轻 ...
随机推荐
- V语言横空出世,C/C++/Java/Python/Go地位不保
V语言已在github正式开源,目前已收获近9000星,引发开发者的强烈关注. V语言到底是怎样一门语言?已经有了C/C++/Java/Python/Go..., 我们还需要另外一门语言吗? 先看看V ...
- leadcode的Hot100系列--206. 反转链表
这里使用两种方式, 一个是直接从头往后遍历 -------> 迭代 一个是从最后一个往前遍历 -----> 递归 迭代 定义三个变量:pPre pNext pNow pPre表示当前节点的 ...
- 借助URLOS快速安装Discuz论坛
### 简介 Discuz!是个可免费下载的PHP网络论坛程序,简称DZ,由戴志康所创立,当前最新版本是Discuz! X3.4.前身为Crossday Bulletin,最初改自XMBForum,尔 ...
- 字符串匹配Boyer-Moore算法:文本编辑器中的查找功能是如何实现的?---这应该讲的最容易懂的文章了!
关于字符串匹配算法有很多,之前我有讲过一篇 KMP 匹配算法:图解字符串匹配 KMP 算法,不懂 kmp 的建议看下,写的还不错,这个算法虽然很牛逼,但在实际中用的并不是特别多.至于选择哪一种字符串匹 ...
- django实战仿慕课网在线视频网站(完成了85%以上的功能已上传github:https://github.com/huwei86/mxonline):
1. 前台功能模块 基本模块:登录 注册 找回密码 / 全局搜索 / 个人中心, 课程功能:课程管理 / 讲师管理 / 授课机构管理 热门推荐 相关课程推荐 用户操作管理:用户收藏 / 课程评论 ...
- django基础知识之GET属性:
GET属性 QueryDict类型的对象 包含get请求方式的所有参数 与url请求地址中的参数对应,位于?后面 参数的格式是键值对,如key1=value1 多个参数之间,使用&连接,如ke ...
- 使用react+redux+react-redux+react-router+axios+scss技术栈从0到1开发一个applist应用
先看效果图 github地址 github仓库 在线访问 初始化项目 #创建项目 create-react-app applist #如果没有安装create-react-app的话,先安装 npm ...
- Java基础之回味finally
平时大家try…catch…finally语句用的不少,知道finally块一定会在try…catch..执行结束时执行,但是具体是在什么时候执行呢,今天我们一起来看下. public static ...
- WinForm控件之【ComboBox】
基本介绍 下拉文本框应用较为广泛,在winfrom控件当中使用设置也是相对的简单,主要用于存在多种选择的单选操作场景. 常设置属性.事件 DataSource:绑定加载项的数据源,设置属性Displa ...
- Oracle将两张表的数据插入第三张表且第三张表中不存在
1.由于是先查再插所以不能使用insert into table1() values(), 要使用insert into table1() select * table2,不能使用values. 2. ...