服务总线消息传送异常 (.NET)

当服务操作或客户端遇到错误时,服务总线 .NET 客户端库会显示异常。 如果可能,标准 .NET 异常类型用于传达错误信息。 对于特定于服务总线的方案,将引发 ServiceBusException

根据配置的重试选项,服务总线客户端会自动重试被视为暂时性的异常。 当应用程序出现异常时,所有重试都未成功应用,或者该异常被视为非暂时性异常。 有关配置重试选项的详细信息,请参阅自定义重试选项示例。

ServiceBusException

该异常包括一些上下文信息,可帮助你了解错误的上下文及其相对严重性。

  • EntityPath:标识发生异常的服务总线实体(如果可用)。
  • IsTransient:指示异常是否被视为可恢复。 如果认为异常是暂时性的,则表明 Azure 服务总线已应用适当的重试策略,但所有重试均不成功。
  • Message:提供发生的错误的说明和相关上下文。
  • StackTrace:表示调用堆栈的即时帧,突出显示代码中发生错误的位置。
  • InnerException:当异常是服务操作的结果时,它通常是描述错误的 Microsoft.Azure.Amqp.AmqpException 实例,遵循 OASIS 高级消息队列协议 (AMQP) 1.0 规范
  • Reason:为故障提供了一组众所周知的原因,它们有助于分类和阐明根本原因。 这些值旨在让你能够在不适合检查异常消息文本时应用异常筛选和其他逻辑。 一些关键的失败原因包括:
    • ServiceTimeout:指示服务总线服务未在预期时间内响应操作请求。 这可能是由于暂时性网络问题或服务问题造成的。 服务总线服务可能已成功完成请求,也可能未成功完成;状态未知。 在下一个可用会话的上下文中,此异常指示实体中没有可用的解锁会话。 这些错误是自动重试的暂时性错误。

    • QuotaExceeded:通常表示单个实体的活动接收操作过多。 为了避免此错误,请减少潜在的并发接收数。 可以使用批处理接收来尝试接收每个接收请求的多条消息。 有关详细信息,请参阅服务总线配额

    • MessageSizeExceeded:指示消息大小超过最大消息大小。 消息大小包括消息的正文以及任何关联的元数据。 解决此错误的最佳方法是减少在批处理中发送的消息数或消息中包含的正文大小。 由于大小限制可能会更改,因此请参阅服务总线配额以了解具体情况。

    • MessageLockLost:指示消息的锁定已丢失。 调用方应尝试再次接收和处理消息。 此异常仅适用于不使用会话的实体。 如果处理时间超过锁定持续时间且消息锁定未续订,则会发生此错误。 当链路由于暂时性网络问题而拆离时,或者当链路空闲 10 分钟时,也可能发生此错误。

      服务总线服务使用 AMQP 协议,该协议是有状态的。 由于协议的性质,如果连接客户端和服务的链路在接收消息后但在消息解决之前拆离,则无法在重新连接链路时解决该消息。 由于短期暂时性网络故障、网络中断或服务强制实施 10 分钟空闲超时,可以拆离链接。 链接重新连接会作为任何需要链接的操作(即解决或接收消息)的一部分自动进行。 由于此行为,即使锁定到期时间尚未过去,你也可能会遇到 ReasonMessageLockLostSessionLockLostServiceBusException

    • SessionLockLost:指示会话上的锁定已过期。 调用方应再次尝试接受会话。 此异常仅适用于启用了会话的实体。 如果处理时间超过锁定持续时间且会话锁定未续订,则会发生此错误。 当链路由于暂时性网络问题而拆离时,或者当链路空闲 10 分钟时,也可能发生此错误。 服务总线服务使用 AMQP 协议,该协议是有状态的。 由于协议的性质,如果连接客户端和服务的链路在接收消息后但在消息解决之前拆离,则无法在重新连接链路时解决该消息。 由于短期暂时性网络故障、网络中断或服务强制实施 10 分钟空闲超时,可以拆离链接。 链接重新连接会作为任何需要链接的操作(即解决或接收消息)的一部分自动进行。 由于此行为,即使锁定到期时间尚未过去,你也可能会遇到 ReasonMessageLockLostSessionLockLostServiceBusException

    • MessageNotFound:当尝试按实体中不存在或当前已锁定的消息的序列号接收延迟消息时,会发生此错误。

    • SessionCannotBeLocked:指示无法锁定请求的会话,因为锁定已保留在其他位置。 锁定过期后,可以接受会话。

    • GeneralError:指示服务总线服务处理请求时遇到错误。 此错误通常是由服务升级和重启引起的。 这些错误是自动重试的暂时性错误。

    • ServiceCommunicationProblem:指示与服务通信时出错。 此问题可能源于暂时性网络问题或服务问题。 这些错误是将自动重试的暂时性错误。

    • ServiceBusy:指示服务限制了请求。 此处可以找到描述导致请求受到限制的原因以及如何避免被限制的详细信息。 将重试受限制的请求,但在尝试使用相同 ServiceBusClient(或从该客户端创建的任何子类型)之前,客户端库会自动应用 10 秒回退。 如果实体的锁定持续时间小于 10 秒,则可能会导致问题,因为任何未解决的消息或锁定的会话都可能丢失消息或会话锁定。 由于受限制的请求通常会重试成功,因此生成的异常将记录为警告而不是错误 - 特定的警告级别事件源事件为 43(RunOperation 遇到异常并重试)。

    • MessagingEntityAlreadyExists:指示同名实体存在于同一命名空间下。

    • MessagingEntityDisabled:消息传送实体已禁用。 使用门户再次启用实体。

    • MessagingEntityNotFound:服务总线服务找不到服务总线资源。

处理 ServiceBusException - 示例

下面是如何处理 ServiceBusException 并按 Reason 进行筛选的示例。

try
{
    // Receive messages using the receiver client
}
catch (ServiceBusException ex) when
    (ex.Reason == ServiceBusFailureReason.ServiceTimeout)
{
    // Take action based on a service timeout
}

其他常见异常

  • ArgumentException:如果与客户端交互时提供的参数无效,则客户端将引发派生自 ArgumentException 的此异常。 可以在 Message 中找到有关具体参数和问题性质的信息。
  • InvalidOperationException:尝试执行对其当前配置无效的操作时发生。 当客户端未配置为支持该操作时,通常会发生此异常。 通常,可以通过调整传递给客户端的选项来缓解此问题。
  • NotSupportedException:当请求的操作对客户端有效但不受其当前状态支持时发生。 可以在 Message 中找到有关方案的信息。
  • AggregateException:当操作可能遇到多个异常并将它们显示为单个故障时发生。 启动或停止服务总线处理器或服务总线会话处理器时,通常会遇到此异常。

原因:QuotaExceeded

原因设置为 QuotaExceededServiceBusException 指示已超出特定实体的配额。

注意

有关服务总线配额,请参阅配额

队列和主题

对队列和主题而言,这通常指队列的大小。 错误消息属性会包含更多详细信息,如以下示例所示:

Message: The maximum entity size has been reached or exceeded for Topic: 'xxx-xxx-xxx'. 
    Size of entity in bytes:1073742326, Max entity size in bytes:
1073741824..TrackingId:xxxxxxxxxxxxxxxxxxxxxxxxxx, TimeStamp:3/15/2013 7:50:18 AM

该消息指出,主题超过其大小限制,本例中为 1 GB(默认大小限制)。

命名空间

对于命名空间,QuotaExceeded 异常可能指示应用程序已超出到命名空间的最大连接数。 例如:

<tracking-id-guid>_G12 ---> 
System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: 
ConnectionsQuotaExceeded for namespace xxx.

常见原因

此错误有两个常见原因:死信队列和无法正常运行的消息接收器。

  • 死信队列 读取器无法完成消息,当锁定过期后,消息将返回至队列/主题。 如果读取者遇到阻止其完成消息的异常,则可能会发生这种情况。 消息读取 10 次后,默认移至死信队列。 此行为由 MaxDeliveryCount 属性控制,默认值为 10。 消息堆积在死信队列中会占用空间。

    若要解决此问题,请读取并完成死信队列中的消息,就像处理任何其他队列一样。

  • 接收方已停止。 接收方已停止从队列或订阅接收消息。 确定问题的方式是查看活动消息计数。 如果活动消息计数属性很高或不断增加,则表示消息写入的速度超过读取的速度。

原因:MessageLockLost

原因

原因设置为 MessageLockLostServiceBusException 指示使用 PeekLock 接收模式接收了消息,客户端持有的锁定在服务端过期。

消息上的锁定可能会因多种原因过期:

  • 锁计时器在客户端应用程序续订之前已过期。
  • 客户端应用程序获取了锁,将其保存到持久存储区,然后重新启动。 重启后,客户端应用程序会查看即时消息并尝试完成这些消息。

在以下情况下,也可能会收到此异常:

  • 服务更新
  • OS 更新
  • 在持有锁时更改实体(队列、主题、订阅)的属性。

解决方法

当客户端应用程序收到 MessageLockLostException 时,它将无法再处理该消息。 客户端应用程序可以选择记录异常以进行分析,但客户端必须释放消息才行。

由于消息上的锁定已过期,因此它将返回到队列(或订阅),可由调用接收的下一个客户端应用程序进行处理。

如果超过了 MaxDeliveryCount,则消息可能会移至 DeadLetterQueue

原因:SessionLockLost

原因

如果接受了某个会话,且客户端持有的锁定在服务端过期,则将引发原因设置为 MessageLockLostServiceBusException

会话上的锁定可能会因多种原因过期:

  • 锁计时器在客户端应用程序续订之前已过期。
  • 客户端应用程序获取了锁,将其保存到持久存储区,然后重新启动。 重新启动后,客户端应用程序会查看即时会话,并尝试处理这些会话中的消息。

在以下情况下,也可能会收到此异常:

  • 服务更新
  • OS 更新
  • 在持有锁时更改实体(队列、主题、订阅)的属性。

解决方法

当客户端应用程序收到 SessionLockLostException 时,它无法再处理会话上的消息。 客户端应用程序可以考虑记录异常以进行分析,但客户端必须释放消息才行。

由于会话上的锁定已过期,因此它将返回到队列(或订阅),并且可由接受会话的下一个客户端应用程序锁定。 由于会话锁由单个客户端应用程序在任意给定的时间持有,因此保证了有序处理。

TimeoutException

TimeoutException 指示用户启动的操作所用的时间超过操作超时值。

应检查 ServicePointManager.DefaultConnectionLimit 属性的值,因为达到此限制也会导致 TimeoutException 异常。

预计会在维护操作(例如,服务总线服务更新或运行服务的资源上的 OS 更新)期间或操作间隙发生超时。 在 OS 更新期间,实体会四处移动,节点会更新或重启,这可能会导致超时。 有关 Azure 服务总线服务的服务级别协议 (SLA) 详细信息,请参阅服务总线的 SLA

SocketException

原因

以下情况下会引发 SocketException:

  • 当由于主机在指定时间后没有正确响应而导致连接尝试失败时(TCP 错误代码 10060)。
  • 建立的连接失败,因为连接的主机未能响应。
  • 处理消息时出错,或者远程主机超过了规定的超时时间。
  • 基础网络资源问题。

解决方法

SocketException 错误表明承载应用程序的 VM 无法将名称 转换为相应的 IP 地址。

检查以下命令是否成功映射到 IP 地址。

PS C:\> nslookup <mynamespace>.servicebus.chinacloudapi.cn

应提供如下所示的输出:

Name:    <cloudappinstance>.chinacloudapp.cn
Address:  XX.XX.XXX.240
Aliases:  <mynamespace>.servicebus.chinacloudapi.cn

如果上述名称未解析为 IP 和命名空间别名,请与网络管理员联系,开展进一步调查。 名称解析是通过 DNS 服务器完成的,通常是客户网络中的资源。 如果 DNS 解析由 Azure DNS 完成,请与 Azure 支持部门联系。

如果名称解析按预期工作,请在此处检查是否允许连接到 Azure 服务总线。

UnauthorizedAccessException

UnauthorizedAccessException 指示提供的凭据不允许执行请求的操作。 Message 属性包含有关失败的详细信息。

建议按照这些验证步骤操作,具体取决于构造 ServiceBusClient 时提供的授权类型。

地域复制异常

ServerBusyException

原因

  • 在异步复制期间(复制延迟大于零),客户端尝试对服务总线实体(队列、主题)执行操作或执行管理操作,但无法完成该操作,因为主要区域和次要区域之间的复制延迟时间超过了允许的复制延迟时间上限(以秒为单位)。
    • 示例:操作受到限制,因为新的复制延迟将达到 38323 秒,这一时间大于设置的复制延迟时间上限(300 秒)。 正在复制的最新操作的当前复制延迟为 0 秒。
  • 实体的复制队列超出了其大小上限(以字节为单位)。 复制队列的大小上限(以字节为单位)是由服务总线设置的内部限制。
    • 示例:复制队列大小 73128000 超出了阈值 67108864。
  • 在同步复制中,一个请求在等待另一个请求复制时超时。
    • 示例:客户端应用程序对 skarri-stroage-exp1(chinanorth3)/q1:MessagingJournal 的请求量很大。 正在复制到其他区域。

解决方法

  • 客户端应该回退,以便为服务提供时间来处理其给定的工作负载,然后客户端应该重试。

超时

原因

  • Geo DR 中的超时异常意味着操作未在客户端提供的超时时间内完成。
    • 在同步复制中,操作的主要区域写入和复制到辅助区域都在操作超时的范围内。
    • 在异步复制中,操作的主要区域写入在操作的超时范围内,但操作复制到辅助区域不在操作的超时范围内。
    • 示例:该操作未在为对象消息分配的时间 (00:01:00) 内完成。 (ServiceTimeout)。

解决方法

  • 客户端应重试该操作。
  • 请注意,超时操作的某些步骤可能已完成。 超时操作可能已写入主要区域和某些辅助区域。 如果已将某个操作写入主要区域,则无论客户端超时如何,系统最终都会将该操作复制到所有次要区域。

BadRequest

原因

  • 在计划的故障转移期间,主要区域临时设置为只读,以便次要区域能够更新并保持数据同步。 如果客户端在主要区域处于临时只读状态时尝试对主要区域执行写入操作,则客户端将收到 BadRequest 异常。
    • 示例:正在进行复制角色切换,主副本:<实体名称>为只读。

解决方法

  • 在写入操作成功之前,客户端必须等待计划内故障转移完成。
  • 如果计划的故障转移时间过长,可以改为触发强制故障转移。

后续步骤

有关服务总线 .NET API 的完整参考,请参阅 Azure .NET API 参考。 有关故障排除提示,请参阅故障排除指南