HttpClient的正确使用方式

.NET开发中,HttpClient是一个常用的类,用于发送HTTP请求。然而,许多开发者在使用HttpClient时存在误解,常常在每次请求时创建新的实例,这不仅效率低下,还可能导致连接池耗尽。本文将探讨HttpClient的正确使用方式,以帮助开发者避免常见的错误,并提高应用程序的性能。

HttpClient应该重用

HttpClient设计为可重用。应该在整个应用程序的生命周期中使用单个HttpClient实例,或者至少在请求签名变化时尽可能少地使用。这样做的主要原因是,HttpClient的每个实例都会打开一个新的套接字连接,而在高流量网站上,可能会耗尽可用的连接池,并收到System.Net.Sockets.SocketException异常。此外,通过重用实例,可以避免建立新的TCP连接的显著开销,因为HttpClient可以以线程安全的方式重用其现有连接。

创建静态HttpClient实例

在控制台或桌面应用程序中,可以在任何地方公开这个实例。在ASP.NETWebAPI应用程序中,可以在Global.asax.cs文件中创建它。以下是设置HttpClient实例的示例:

public class WebApiApplication : System.Web.HttpApplication { internal static HttpClient httpClientInstance; protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); Uri baseUri = new Uri("https://someresource.com"); httpClientInstance = new HttpClient(); httpClientInstance.BaseAddress = baseUri; httpClientInstance.DefaultRequestHeaders.Clear(); httpClientInstance.DefaultRequestHeaders.ConnectionClose = false; httpClientInstance.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); ServicePointManager.FindServicePoint(baseUri).ConnectionLeaseTimeout = 60 * 1000; } }

在这个示例中,一次性设置了客户端。所有的使用都将:

  • 使用相同的基础地址。可以在不出现问题的情况下附加不同的路由(例如/api/somecontroller, /api/someothercontroller)。
  • 将请求content-type设置为application/json。
  • 尝试保持连接打开(.ConnectionClose = false),这使得客户端的使用更加高效。

处理多个配置

虽然HttpClient是线程安全的,但它并不是所有的属性都是线程安全的。如果在每次调用之前更改URL和/或标头,可能会导致一些非常难以识别的错误。如果这种配置会变化,那么不能使用第一个配方。如果有一个可管理的配置数量,为每个配置创建一个HttpClient实例。将比单实例稍微低效一些,但比每次都创建和处置要高效得多。

使用HttpRequestMessage

在这种情况下,可以为所有HTTP调用创建单独的客户端实例,这可能会变得难以管理。仍然可以通过变化HttpRequestMessage而不是HttpClient来获得单实例的所有优势。以下是新的Global.asax.cs文件的示例:

public class WebApiApplication : System.Web.HttpApplication { internal static HttpClient httpClientInstance; protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); httpClientInstance = new HttpClient(); httpClientInstance.DefaultRequestHeaders.ConnectionClose = false; ServicePointManager.FindServicePoint("some uri").ConnectionLeaseTimeout = 60 * 1000; ServicePointManager.FindServicePoint("some other uri").ConnectionLeaseTimeout = 60 * 1000; ServicePointManager.FindServicePoint("some other other uri").ConnectionLeaseTimeout = 60 * 1000; // etc.... } }

在这个示例中,已经消除了内容类型和基础URI,因为在客户端使用中,这将经常变化。此外,已经为所有知道将要使用的URI配置了ConnectionLeaseTimeout。如果在设计时不知道所有URI,那么在运行时这样做是可以的——至少在运行时这样做比根本不做要好。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485