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

Azure Functions中ActivityFunction内HttpClient挂起致超时问题求助

问题:Azure Functions ActivityFunction处理大量请求后挂起超时

在Azure Functions的ActivityFunction中循环调用外部API,处理200-500个负载后出现挂起,最终导致ActivityFunction超时(已设置最长运行10分钟)。使用IHttpClientFactory创建客户端,尝试过命名HttpClient、设置Timeout属性均无效。

可能原因及解决方案

1. TCP连接池耗尽(最常见原因)

虽然IHttpClientFactory会管理连接池,但每次创建客户端后重复配置BaseAddress和Authorization头,且同步循环调用可能导致大量TCP连接处于TIME_WAIT状态,耗尽可用连接。

解决办法:

  • 预配置命名HttpClient,复用客户端实例,避免重复创建和配置:
    在Program.cs中注册命名客户端:

    builder.Services.AddHttpClient("ApiClient", client =>
    {
        client.BaseAddress = new Uri("http://api.test.com");
    })
    .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
    {
        // 调整单服务器最大连接数,默认100,根据需求修改
        MaxConnectionsPerServer = 500,
        UseCookies = false,
        AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
    });
    
  • 在服务中注入预配置的客户端,不要修改复用客户端的默认请求头(多线程场景下线程不安全),而是在每个请求中单独设置Authorization:

    public class InjectedService(IHttpClientFactory httpClientFactory)
    {
        private readonly HttpClient _apiClient = httpClientFactory.CreateClient("ApiClient");
    
        public async Task Action(Payload payload, string jwtToken, CancellationToken cancellationToken)
        {
            var request = new HttpRequestMessage(HttpMethod.Post, "api/abc/action");
            // 为每个请求单独设置认证头
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
            request.Content = JsonContent.Create(payload);
    
            using var response = await _apiClient.SendAsync(request, cancellationToken);
            // 可选:确保请求成功,根据业务需求处理错误
            response.EnsureSuccessStatusCode();
        }
    }
    

2. 同步循环导致请求积压

同步foreach逐个处理请求,总耗时过长,加上连接池限制容易导致挂起。如果目标API支持并行处理,可以控制并发数批量处理:

[Function(nameof(ActivityFunction))]
public async Task Run([ActivityTrigger] ActivityFunctionRequest request, CancellationToken cancellationToken)
{
    string jwt = "...";
    var payloads = new List<Payload>();

    // 控制并发数,避免压垮目标API和耗尽本地资源
    var semaphore = new SemaphoreSlim(50);
    var taskList = payloads.Select(async payload =>
    {
        await semaphore.WaitAsync(cancellationToken);
        try
        {
            using var requestCts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
            using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCts.Token, cancellationToken);
            await injectedService.Action(payload, jwt, linkedCts.Token);
        }
        finally
        {
            semaphore.Release();
        }
    });

    await Task.WhenAll(taskList);
}

3. 目标API限流或响应缓慢

检查目标API是否有请求频率、并发数限制,或者响应缓慢导致请求排队。可以通过添加日志排查具体是哪个请求卡住:

public async Task Action(Payload payload, string jwtToken, CancellationToken cancellationToken)
{
    var payloadId = payload.Id; // 假设Payload有唯一标识
    try
    {
        // 日志:开始处理Payload {payloadId}
        var request = new HttpRequestMessage(HttpMethod.Post, "api/abc/action");
        // ... 请求配置
        using var response = await _apiClient.SendAsync(request, cancellationToken);
        // 日志:Payload {payloadId}处理完成,状态码:{response.StatusCode}
        response.EnsureSuccessStatusCode();
    }
    catch (Exception ex)
    {
        // 日志:Payload {payloadId}处理失败,异常:{ex.Message}
        throw;
    }
}

4. 取消令牌未正确关联

每个请求单独创建CancellationTokenSource,但未关联ActivityFunction的原生取消令牌,可能导致ActivityFunction超时后请求仍在继续。建议将请求的取消令牌与Activity的令牌关联:

using var requestCts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCts.Token, cancellationToken);
await injectedService.Action(payload, jwt, linkedCts.Token);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 12:44:54