问题描述
Service Bus如何确保消息发送成功,发送端是否有Ack机制(是否有回调API告诉发送端,服务端已经收到消息)?根据对.NET发送Service Bus消息代码的分析,发送方法queueClient.SendAsync(message)并没有返回值,所以无法知道发送消息是否成功。
问题根源
Azure 服务总线已针对持久性进行优化,会确保在服务确认请求成功之前,发送到服务总线的所有数据将提交到存储。一旦服务总线成功“ACK”(确认)请求,即表示服务总线已成功处理该请求。 如果服务总线返回“NACK”(失败),则表示服务总线无法处理该请求,客户端应用程序必须重试该请求。官方提供的SDK,在没有自定义RetryPolicy的情况下,都会采用默认的重试机制。
Service Bus .Net SDK 默认的重试机制(RetryPolicy)为:
- DeltaBackoff: 重试之间的间隔时间
- MaxRetryCount: 最大重试次数
- MaximumBackoff: 最大的间隔时间
- MinimalBackoff: 最小的间隔时间
所以在调用Service Bus SDK发送消息时,如果代码正常执行且没有Exception,则表示消息成功发送。
一句话总结为:无错即成功,失败看异常。
另外也有如下两种发送俩查看消息:
方式一:在Azure Service Bus门户中查看指标 Incoming request/message
方式二:通过Servie Bus Explorer工具查看发送消息的内容
Service Bus异常消息类型
- 用户代码错误(System.ArgumentException、System.InvalidOperationException、System.OperationCanceledException、System.Runtime.Serialization.SerializationException)。 常规操作:继续之前尝试修复代码。
- 设置/配置错误(Microsoft.ServiceBus.Messaging.MessagingEntityNotFoundException、System.UnauthorizedAccessException)。 常规操作:检查配置,必要时进行更改。
- 暂时性异常(Microsoft.ServiceBus.Messaging.MessagingException、Microsoft.ServiceBus.Messaging.ServerBusyException、Microsoft.ServiceBus.Messaging.MessagingCommunicationException)。 常规操作:重试操作或通知用户。 客户端 SDK 中的
RetryPolicy
类可以配置为自动处理重试。 有关详细信息,请参阅重试指南。- 其他异常(System.Transactions.TransactionException、System.TimeoutException、Microsoft.ServiceBus.Messaging.MessageLockLostException、Microsoft.ServiceBus.Messaging.SessionLockLostException)。 常规操作:特定于异常类型。
异常类型
下表列出了消息异常的类型及其原因,并说明可以采取的建议性操作。
异常类型
异常类型 | 说明/原因/示例 | 建议的操作 | 自动/立即重试注意事项 |
TimeoutException | 服务器在 OperationTimeout 控制的指定时间内未响应请求的操作。 服务器可能已完成请求的操作。 这可能是由于网络或其他基础结构延迟造成的。 | 检查系统状态的一致性,并根据需要重试。 请参阅超时异常。 | 在某些情况下,重试可能会有帮助;在代码中添加重试逻辑。 |
InvalidOperationException | 不允许在服务器或服务中执行请求的用户操作。 有关详细信息,请查看异常消息。 例如,如果在 ReceiveAndDelete 模式下收到消息,则 Complete() 将生成此异常。 | 检查代码和文档。 确保请求的操作有效。 | 重试不起作用。 |
OperationCanceledException | 尝试对已关闭、中止或释放的对象调用某个操作。 在极少数情况下,环境事务已释放。 | 检查代码并确保代码不会对已释放的对象调用操作。 | 重试不起作用。 |
UnauthorizedAccessException | TokenProvider 对象无法获取令牌,该令牌无效,或者令牌不包含执行操作所需的声明。 | 确保使用正确的值创建令牌提供程序。 检查访问控制服务的配置。 | 在某些情况下,重试可能会有帮助;在代码中添加重试逻辑。 |
ArgumentException ArgumentNullException ArgumentOutOfRangeException |
提供给该方法的一个或多个参数均无效。 提供给 NamespaceManager 或 Create 的 URI 包含路径段。 提供给 NamespaceManager 或 Create 的 URI 方案无效。 属性值大于 32 KB。 |
检查调用代码并确保参数正确。 | 重试不起作用。 |
MessagingEntityNotFoundException | 与操作关联的实体不存在或已被删除。 | 确保该实体存在。 | 重试不起作用。 |
MessageNotFoundException | 尝试接收具有特定序列号的消息。 找不到此消息。 | 确保该消息尚未接收。 检查死信队列,以确定该消息是否被视为死信。 | 重试不起作用。 |
MessagingCommunicationException | 客户端无法与服务总线建立连接。 | 确保提供的主机名正确并且主机可访问。 如果你的代码在使用防火墙/代理的环境中运行,请确保到服务总线域/IP 地址和端口的流量未被阻止。 |
如果存在间歇性的连接问题,重试可能会有帮助。 |
ServerBusyException | 服务目前无法处理请求。 | 客户端可以等待一段时间,并重试操作。 | 客户端可在特定的时间间隔后重试操作。 如果重试导致其他异常,请检查该异常的重试行为。 |
MessagingException | 在以下情况下,可能会引发一般消息异常: 尝试使用属于其他实体类型(例如主题)的名称或路径创建 QueueClient。 尝试发送大于 256 KB 的消息。 服务器或服务在处理请求期间遇到错误。 有关详细信息,请查看异常消息。 这通常是暂时性异常。
由于实体正受到限制,因此已终止请求。 错误代码:50001、50002、50008。 |
检查代码,并确保只对消息正文使用可序列化对象(或使用自定义序列化程序)。 在文档中查看属性支持的值类型,并只使用支持的类型。 检查 IsTransient 属性。 如果为 true,可以重试操作。 |
如果异常是由于限制导致的,请等待几秒钟,然后重试该操作。 重试行为未定义,在其他场景中可能没有帮助。 |
MessagingEntityAlreadyExistsException | 尝试使用已被该服务命名空间中另一实体使用的名称创建实体。 | 删除现有的实体,或者选择不同的名称来创建实体。 | 重试不起作用。 |
QuotaExceededException | 消息实体已达到其允许的最大大小,或已超出到命名空间的最大连接数。 | 通过从实体或其子队列接收消息在该实体中创建空间。 请参阅QuotaExceededException。 | 如果同时已删除消息,则重试可能会有帮助。 |
RuleActionException | 如果尝试创建无效的规则操作,服务总线将返回此异常。 如果在处理该消息的规则操作时出错,服务总线会将此异常附加到死信消息。 | 检查规则操作是否正确。 | 重试不起作用。 |
FilterException | 如果尝试创建无效的筛选器,服务总线将返回此异常。 如果在处理该消息的筛选器时出错,服务总线会将此异常附加到死信消息。 | 检查筛选器是否正确。 | 重试不起作用。 |
SessionCannotBeLockedException | 尝试接受具有特定会话 ID 的会话,但该会话当前已被另一客户端锁定。 | 确保该会话未由其他客户端锁定。 | 如果在此期间会话已释放,则重试可能会有帮助。 |
TransactionSizeExceededException | 事务包含过多的操作。 | 减少此事务中操作的数目。 | 重试不起作用。 |
MessagingEntityDisabledException | 对已禁用的实体请求运行时操作。 | 激活实体。 | 如果在此期间该实体已激活,则重试可能会有帮助。 |
NoMatchingSubscriptionException | 如果向已启用预筛选的主题发送消息并且所有筛选器都不匹配,则服务总线返回此异常。 | 确保至少有一个筛选器匹配。 | 重试不起作用。 |
MessageSizeExceededException | 消息有效负载超出 256 KB 限制。 256-KB 限制是指总消息大小,可能包括系统属性和任何 .NET 开销。 | 减少消息负载的大小,并重试操作。 | 重试不起作用。 |
TransactionException | 环境事务 (Transaction.Current) 无效。 该事务可能已完成或已中止。 内部异常可能提供了更多信息。 | 重试不起作用。 | |
TransactionInDoubtException | 已对未决事务尝试进行操作,或尝试提交该事务并且事务进入不确定状态。 | 应用程序必须处理此异常(作为特例),因为此事务可能已提交。 |
参考资料
Service Bus Explorer: https://github.com/paolosalvatori/ServiceBusExplorer/releases
Service Bus Retry Policy: https://docs.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific#service-bus