ServiceBusClient 发送消息前必须确认连接字符串权限
用
ServiceBusClient发送消息失败,十有八九是连接字符串没开“Send”权限。Azure 门户里生成的连接字符串默认只含“Listen”,哪怕你只是发消息,也得手动进“Shared access policies”新建一个带
Send(或
Manage)权限的策略,再拿它的连接字符串。
常见错误现象:
UnauthorizedAccessException或
401 Unauthorized,但堆栈里不直接提权限——它藏在底层 HTTP 响应里。 队列场景:连接字符串只需
Send权限 主题场景:同样只需
Send,不用
Manage(除非你要动态创建订阅) 本地开发调试时,别用 RootManageSharedAccessKey 的完整连接字符串,容易误提交到代码库
发送到队列用 ServiceBusSender.SendMessageAsync,不是 SendAsync
ServiceBusSender是发送入口,不是
ServiceBusClient直接发。调用链必须是:
ServiceBusClient→
CreateSender("queue-name") → SendMessageAsync。写成
client.SendAsync(...)会编译不过——这个方法根本不存在。
示例关键片段:
var client = new ServiceBusClient(connectionString);
var sender = client.CreateSender("myqueue");
await sender.SendMessageAsync(new ServiceBusMessage("hello"));
每条消息封装为 ServiceBusMessage,不能直接传 string 或 byte[] 若需设置 TTL、SessionId、CorrelationId 等,都在
ServiceBusMessage构造后赋值属性 同一个
ServiceBusSender实例可复用,但别跨线程共用未加锁的实例(SDK 内部已做线程安全处理,但高并发下仍建议按作用域创建)
发到主题要指定主题名,订阅名由接收方控制
主题(Topic)和队列(Queue)的发送 API 完全一致,区别只在
CreateSender的参数:传主题名,不是订阅名。订阅(Subscription)是接收端概念,发送方完全感知不到。
例如发到主题
orders-topic:
var sender = client.CreateSender("orders-topic"); // 不是 "orders-topic/subscriptions/new-orders"
await sender.SendMessageAsync(new ServiceBusMessage(JsonSerializer.Serialize(order)));
主题支持通配符订阅(如 orders-*),但发送方无需、也不能指定匹配逻辑 若消息需要路由到特定订阅,得靠
Rule+
Message.UserProperties或
ApplicationProperties配合筛选器,不是靠发送路径 主题本身不存消息,消息进入主题后立刻分发到所有匹配的订阅;所以发送成功 ≠ 订阅端收到,得单独查订阅的活跃消息数
生产环境必须处理 MessageException 和 ServiceBusException
网络抖动、配额超限、消息体超 256KB(标准层)都会抛
ServiceBusException,而
MessageException多见于序列化失败(比如传了不可序列化的对象)。不捕获这些异常,会导致消息静默丢失或应用崩溃。 推荐用
try/catch (ServiceBusException ex) when (ex.Reason == ServiceBusFailureReason.ServiceTimeout)做重试 对
MessageSizeExceeded类异常,必须提前校验
ServiceBusMessage.Body长度,SDK 不会在发送前主动检查 不要依赖
sender.DisposeAsync()清理连接——它只释放本地资源;连接池由
ServiceBusClient统一管理,应优先复用 client 实例 实际用起来最易忽略的是:主题发送和队列发送代码一模一样,差别全在 Azure 资源命名和后台配置上。很多人卡在“发了但收不到”,结果发现是订阅没建、或筛选器写错了条件、或订阅被手动禁用了。
