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

AKS中ASP.NET Web API端点挂起但CPU内存未过载的原因排查

问题背景与故障现象

我有一个ASP.NET Web API解决方案,包含用于检索和过滤记录的简单端点。为卸载数据处理工作,添加了BackgroundService在后台持续处理记录,本地运行正常,但部署到AKS后存活/就绪检查失败、服务端点挂起。计划单独部署数据处理服务,但想搞清楚故障原因:

  • 猜测是线程过多,但K9S显示CPU、内存均未过载,是否存在线程过多但资源未过载的情况?
  • 或是其他潜在问题?

测试场景:创建50个Service Bus客户端及处理器对应50个队列,通过测试程序发送大量消息压测时出现问题。

后台服务核心代码
// 初始化处理器部分
{
    _services = services;
    _auditWriter = auditWriter;
    foreach (var queueName in QueueConfiguration.QueueNames)
    {
        var serviceBusClient = new ServiceBusClient("<connectionString>");
        _clients.Add(serviceBusClient);
        var processorOptions = new ServiceBusProcessorOptions
        {
            MaxConcurrentCalls = 20,
            PrefetchCount = 20,
            ReceiveMode = ServiceBusReceiveMode.ReceiveAndDelete,
        };
        var processor = serviceBusClient.CreateProcessor(queueName, processorOptions);
        processor.ProcessMessageAsync += MessageHandler;
        processor.ProcessErrorAsync += ErrorHandler;
        processor.StartProcessingAsync();
        _queueProcessors.Add(processor);
    }
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    // Through trial and error you need to await in the ExecuteAsync
    await Task.Delay(1, stoppingToken);
    stoppingToken.Register(async () =>
    {
        foreach (var processor in _queueProcessors)
        {
            await processor.DisposeAsync();
        }
    });
    try
    {
        await Task.Delay(Timeout.Infinite, stoppingToken);
    }
    catch (OperationCanceledException)
    {
        // Expected; no action needed
    }
    return;
}


private static async Task MessageHandler(ProcessMessageEventArgs args)
{
    try
    {
        var data = buildData(); // business logic
        try
        {
            using (var sqlConnection = new SqlConnection(_sqlConnectionString)) //removed connection string for post
            {
                _auditWriter.WriteAuditData(data, sqlConnection);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("There was an exception while saving the data to the database");
        }
        
        using (var scope = _services.CreateScope())
        {
            // use scoped variable to write files to data store
        }

    }
    catch (Exception e)
    {
        Console.WriteLine($"Issue processing message for queue {args.EntityPath} | {e}");
    }
}
Pod资源使用情况

就绪检查失败后,Pod的CPU使用率极低,内存仅占用34%:
Pod资源使用情况截图

故障原因分析与解决方案

1. 线程池耗尽(线程过多但资源未过载的可能)

这种情况确实存在:

  • 每个ServiceBusProcessor配置MaxConcurrentCalls=20,50个队列对应1000个并发消息处理任务。
  • 如果buildData()、数据库操作或文件存储是同步阻塞调用(比如WriteAuditData是同步方法,或SqlConnection操作未用异步API),会占用线程池线程且无法释放。线程池被占满后,ASP.NET Core的请求处理(包括存活/就绪检查)因拿不到线程挂起,此时CPU因大量线程处于阻塞等待状态(如等数据库响应、IO)而使用率不高。

2. ServiceBusClient实例滥用

代码中为每个队列创建独立的ServiceBusClient是反模式:

  • ServiceBusClient是线程安全、可复用的重量级对象,推荐全局单例使用。创建50个实例会导致过多TCP连接、资源句柄占用,触发操作系统句柄限制,使进程无法创建新资源(如HTTP请求Socket),进而导致就绪检查失败。

3. 资源泄漏与优雅终止问题

  • stoppingToken.Register中使用异步委托错误:Register不等待异步操作完成,处理器的DisposeAsync可能在进程终止时未完成,导致资源泄漏。
  • MessageHandler中若CreateScope后的操作存在未捕获异常或同步阻塞,也会持续占用线程池线程。

修复建议

  • 复用ServiceBusClient:全局仅创建一个ServiceBusClient实例,所有处理器共享。
  • 全异步化IO操作:检查buildData()、WriteAuditData、文件存储操作,全部使用异步API并await,避免阻塞线程池线程。
  • 降低并发数:将MaxConcurrentCalls调低(比如从20降到5),减少总并发线程数,避免线程池耗尽。
  • 修复优雅终止逻辑:在ExecuteAsync中等待处理器停止,替代Register:
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            await Task.Delay(Timeout.Infinite, stoppingToken);
        }
        catch (OperationCanceledException)
        {
            // 等待所有处理器停止
            var stopTasks = _queueProcessors.Select(p => p.StopProcessingAsync());
            await Task.WhenAll(stopTasks);
            // 释放资源
            foreach (var processor in _queueProcessors)
            {
                await processor.DisposeAsync();
            }
            foreach (var client in _clients)
            {
                await client.DisposeAsync();
            }
        }
    }
    
  • 检查数据库连接池:确保SqlConnection使用正确异步API,且连接池配置合理(如最大连接数),避免数据库连接耗尽导致线程阻塞。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 11:02:04