集成测试场景下消息未发送至Azure Service Bus问题咨询
问题排查与解决方案
依赖包冲突
你同时引入了两套Azure Service Bus SDK:Azure.Messaging.ServiceBus是微软官方推荐的新一代SDK,Microsoft.Azure.ServiceBus是已经停止维护的旧版本SDK,二者共存会出现类型冲突、内部逻辑互斥的问题,是消息静默发送失败的高发原因。请先卸载Microsoft.Azure.ServiceBus包,只保留新版本SDK。连接字符串赋值逻辑错误
构造函数中连接字符串的赋值逻辑写反:
// 原错误写法 _connectionString ??= connectionString;
??=运算符的逻辑是只有左边变量为null时,才会将右边的值赋值给左边。你优先将连接字符串赋值为环境变量的值,只有当环境变量不存在时,才会使用构造函数传入的connectionString参数。如果你的测试环境中存在该环境变量,你传入的WriterConnectionString会被直接忽略,消息可能被发送到了其他队列/命名空间,导致你以为发送失败。
正确写法应该是:
_connectionString = connectionString ?? Environment.GetEnvironmentVariable("SERVICE_BUS_CONNECTION_STRING_SENDER");
优先使用传入的连接字符串,为空时才 fallback 到环境变量。
- 同步阻塞调用存在死锁风险
SendMessageAsync(...).GetAwaiter().GetResult()这种同步阻塞异步方法的写法,在带有同步上下文的环境中(比如NUnit测试框架、UI线程)很容易触发死锁,死锁时会出现方法卡住无响应、也不抛异常的现象。
建议将发送方法改为异步,测试方法也改为异步:
// 发送端修改 public async Task SendMessageAsync(T message) { var serviceBusMessage = _messageSerializer.Serialize(message); await _sender.SendMessageAsync(serviceBusMessage).ConfigureAwait(false); } // 测试方法修改 [Test] public async Task GoodTypes() { // ... 省略其他逻辑 await _messagingSender.SendMessageAsync(request); // ... 省略其他逻辑 }
ServiceBusClient生命周期使用错误ServiceBusClient是设计为单例复用的对象,官方建议整个应用生命周期内只初始化一个实例。你现在每次创建ServiceBusMessagingSender就新建一个ServiceBusClient,频繁创建销毁会导致连接池耗尽、限流、连接异常等问题,且这类异常有时候会被默认重试策略静默吞掉。
修改方案:将ServiceBusClient作为单例注入,所有Sender复用同一个Client实例。免费层配额与限流问题
Azure Service Bus 免费层有严格的配额限制:- 单条消息最大大小256KB,如果序列化后的消息超过该大小会发送失败
- 每秒操作数上限为100次,超过会被节流,默认重试策略如果重试耗尽也会失败
- 总消息存储上限为1GB,存满后新消息无法写入
你可以在Azure门户的ASB命名空间「指标」页查看是否有节流、消息大小超限、发送失败的指标记录。
SDK版本bug
你使用的Azure.Messaging.ServiceBus7.2.0是2021年的老旧版本,存在多个已知的消息发送静默失败、重试策略异常的bug,建议升级到最新的稳定版本。增加异常捕获与日志
可以在发送逻辑外层增加异常捕获,输出详细错误信息,排查是否有内部未抛出的异常:
public async Task SendMessageAsync(T message) { try { var serviceBusMessage = _messageSerializer.Serialize(message); await _sender.SendMessageAsync(serviceBusMessage).ConfigureAwait(false); } catch (Exception ex) { // 输出异常详细信息,包括错误码、请求ID等 Console.WriteLine($"消息发送失败: {ex}"); throw; } }
内容的提问来源于stack exchange,提问作者chodi

