HttpClientFactory in ASP.NET Core 2.1 Part 1 介绍
HttpClientFactory in ASP.NET Core 2.1 Part 1
原文地址:https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore
在 ASP.NET Core 2.1 中带来了一个新的 HttpClientFacotory 特性,在从应用使用 HttpClient 实例对外发出 Web 请求的时候,它可以帮助开发者解决常见的问题。
介绍
本文完成于 2017 年 10 月中旬,当时我注意到新的 HttpClientFactory 出现在 GitHub 的仓库中,我对它的出现很好奇,并好奇 ASP.NET 团队接下来要做什么,所以我深入到此时的代码中。从此时开始我一直关注着它,通过阅读提交的内容、问题和改进请求讨论来观察功能的演进。
最近,该特性开始被讨论的越来越多,在最近的 Damian Edwards 和 David Fowler 在 NDC 伦敦的讨论中也被涉及。实际上,在撰写本介绍的时候,它还被展示在 Jeff Fritz’s livestream show 和 ASP.NET Community Standup。Ryan Nowak 的建议是,他是 ASP.NET 主力团队中该功能的开发者之一,是现在已经足够稳定可以基于它开发开发了。
注:请注意到本文是在官方预览发布之前撰写的,进而,在正式发布的时候,情况可能发生了一些变化,
什么是 HttpClientFactory?
在 ASP.NET 团队的词汇里,它是 用来创建 HttpClient 实例的工厂,并且是伴随 ASP.NET Core 2.1 发布带来的新特性。基于你过去使用 HttpClient 的体验,你可能遇到也可能没有遇到一些问题,有时甚至你没有感到是问题。
第一个问题是,当你在应用中创建多个 HttpClient 实例的时候,你会遇到两个问题:
- 它是低效的,因为每个都会针对远程服务器拥有自己的连接池。这意味着你会在每个客户端每次重新连接到远程服务器的时候花费代价。
- 更大的问题是,如果你创建了大量的 HttpClient,你会遇到 socket 耗尽问题,你太快地使用了大量 socket。你同时可以打开多少 socket 存在一个限额。当你 dispose HttpClient 的时候,它会保持已经打开的连接直到 240s 在 TIME_WAIT 状态 (此时,来自远程服务器的包仍然可以到达)。
HttpClient 实现了 IDispose 接口,这通常导致开发者遵循通常的在使用 IDisposable 对象,使用 using 块来创建它。这可以确保在一旦你完成操作,对象的生命周期离开 scope 的时候被 dispose 掉。如果你希望更深入了解这一点,更好的文档是来自 ASP.NET 怪兽的 “You’re using HttpClient wrong and it’s destablizing your software”。
推荐的使用方式是重用 HttpClient 实例,以便它的连接可以被重用。HttpClient 可以被重复使用,且不会导致问题。它是线程安全且可以共享的。常见的方式是将它注册为一个 DI 框架中的单例,或者创建一个封装器将它作为静态成员持有。
但是,这导致了另一个问题。使用单个的 HttpClient 将保持连接打开以至于不会实时刷新 DNS。现在该连接将永远不会获得 DNS 更新,所以你永远不会得到你要访问的服务器地址的最新更新。你在多个服务器之间做负载均衡的时候,或者做蓝/绿部署的时候就是一个大问题。如果服务器已经下线了,那么你使用 HttpClient 连接的 IP 也将不会对请求做出响应。关于此问题,可以阅读 “Singleton HttpClient? Beware of this serious behaviour and how to fix it” and “Singleton HttpClient doesn’t respect DNS changes”.
HttpClientFacotory 是用来帮助解决这些问题,并提供了一个新的机制来创建 HttpClient 实例,可以在后台为我们很好地管理它们。它可以为我们做正确的事,我们可以专注于其它的问题,而不是上述列举的直接使用 HttpClient 带来的问题。HttpClientFactory 管理处理器的生命周期,所以我们拥有了一个可以重用的连接池,它还会进行轮换,所以 DNS 也不会固定掉。
使用 HttpClient 的代价本质上是创建 HttpClientHandler 和连接。池化意味着我们可以更有效地使用连接。当你通过 HttpClientFactory 来获取 HttpClient 的时候,每次都会得到一个新的 HttpClient 对象,这意味着你不必担心状态变化的问题。HttpClient 可能 (也可能没有) 使用现存在的池中的 HttpClientHandler ,进而使用现存的已经打开的连接。
默认情况下,每个新的 HttpClientHandler (从 HttpMessageHander 派生) 将会被创建为一个 2 分钟生命周期。在创建它的处理器链的时候,可以针对名称控制。一旦到达生命周期,它不是马上被 disposed,而是被放入到一个过期池中。任何还在使用它的客户端可以继续使用它而不会有问题。有一个后台线程负责检查过期池,检查是否所有针对 handler 的引用已经无效了,此时它可以被真正 dispose 掉。一旦处理器过期,任何新的对客户端的请求都将返回新的处理器。
该机制可以工作很好,但是还有其他的 .NET Core 方面的情况,可能在未来改进。.NET Core 团队致力于新的 MessageHandler ,它可以更正确地管理 DNS,它的任务更为长久,意味着连接可以更为有效地共享。新的处理器还被设计为跨操作系统的更加一致的功能,到该工作完成时,上面的池化处理器只是一个临时处理。
如何使用 HttpClientFactory
本文中,我将演示最为基本的使用场景之一来开始使用 HttpClientFactory。为了该示例,我们将缠绵一个简单的 WebAPI 项目,然后编辑 .csproj 文件来升级到新的 ASP.NET Core 2.1 版本。
然后,我们需要打开 Startup.cs 文件,并注册一个服务。HttpClientFactory 包含有多种注册的变体,我们使用的方式如下所示:
service.AddHttpClient();
在幕后,这将注册一些必要的服务,其中实现了接口 IHttpClientFactory。随后,我们需要更新默认的控制器来使用该特性。
在控制器中,我们将添加针对 IHttpClientFactory 的依赖,它将通过依赖注入注入到控制器中。IHttpClientFactory 支持我们请求并接收 HttpClient 实例。
在 Get action 操作方法中,使用 HttpClientFactory 来创建客户端。在幕后,HttpClientFactory 将创建一个新的 HttpClient 实例。但是请稍等,不是使用前面的 new 操作符来针对每次请求创建,相反,有点不同的是,HttpClient 不是真正的问题,是 HttpClientHandler 用来发出 Http 调用,它才是真正的问题。它打开到外部服务的连接,然后保持打开并阻塞 socket,甚至在 HttpClient 被 dispose 之后。
HttpClientFactory 池化 HttpClientHandler 实例并管理其生命周期,以解决我前面介绍的问题。每次在请求一个 HttpClient 实例的时候,我们得到一个新的实例,它可能也可能不是使用现存的 HttpClientHandler。而 HttpClient 本身并不难构建,所以构建新的 HttpClient 并不是问题。
一旦创建的 HttpClientHandler 被池化,默认被持有 2 分钟。这意味着新的请求创建 HttpClient 可能共享底层的处理器,进而共享连接。在 HttpClient 活动的时候,它使用的底层处理器保持可用并继续共享连接。
在 2 分钟之后,每个 HttpClientHandler 被标记为过期。标记为过期状态只是简单地标记,所以只是在任何新的创建 HttpClient 实例的时候不再被使用。并不是立即被 dispose 掉。实际上,其它的 HttpClient 可能还在使用它。HttpClientFactory 使用后台服务来监控这些过期的处理器,一旦它们不再被引用,就会正确地 dispose 掉,使得底层的连接也被关闭掉。
池化功能帮助减少了 socket 耗尽的风险,刷新过程则通过确保没有长寿命的 HttpClientHandler 实例来帮助解决 DNS 更新问题,以及连接挂起问题。通过 HttpClientFactory 功能来管理是一个合理的折衷方案,
总结
这只是一个介绍文章,后面的文章将深入 HttpClientFactory 的高级特性,如何基于配置创建命名的 HttpClient ,如何创建类型化的 HttpClient,这才是真正的闪光点。希望你能够掌握它们,即是在基本的示例中。如何改进对 HttpClient 的使用,更加有效和正确。我们不需要考虑管理客户端的生命周期,或者担心掉入 DNS 问题。
Part 1 – HttpClientFactory in ASP.NET Core 2.1 Part 1 介绍
Part 2 – HttpClientFactory in ASP.NET Core 2.1 Part 2:定义命名和类型化的客户端
Part 3 – HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件
Part 4 – HttpClientFacotry Part 4: 集成 Polly 处理瞬时失效
Part 5 – HttpClientFactory in ASP.NET Core 2.1 Part 5: 日志