c# IHttpClientFactory 和 new HttpClient() 的区别

来源:这里教程网 时间:2026-02-21 17:38:31 作者:

直接 new HttpClient() 为什么会在高并发下崩?

不是它“不能用”,而是频繁

new HttpClient()
会快速耗尽本地端口(
TIME_WAIT
状态堆积),尤其在短生命周期服务(如 ASP.NET Core Controller 每次请求都 new 一个)中,几分钟内就可能触发
SocketException: Only one usage of each socket address is permitted
。根本原因在于:
HttpClient
虽轻量,但底层的
SocketsHttpHandler
是重量级资源——它持有连接池、SSL 会话缓存、DNS 解析结果。每次 new 都新建一套,既不复用,也不释放干净。

DNS 缓存不会刷新:比如你调用的 API 域名做了灰度切流,
new HttpClient()
实例可能永远卡在旧 IP 上
超时、Header、BaseAddress 等配置散落在各处,改一处漏十处 没显式
Dispose()
?.NET Core 会延迟回收,但连接池已泄漏;.NET Framework 下更严重,可能永久占用 socket

IHttpClientFactory 怎么解决这些问题?

IHttpClientFactory
不是“造 HttpClient 的工厂”,而是“管理连接池 + 注入策略 + 统一配置”的协调者。它背后只维护一组共享的
SocketsHttpHandler
实例,所有通过它创建的
HttpClient
都复用同一套底层连接池和 DNS 缓存策略(默认每 2 分钟刷新一次 DNS)。

生命周期由 DI 容器托管:你注入
IHttpClientFactory
,它活到应用结束;你调用
CreateClient("xxx")
得到的
HttpClient
是瞬态对象(可 Dispose,也可不 Dispose——工厂会自动清理)
配置集中化:在
Program.cs
里统一设
BaseAddress
Timeout
DefaultRequestHeaders
天然支持弹性策略:比如集成 Polly,一行代码加重试:
.AddPolicyHandler(Policy.Handle<httprequestexception>().WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(100)))</httprequestexception>

构造函数里该注入 IHttpClientFactory 还是 HttpClient?

必须注入

IHttpClientFactory
,而不是直接注入
HttpClient
。后者看似方便,实则是陷阱:

ASP.NET Core 会把
HttpClient
当作单例注册(如果你没配作用域),导致所有服务共享同一个实例 → 全局超时、Header 冲突、线程不安全(
DefaultRequestHeaders
是共享集合)
无法按业务区分配置:支付服务要 30 秒超时、通知服务只要 5 秒,单例
HttpClient
无法满足
测试困难:你没法在单元测试中 mock 不同行为的客户端

正确姿势是:

public class PaymentService
{
    private readonly IHttpClientFactory _factory;
    public PaymentService(IHttpClientFactory factory) => _factory = factory;
    public async Task ProcessAsync()
    {
        var client = _factory.CreateClient("PaymentApiClient"); // 名称匹配注册时的 key
        await client.PostAsync("/charge", content);
    }
}

命名客户端 vs 类型化客户端,怎么选?

两者本质都是为了解耦配置与使用,区别在于绑定方式:

命名客户端
AddHttpClient("xxx")
):灵活,适合多环境/多租户场景,比如
CreateClient("ProdApi")
CreateClient("StagingApi")
指向不同地址
类型化客户端
AddHttpClient<githubservice>()</githubservice>
):强类型,把客户端逻辑封装进类,构造函数直接接收配置好的
HttpClient
,适合职责单一的服务(如专门调 GitHub API 的类)

注意:类型化客户端内部仍走工厂机制,不是 new 出来的;它的

HttpClient
参数由 DI 自动注入,生命周期受工厂管控——这点常被忽略。

真正容易被忽略的点是:即使用了
IHttpClientFactory
,如果在非 DI 管理的类(比如静态工具类、
BackgroundService
手动 new 的对象)里调用
CreateClient
,依然可能因工厂未被正确释放或作用域错乱引发问题。工厂本身也依赖 DI 容器的生命周期管理,脱离上下文就失效。

相关推荐