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

使用AddAzureClients与ClientFactory实现Azure ServiceBus依赖注入是否优于单例模式?

Azure ServiceBus 两种服务实现方案对比

我有两种可正常运行的Web应用与Azure ServiceBus交互的服务类实现方案,想对比二者的优势,以及哪种更符合最佳实践。以下是两种方案的代码示例:

方案一:标准单例服务类

这种方案在类内部实例化并释放ServiceBus Client和Sender,启动时通过.NET DI注册为单例。

启动代码

services.AddSingleton<IAIMarkingMessageService>(s => new AIMarkingMessageServiceSingleton(aiMarkingSettings.ServiceBusNamespace, aiMarkingSettings.InitQueueName, aiMarkingSettings.FeedbackQueueName, siteSettings.DefaultManagedIdentityClientId));

服务类代码(简化版)

public class AIMarkingMessageServiceSingleton : IAIMarkingMessageService, IAsyncDisposable
{
    readonly ServiceBusClient _client;
    readonly ServiceBusSender _initSender;
    readonly ServiceBusSender _feedbackSender;

    public AIMarkingMessageServiceSingleton(string serviceBusNameSpace, string initQueueName, string feedbackQueueName, string userAssignedClientId)
    {
        var clientOptions = new ServiceBusClientOptions { TransportType = ServiceBusTransportType.AmqpWebSockets };
        _client = new ServiceBusClient($"{serviceBusNameSpace}.servicebus.windows.net", new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId }), clientOptions);
        _initSender = _client.CreateSender(initQueueName);
        _feedbackSender = _client.CreateSender(feedbackQueueName);
    }

    public async Task<string> SendInitSubmissionAsync(long taskId, string requestUID)
    { 
       ...
    }

    public async Task<List<long>> SendInitSubmissionBatchAsync(IEnumerable<(long taskId, string requestUID)> tasksToInit)
    {
         ...
    }

    public async ValueTask DisposeAsync()
    {         
        await _client.DisposeAsync(); // 此调用会同时释放由该客户端创建的发送者和接收者
        GC.SuppressFinalize(this);
    }
}

方案二:使用AddAzureClients DI方法

这种方案在启动代码中配置ServiceBus Client和Sender,服务类通过注入IAzureClientFactory触发Sender的延迟实例化,服务类可按需注册为作用域或单例,是微软最新文档推荐的实现方式。

启动代码

services.AddAzureClients(clientBuilder =>
{
    // 注册客户端
    clientBuilder.AddServiceBusClientWithNamespace($"{aiMarkingSettings.ServiceBusNamespace}.servicebus.windows.net")
                 .ConfigureOptions(x => new ServiceBusClientOptions { TransportType = ServiceBusTransportType.AmqpTcp });

    clientBuilder.UseCredential(new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = siteSettings.DefaultManagedIdentityClientId }));

    // 注册子客户端(队列发送者)
    // 初始化发送者
    clientBuilder.AddClient<ServiceBusSender, ServiceBusClientOptions>(
        (_, _, provider) => provider.GetService<ServiceBusClient>()
        .CreateSender(aiMarkingSettings.InitQueueName)).WithName(aiMarkingSettings.InitQueueName);
    // 反馈发送者
    clientBuilder.AddClient<ServiceBusSender, ServiceBusClientOptions>(
        (_, _, provider) => provider.GetService<ServiceBusClient>()
        .CreateSender(aiMarkingSettings.FeedbackQueueName)).WithName(aiMarkingSettings.FeedbackQueueName);
});

// 可注册为作用域或单例
services.AddScoped<IAIMarkingMessageService, AIMarkingMessageService>();

服务类代码(简化版)

public class AIMarkingMessageService : IAIMarkingMessageService
{
    readonly ServiceBusSender _initSender;
    readonly ServiceBusSender _feedbackSender;

    public AIMarkingMessageService(IAzureClientFactory<ServiceBusSender> senderFactory, IOptions<AIMarkingSettings> config )
    {
        var _config = config.Value;
        _initSender = senderFactory.CreateClient(_config.InitQueueName);
        _feedbackSender = senderFactory.CreateClient(_config.FeedbackQueueName);
    }
    
    public async Task<string> SendInitSubmissionAsync(long taskId, string requestUID)
    { 
       ...
    }

    public async Task<List<long>> SendInitSubmissionBatchAsync(IEnumerable<(long taskId, string requestUID)> tasksToInit)
    {
         ...
    }
}

两种方案的优势对比

方案一(标准单例)的优势

  • 封装性强:ServiceBus客户端的创建、配置、释放逻辑都封装在服务类内部,外部只需依赖IAIMarkingMessageService接口,业务逻辑与ServiceBus实现细节解耦。
  • 实现简单:无需引入额外的Azure ClientFactory依赖包,代码结构直观,上手快。
  • 资源管控直接:自己掌控ServiceBusClient的生命周期,明确资源释放时机,适合对资源管控有明确需求的场景。

方案二(AddAzureClients)的优势

  • 官方推荐:遵循微软最新的Azure SDK最佳实践,后续版本兼容性、官方支持更有保障。
  • 配置集中:所有ServiceBus相关的配置都集中在启动代码中,修改配置无需改动服务类,便于统一管理。
  • 延迟实例化:Sender在实际需要时才创建,避免应用启动时初始化不必要的资源,提升启动速度。
  • 服务类灵活:服务类可自由注册为作用域或单例,依赖注入更符合.NET生态的规范,无需自己处理单例的异步释放逻辑(ClientFactory会自动管理客户端生命周期)。
  • 资源复用高效:ServiceBusClient由ClientFactory统一管理,可被多个Sender/Receiver复用,避免重复创建客户端导致的连接资源浪费。

最佳实践建议

第二种方案(AddAzureClients)更符合当前的最佳实践。它不仅遵循官方推荐的实现方式,还在配置管理、资源复用、依赖注入灵活性上有明显优势,后续维护和扩展也更方便。

如果现有第一种方案运行稳定且没有维护痛点,也可以继续使用;但新开发的项目或需要重构的场景,建议优先采用第二种方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 12:54:51