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

如何在不违反开闭原则的前提下将Azure服务总线从Topic切换到Queue?

符合开闭原则的Azure ServiceBus Topic转Queue最佳实践

开闭原则核心是对扩展开放,对修改关闭——也就是说不能改动你现有的AzureServiceBusSender代码,而是通过扩展的方式支持Queue对接,下面是几个公认的标准实现方法:

1. 基于现有抽象扩展Queue专属实现

你已经定义了IServiceBusSender接口,直接新建一个AzureQueueSender类实现该接口即可,完全不碰原有的Topic发送代码。

示例代码:

public class AzureQueueSender : IServiceBusSender
{
    private ServiceBusClient _client;
    private ServiceBusSender _sender;

    // 构造函数接收队列名称而非主题名称
    public AzureQueueSender(string connectionString, string queueName)
    {
        _client = new ServiceBusClient(connectionString);
        _sender = _client.CreateSender(queueName);
    }

    public async Task DisposeAsync()
    {
        await _sender.DisposeAsync();
        await _client.DisposeAsync();
    }

    public async Task SendMessageAsync(CustomMessage message, string action)
    {
        string json = JsonConvert.SerializeObject(message);

        var queueMessage = new ServiceBusMessage(json)
        {
            ContentType = "application/json"
        };
        queueMessage.Settings.Add("Action", action);

        await _sender.SendMessageAsync(queueMessage);
    }
}

这种方式完全遵循开闭原则:原有Topic发送逻辑不受任何影响,新增Queue支持只需要加新类,业务代码依赖IServiceBusSender接口即可,无需关心底层是Topic还是Queue。

2. 配置驱动+依赖注入实现无缝切换

结合.NET的依赖注入容器,通过配置文件来决定注册哪个实现,不需要修改业务代码就能在Topic和Queue之间切换。

比如在appsettings.json里加配置:

{
  "ServiceBusSettings": {
    "ConnectionString": "your-connection-string",
    "TargetName": "your-queue-or-topic-name",
    "Type": "Queue" // 可选值:Queue/Topic
  }
}

然后在Program.cs/Startup.cs里根据配置注册:

var serviceBusSettings = configuration.GetSection("ServiceBusSettings").Get<ServiceBusSettings>();

if (serviceBusSettings.Type.Equals("Queue", StringComparison.OrdinalIgnoreCase))
{
    services.AddScoped<IServiceBusSender>(sp => 
        new AzureQueueSender(serviceBusSettings.ConnectionString, serviceBusSettings.TargetName));
}
else
{
    services.AddScoped<IServiceBusSender>(sp => 
        new AzureServiceBusSender(serviceBusSettings.ConnectionString, serviceBusSettings.TargetName));
}

业务代码只需要注入IServiceBusSender即可,切换时只需要修改配置文件,完全符合开闭原则。

3. 提取公共逻辑到抽象基类(优化重复代码)

如果Topic和Queue的发送逻辑有大量重复(比如序列化、资源释放),可以把公共代码抽离到抽象基类,让两个实现类继承基类,只实现差异部分,减少代码冗余。

示例基类:

public abstract class ServiceBusSenderBase : IServiceBusSender
{
    protected ServiceBusClient _client;
    protected ServiceBusSender _sender;

    public abstract Task DisposeAsync();

    public async Task SendMessageAsync(CustomMessage message, string action)
    {
        string json = JsonConvert.SerializeObject(message);

        var serviceBusMessage = new ServiceBusMessage(json)
        {
            ContentType = "application/json"
        };
        serviceBusMessage.Settings.Add("Action", action);

        await _sender.SendMessageAsync(serviceBusMessage);
    }
}

然后修改原Topic实现和新增Queue实现:

// 原Topic实现,只保留差异逻辑
public class AzureServiceBusSender : ServiceBusSenderBase
{
    public AzureServiceBusSender(string connectionString, string topicName)
    {
        _client = new ServiceBusClient(connectionString);
        _sender = _client.CreateSender(topicName);
    }

    public override async Task DisposeAsync()
    {
        await _sender.DisposeAsync();
        await _client.DisposeAsync();
    }
}

// Queue实现
public class AzureQueueSender : ServiceBusSenderBase
{
    public AzureQueueSender(string connectionString, string queueName)
    {
        _client = new ServiceBusClient(connectionString);
        _sender = _client.CreateSender(queueName);
    }

    public override async Task DisposeAsync()
    {
        await _sender.DisposeAsync();
        await _client.DisposeAsync();
    }
}

这种方式既保持了开闭原则(原代码只做了继承调整,没有修改核心逻辑),又解决了代码重复问题,是更优雅的实现方式。

内容的提问来源于stack exchange,提问作者Andrеw

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 23:17:46