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

Azure Service Bus发送消息偶发异常:已释放对象/连接关闭无法创建会话

解决Azure Service Bus消息发送偶发失败的问题

你的偶发报错核心是静态QueueClient的不当复用+频繁创建销毁连接导致的并发冲突,结合代码细节我来拆解问题并给出修复方案:

问题根源分析

  1. 静态QueueClient的并发冲突
    你的queueClient是静态变量,但每次调用SendQueueMessage都会重新实例化它,还在方法末尾执行CloseAsync()。当数千个异步任务同时跑时,多个线程会同时修改这个静态变量:

    • 线程A刚创建queueClient开始发消息,线程B就把queueClient重新赋值成新实例
    • 线程A执行CloseAsync()时,关闭的可能已经是线程B刚创建的实例,反过来线程B的发送操作就会访问一个已被关闭/释放的对象,触发Cannot access a disposed object或连接关闭相关错误
  2. QueueClient生命周期误用
    Azure Service Bus的QueueClient是设计为可复用的长连接对象,频繁创建销毁会导致连接池不稳定,既浪费资源又增加连接类错误的概率。

修复方案

1. 改为单例模式复用QueueClient

把QueueClient改成全局单例,仅在应用启动时初始化一次,应用关闭时再统一关闭,避免每次发送都创建/销毁连接。

2. 修复后的代码示例

public class ServiceBusService
{
    // 用懒加载实现线程安全的单例初始化
    private static readonly Lazy<IQueueClient> _lazyQueueClient = new Lazy<IQueueClient>(() =>
        new QueueClient(AzureUtils.SERVICE_BUS_CONNECTIONSTRING, "eventqueue"));
    
    private static IQueueClient QueueClient => _lazyQueueClient.Value;

    public async Task SendQueueMessage(JObject jObject, DateTime? scheduledEnqueueTimeUtc = null)
    {
        string scheduledTime = scheduledEnqueueTimeUtc?.ToLongTimeString() ?? "";
        try
        {
            var message = new Message(Encoding.UTF8.GetBytes(jObject.ToString()));
            if (scheduledEnqueueTimeUtc.HasValue)
            {
                message.ScheduledEnqueueTimeUtc = scheduledEnqueueTimeUtc.Value;
            }
            // 直接复用单例QueueClient,无需每次创建和关闭
            await QueueClient.SendAsync(message);
        }
        catch (Exception e)
        {
            Trace.TraceError($"{Tag()} {e.InnerException?.Message ?? ""} {e.Message}");
        }
    }

    // 新增:在应用关闭时调用此方法统一关闭QueueClient
    public static async Task CloseQueueClientAsync()
    {
        if (_lazyQueueClient.IsValueCreated)
        {
            await QueueClient.CloseAsync();
        }
    }

    private string Tag() => "[ServiceBusService]";
}

3. 优化异步任务调用

原来的Task.Run(() => new ServiceBusService().SendQueueMessage(busMessageObject))可以简化为直接调用异步方法(上下文允许的话),或者正确处理后台任务的异常:

// 若在异步上下文,直接await调用
await new ServiceBusService().SendQueueMessage(busMessageObject);

// 若需后台执行,建议捕获异常避免静默失败
_ = Task.Run(async () => 
{
    try
    {
        await new ServiceBusService().SendQueueMessage(busMessageObject);
    }
    catch (Exception ex)
    {
        Trace.TraceError($"后台发送消息失败: {ex.Message}");
    }
});

额外建议

  • 用批量发送:如果要发数千条消息,用SendBatchAsync批量发送能大幅提升性能,减少连接开销
  • 配置重试策略:Service Bus SDK内置重试机制,可配置重试策略处理临时连接错误,提升可靠性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 06:35:50