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

Azure Service Bus客户端释放异常问题及代码修复咨询

问题分析与解决方案

问题根源

  1. ServiceBusClient被过早释放:原代码在每次发送消息后都调用_client.DisposeAsync(),导致第二次调用EnqueueAsync时,已释放的客户端无法再创建新的ServiceBusSender。
  2. 无任务时资源泄漏:构造函数中创建的ServiceBusClient仅在调用EnqueueAsync时才会进入finally块释放,若从未执行过入队操作,客户端资源将永远无法回收。

核心优化思路

Azure Service Bus官方明确推荐:ServiceBusClient是重量级、线程安全的单例对象,应在整个应用生命周期内复用;ServiceBusSender是轻量级对象,可按需创建并在使用后及时释放。

修改后的代码

public class ServiceBusBackgroundJobManager : BackgroundWorkerBase, IBackgroundJobManager, IAsyncDisposable
{
    private readonly IAppConfigurationAccessor _configurationAccessor;
    private readonly ServiceBusClient _client;
    private readonly string _serviceBusQueueName;

    public ServiceBusBackgroundJobManager(IAppConfigurationAccessor configurationAccessor)
    {
        _configurationAccessor = configurationAccessor;
        var connectionString = _configurationAccessor.Configuration["Abp:ServiceBusConnectionString"];
        _serviceBusQueueName = _configurationAccessor.Configuration["Abp:ServiceBusQueueName"];
        _client = new ServiceBusClient(connectionString);
    }

    public async Task<string> EnqueueAsync<TJob, TArgs>(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal,
        TimeSpan? delay = null) where TJob : IBackgroundJobBase<TArgs>
    {
        var messageBody = JsonSerializer.Serialize(args);
        var message = new ServiceBusMessage(Encoding.UTF8.GetBytes(messageBody))
        {
            MessageId = Guid.NewGuid().ToString(),
            ContentType = "application/json"
        };

        if (delay != null)
        {
            message.ScheduledEnqueueTime = DateTimeOffset.UtcNow.Add(delay.Value);
        }

        // 使用await using自动释放ServiceBusSender,无需手动管理
        await using var sender = _client.CreateSender(_serviceBusQueueName);
        using var messageBatch = await sender.CreateMessageBatchAsync();
        
        if (!messageBatch.TryAddMessage(message))
        {
            throw new Exception("无法将消息添加到批次中,消息大小超过队列限制");
        }

        await sender.SendMessagesAsync(messageBatch);

        return message.MessageId; // 返回实际消息ID,便于后续追踪
    }

    // 实现异步释放接口,在类生命周期结束时回收ServiceBusClient
    public async ValueTask DisposeAsync()
    {
        await _client.DisposeAsync();
        // 调用基类的销毁逻辑
        if (IsDisposed) return;
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

关键修改点

  • 移除全局_sender字段:每次发送时创建局部ServiceBusSender,通过await using自动释放,避免资源持有。
  • 删除finally中的_client.DisposeAsync():保留ServiceBusClient的单例复用,不再每次发送后销毁。
  • 实现IAsyncDisposable接口:在类被容器销毁或应用关闭时,自动释放ServiceBusClient,解决无任务时的资源泄漏问题。
  • 优化返回值与异常信息:返回实际消息ID替代空字符串,异常信息更具针对性,便于问题排查。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:13:16