应使用 ihttpclientfactory 替代手写 new httpclient(),避免 socketexception 和 time_wait 问题;推荐类型化客户端封装业务逻辑,通过 di 注入预配置 httpclient;连接池、dns、ssl 配置须通过 configureprimaryhttpmessagehandler 设置。

别再手写 new HttpClient() 了——这会直接触发 SocketException: Only one usage of each socket address,尤其在高并发或云环境滚动发布时。根本问题不是“忘了 Dispose”,而是每次新建都生成独立的 SocketsHttpHandler,导致连接池、DNS 缓存、SSL 会话全部失效,端口迅速卡死在 TIME_WAIT 状态。
命名客户端:多后端、不同 BaseAddress 场景怎么配
适合调用多个外部服务(如 GitHub API、支付网关、内部风控服务),每个服务地址、超时、默认头都不同。
- 注册时用字符串名区分:
services.AddHttpClient("payment", c => { c.BaseAddress = new Uri("https://api.pay.example/"); c.Timeout = TimeSpan.FromSeconds(10); }) - 使用时通过
IHttpClientFactory按名取用:_factory.CreateClient("payment"),每次返回新HttpClient实例,但底层HttpMessageHandler很可能复用 - 别写魔法字符串:定义
const string PaymentClientName = "payment";统一管理 - 高频循环里别反复调用
CreateClient("xxx")——虽安全,但对象创建无谓开销;应提取为局部变量或字段复用
类型化客户端:封装业务逻辑、单元测试友好的写法
这是推荐首选,把 HTTP 调用逻辑封装成普通类,构造函数直接注入 HttpClient,由 DI 容器供应预配置实例。
- 定义类时不要管生命周期:
public class WeatherService { private readonly HttpClient _client; public WeatherService(HttpClient client) { _client = client; _client.BaseAddress = new Uri("https://api.weather.com/"); } } - 注册只需一行:
services.AddHttpClient<weatherservice>()</weatherservice>,自动绑定默认 handler 和策略 - 使用时直接注入:
public MyController(WeatherService weather),完全解耦 HTTP 细节 - 切记:不要在类型化客户端里
Dispose()_client,也不该缓存它;它的生命周期由 DI 控制
ConfigurePrimaryHttpMessageHandler:改底层连接池和 DNS 行为的唯一正途
所有影响连接复用、DNS 刷新、SSL 会话的配置,必须落在 HttpMessageHandler 层,而不是对 HttpClient 实例赋值——因为实例随时被丢弃,改了也白改。
- 超时不能写
client.Timeout = ...,而应在AddHttpClient的 lambda 里用ConfigureHttpClient配置 - 连接池参数必须用
ConfigurePrimaryHttpMessageHandler:services.AddHttpClient<paymentservice>().ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { DnsRefreshTimeout = TimeSpan.FromMinutes(2), PooledConnectionLifetime = TimeSpan.FromMinutes(5), PooledConnectionIdleTimeout = TimeSpan.FromMinutes(4), AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate });</paymentservice> - 注意:命名客户端和类型化客户端共用同一 handler 池,但它们的配置是隔离的;别混用注册方式导致预期外的行为
- 如果需要 Cookie 支持,慎用
IHttpClientFactory——HttpMessageHandler复用会导致CookieContainer共享和意外泄露
最易被忽略的是 DnsRefreshTimeout 和 PooledConnectionIdleTimeout 的配合:前者决定 DNS 变更多久生效,后者决定空闲连接何时回收;若后者长于 LB 的 idle timeout,连接会被后端静默断开,下次复用时直接失败。











