Azure Service Bus队列发送代码内存泄漏及实例化问题咨询
回答
你的代码不会直接引发内存泄漏,但存在严重的性能缺陷和资源浪费问题,ServiceBusClient的实例化逻辑确实应该放到类构造函数中做复用。
现有代码的问题说明
- 内存泄漏层面:你在方法中用
await using声明ServiceBusClient,方法执行完成后会按逻辑释放该对象持有的所有资源,不会出现资源无法回收导致的内存泄漏。 - 性能层面:
ServiceBusClient是官方明确建议单例复用的重量级对象,它内部维护了AMQP连接池、TCP长连接、链路鉴权缓存等核心资源,每次发消息都新建、释放Client的开销极高,高并发场景下会直接导致本地临时端口耗尽、服务端连接数超限、消息发送延迟飙升甚至请求超时,属于典型的反模式实现。 - 代码逻辑层面:你在Startup中注册了
IAzureQueueService的瞬态服务,但业务代码完全没有走DI容器,而是手动new AzureQueueService(config)实例化对象,之前的服务注册代码完全不生效,还额外增加了维护成本;另外类中定义的静态azureServiceBusString字段没有实际作用,静态字段会被所有类实例共享,每次构造函数重复赋值属于冗余逻辑。
推荐实现方案
- 调整
AzureQueueService实现,将ServiceBusClient放到构造函数初始化,同时缓存不同队列对应的ServiceBusSender(Sender同样是线程安全的可复用对象),并实现异步释放接口:
public class AzureQueueService : IAzureQueueService, IAsyncDisposable { private readonly ServiceBusClient _serviceBusClient; private readonly ConcurrentDictionary<string, ServiceBusSender> _senderCache = new(); public AzureQueueService(IConfiguration config) { // 从配置读取连接字符串,不要硬编码 var connString = config.GetConnectionString("AzureServiceBus"); _serviceBusClient = new ServiceBusClient(connString); } public async Task SendMessageAsync<T>(T serviceBusMessage, string queueName) { var sender = _senderCache.GetOrAdd(queueName, qName => _serviceBusClient.CreateSender(qName)); string messageBody = JsonSerializer.Serialize(serviceBusMessage); var message = new ServiceBusMessage(Encoding.UTF8.GetBytes(messageBody)); await sender.SendMessageAsync(message); } public async ValueTask DisposeAsync() { foreach (var sender in _senderCache.Values) { await sender.DisposeAsync(); } await _serviceBusClient.DisposeAsync(); } }
- 调整服务注册生命周期,将服务注册为单例,和
ServiceBusClient的复用要求匹配:
services.AddSingleton<IAzureQueueService, Core.Services.AzureQueueService>();
- 业务代码中不要手动实例化
AzureQueueService,直接通过DI注入IAzureQueueService实例调用方法即可。
注:这是Azure Service Bus官方推荐的标准用法,复用Client和Sender可以把消息发送的吞吐量提升数倍,同时避免连接相关的故障。
内容的提问来源于stack exchange,提问作者mohsinali1317
相关产品推荐
相关产品推荐

