【问题标题】:How should I use the HttpClient in an ASP.NET Core 2.0 API我应该如何在 ASP.NET Core 2.0 API 中使用 HttpClient
【发布时间】:2018-06-12 11:15:09
【问题描述】:

我正在开发 ASP.NET Core 2.0 API,我的 API 需要调用另一个第三方 REST API 来上传和检索文件并获取文件列表和状态信息。我将在 Azure 中托管 API,并计划在我的暂存槽和生产槽之间进行蓝绿部署。

似乎最佳实践的普遍共识是通过在 Startup.cs-->ConfigureServices 方法中的 DI 注册设置 HTTPClient 的 Singleton 实例,以提高性能并避免在我新建时可能发生的套接字错误通过 Using 语句处理每次使用的 HTTPClient 连接。这在以下链接中有所说明;

https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

https://msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.110).aspx#Anchor_5

http://www.nimaara.com/2016/11/01/beware-of-the-net-httpclient/

但如果我这样做了,那么当我在 Azure 中进行蓝绿部署时,我可能会遇到单例实例不会看到任何 DNS 更改的问题。这在以下链接中有所说明;

http://byterot.blogspot.co.uk/2016/07/singleton-httpclient-dns.html

https://github.com/dotnet/corefx/issues/11224

http://www.nimaara.com/2016/11/01/beware-of-the-net-httpclient/

所以.. 现在的普遍共识是使用静态 HTTPClient 实例,但控制 ServicePoint 类的 ConnectionLeaseTimeout 值以将其设置为更短的值,这将强制关闭连接以刷新 DNS。 This 博客文章甚至谈到了 nuget 包中的一个不错的 RestClient 组件(Nima 的 Easy.Common),它可以正确处理 ConnectionLeaseTimeout 以及缓存的 DNS 值。

但是,ASP.NET Core 2.0 似乎没有完全实现 ServicePoint,因此 ASP.Net Core 2.0 目前并不真正支持这种方法。

谁能建议我在 Azure 上运行的 ASP.NET Core 2.0 API 中使用 HttpClient 的正确方法?我希望能够进行蓝绿部署。那么,我是否应该只求助于 Using 语句并在每次使用时更新客户端并遭受性能损失?

似乎必须有一个可靠且高性能的解决方案来满足这一常见需求。

【问题讨论】:

  • 如何在 Azure 上托管和部署 API 无关紧要。您应该考虑对 3rd 方 API 进行 DNS 更改,而不是对 您的 API 进行 DNS 更改。
  • @ToddMenier - 感谢您指出这一点。因此,如果我在使用此 API 的 MVC 应用程序中使用与 HttpClient 相同的方法作为单例,那么当我滚动更新并执行蓝绿翻转时,我需要关注 DNS 刷新。
  • 是的。您的 API 的任何使用者都可能受到该翻转的影响,而不是 API 本身作为某些 3rd-party API 的使用者。

标签: azure design-patterns dotnet-httpclient asp.net-core-2.0 servicepoint


【解决方案1】:

您链接到的一些文章的问题在于,它们导致人们普遍认为设置 ConnectionLeaseTimeout 在套接字层中会产生某种黑魔法,并且如果您在一个不支持它的平台,你就完蛋了。这些文章通过不涉及设置实际作用 来造成损害,即定期向被调用的服务器发送Connection: Close 标头。而已。我已经从源头验证了这一点,并且很容易复制。事实上,我自己在我的 Flurl 库中完成了它,实现细节 herehere

也就是说,我个人认为 DNS 问题有点夸大其词。请注意,例如,在闲置一段时间(默认为 100 秒)后,连接会自动关闭。将HttpClient 用作单身人士的好处远大于风险。

我的建议是每个被调用的第三方服务都使用一个实例。这里的想法是,您可以获得最大的重用,同时仍然利用诸如 DefaultRequestHeaders 之类的东西,这些东西往往特定于一项服务。如果你只调用一个服务,那只是一个单例。 (相反,如果您要调用 1000 个不同的服务,则无法以任何方式避免 1000 个打开的套接字。)如果您不希望连接长时间闲置并且想要防御可能的 DNS 切换使用第 3 方服务,定期发送 Connection: close 标头,或简单地处理并重新创建 HttpClient。请注意,这是一种权衡,而不是完美的解决方案,应该有助于缓解问题。

【讨论】:

  • 在我的用例中,我多次调用一个特定的 Web 服务,所以你说的很有道理。
  • 我突然想到,如果我使用单例并且我对 Web 服务的调用需要不同的用户名/密码身份验证标头(即使它是相同的基本 URL),是否有可能在我发出异步请求之前设置标题是我的原因问题?
  • 不,只要确保您将它们设置在HttpRequestMessage.Headers,而不是HttpClient.DefaultRequestHeaders
【解决方案2】:

文档令人困惑,并且声称您应该只拥有一个 HttpClient 实例的相关文章也有些误导(或误导,取决于观点)。

建议每个应用程序生命周期只有一个实例,但实际上,当您谈论 Web 应用程序时,应该限制在 请求生命周期。当文档警告要为每个请求更新 HttpClient 时,它是在谈论 通过 HttpClient 发出的每个请求,不是您的 Web 应用程序提供的每个请求恰好在使用HttpClient

长短,是的,你应该避免使用usingHttpClient,但是拥有一个请求范围 的实例就可以了。换句话说,它不需要是单例。

【讨论】:

  • 当你说请求范围的实例时,你是说我应该在请求方法中新建一个 HttpClient 的实例吗?
  • 不,只是每个请求都应该有一个实例。您可以通过多种方式实现:DI、控制器 ivar 等。
  • 嗯。这可能在某些情况下是合适的,但我不确定它是否应该作为一种万能的解决方案呈现。如果每个传入的请求仅导致对 3rd 方 API 的 1 次调用,那么这并不比将该调用包装在 using 语句中更好。在高流量、高并发的场景中,您很可能会遇到他试图避免的过多 DSN 查找和打开套接字的问题。
  • 您的建议与将调用包装在“使用”语句中相同,最终会遇到同样的问题。
【解决方案3】:

从 2.1 开始,一个不错的选择是使用 IHttpClientFactory

Microsoft Docs | Announcement blog post

【讨论】:

    猜你喜欢
    • 2019-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-08
    • 1970-01-01
    • 2018-02-20
    相关资源
    最近更新 更多