You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Polly调用ExecuteAsync时,HttpClient实例应创建在委托内还是外?

结论先行

你当前的Post写法逻辑能跑通但有性能隐患,Get写法存在致命逻辑bug会导致重试失效,两种写法都有优化空间。


现有写法的问题

1. Get写法的致命bug

你的重试策略触发时,会先执行Authenticate()更新全局_loginToken,但因为HttpClient实例创建在委托外部,第一次请求时已经给client.DefaultRequestHeaders添加了旧的Token。而你的UpdateClientHeader逻辑是仅当Headers中不存在Token时才添加,重试时不会替换成新Token,第二次请求依然携带旧Token,必然再次返回401,重试完全失效。
同时直接修改HttpClient.DefaultRequestHeaders不是线程安全操作,多线程复用同一个客户端时会出现请求头错乱的问题。

2. Post写法的性能缺陷

虽然每次重试都会新建HttpClient实例,能拿到最新的Token,逻辑可以跑通,但频繁创建销毁HttpClient会导致底层socket资源无法及时复用,大量连接进入TIME_WAIT状态,高并发场景下会出现socket耗尽、请求失败、吞吐量下降的问题,属于典型的HttpClient错误用法。


最优解决方案

HttpClient本身设计为可全局复用的单例实例,不需要每次请求新建,结合你的业务场景改造方式如下:

第一步:全局复用HttpClient实例

提前创建静态HttpClient实例全链路复用,避免重复实例化的开销:

// 全局复用,仅初始化一次
private static readonly HttpClient _sharedClient = new HttpClient(new NativeMessageHandler()) 
{ 
    Timeout = TimeSpan.FromSeconds(TimeOutSec) 
};

第二步:请求头绑定到单次请求而非客户端

不要修改HttpClient.DefaultRequestHeaders,改为把Token、correlationId等头信息绑定到单次HttpRequestMessage上,既没有线程安全问题,重试时也会自动读取最新的_loginToken:

// Get请求示例,Post仅需修改HttpMethod和赋值Content即可
var httpResponse = await AuthenticationOnUnauthorizePolicy.ExecuteAsync(async () =>
{
    using var request = new HttpRequestMessage(HttpMethod.Get, url);
    // 给当前请求单独加头,不影响全局客户端
    if (_loginToken != null)
    {
        request.Headers.TryAddWithoutValidation("Token", _loginToken);
    }
    request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(HttpConsts.JsonMediaType));
    request.Headers.TryAddWithoutValidation("CorrelationId", correlationId);
    
    WriteNetworkAccessStatusToLog();
    return await _sharedClient.SendAsync(request, token);
});

额外优化点

你的Authenticate方法中单独创建的HttpClient也可以替换为上述全局复用的实例,进一步减少资源消耗。


关于Polly Context的疑问

你当前的场景不需要使用Context传递客户端实例,直接复用全局HttpClient即可,现有策略逻辑不需要修改即可正常工作。

内容的提问来源于stack exchange,提问作者o_w

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 19:24:04