Compartilhar via

分区队列和主题

Azure Service Bus使用多个消息代理来处理消息和多个消息存储来存储消息。 常规队列或主题由单个消息代理与一个消息存储进行处理。 Service Bus 分区启用队列、主题或消息实体,以便跨多个消息代理和消息存储进行分区。 分区意味着分区实体的总吞吐量不再受单个消息中转站或消息存储的性能所限制。 此外,消息存储的临时中断不会使分区队列或主题不可用。 分区队列和主题可以包含所有高级Service Bus功能,例如对事务和会话的支持。

注意

在分区方面,基本/标准和高级 SKU 之间存在一些差异。

  • 分区在为基本或标准 SKU 中的所有队列和主题创建实体时可用。 命名空间可同时具有已分区实体和非分区实体。
  • 分区在为高级消息传送 SKU 创建命名空间时可用,该命名空间中的所有队列和主题都已分区。 高级命名空间中以前迁移的所有分区实体将继续按预期工作。
  • 在基本或标准 SKU 中启用分区时,始终创建 16 个分区。
  • 在高级 SKU 中启用分区时,可以在创建命名空间期间指定分区数。

不能更改任何现有命名空间、队列或主题上的分区选项。 只能在创建实体时设置该选项。

工作原理

每个分区队列或主题由多个分区构成。 每个分区存储在不同的消息传送存储中并由不同的消息中转站进行处理。 向分区队列或主题发送消息时,Service Bus将消息分配给其中一个分区。 Service Bus随机选择分区或使用发送方指定的分区键。

当客户端想要从分区队列或从分区主题的订阅接收消息时,Service Bus查询消息的所有分区。 它将从任何消息存储区获取的第一条消息返回到接收方。 Service Bus缓存其他消息,并在收到更多接收请求时返回它们。 接收客户端不知道分区。 分区队列或主题(例如读取、完成、延迟、死信、预提取)的面向客户端的行为与常规实体的行为相同。

对于未分区实体,速览操作始终返回最旧的消息,而对于分区实体则不然。 相反,它返回的是某个消息代理首先响应的分区中的最旧消息。 不保证返回的消息是所有分区中最旧的消息。

向分区队列或主题发送一条消息,或从分区队列或主题接收消息时无需额外付费。

注意

速览操作根据消息的序列号返回分区中最旧的消息。 对于分区实体,序列号相对于分区。 有关详细信息,请参阅消息序列化和时间戳

使用分区键

将消息排入分区队列或主题时,Service Bus检查分区键是否存在。 如果找到,它会选择基于该键的分区。 如果找不到分区键,它会选择基于内部算法的分区。

使用分区键

某些应用场景(例如会话或事务)要求将消息存储在特定的分区中。 所有这些应用场景都需要使用分区键。 Service Bus将使用相同的分区键的所有消息分配给同一分区。 如果分区暂时不可用,Service Bus返回错误。

根据应用场景,将不同的消息属性用作分区键:

SessionId:如果消息设置了会话 ID 属性,Service Bus将其用作分区键。 这样,同一消息中转站处理属于同一会话的所有消息。 通过使用会话,Service Bus保证消息排序以及会话状态的一致性。

PartitionKey:如果消息具有分区键属性而不是会话 ID 属性集,Service Bus使用分区键属性值作为分区键。 如果消息同时设置了会话 ID 和分区键属性,则这两个属性必须相同。 如果分区键属性设置为与会话 ID 属性不同的值,Service Bus 会返回无效操作异常。 如果发送方发送不顾及会话的事务消息,请使用分区键属性。 分区键可确保相同的消息代理处理事务中发送的所有消息。

MessageId:如果使用 重复检测功能 创建队列或主题,并且未设置会话 ID 或分区键属性,则消息 ID 属性值将用作分区键。 (如果发送应用程序没有,Microsoft客户端库会自动分配消息 ID。在这种情况下,同一消息中转站处理同一消息的所有副本。 此 ID 使Service Bus能够检测和消除重复消息。 如果未启用重复检测功能,Service Bus不会将消息 ID 属性视为分区键。

不使用分区键

如果未指定分区键,Service Bus 将以轮询方式将消息分发到分区的队列或主题的所有分区。 如果所选分区不可用,Service Bus将消息分配给其他分区。 这样一来,尽管消息传送存储暂时不可用,发送操作仍可成功。 但是,您无法获得分区键所提供的排序保证。

有关可用性(没有分区键)和一致性(使用分区键)之间的权衡的更深入讨论,请参阅事件中心中的可用性和一致性。 除分区 ID 对用户不可见外,此信息同样适用于分区 Service Bus 实体。

若要给Service Bus足够的时间将消息排入其他分区,发送消息的客户端指定的超时值必须大于 15 秒。 建议使用默认值 60 秒。

分区键会将消息“固定”到特定分区。 如果保存此分区的消息存储不可用,Service Bus返回错误。 在没有分区键的情况下,Service Bus可以选择不同的分区,操作就成功。 因此,除非需要分区键,否则不要提供分区键。

高级主题

对分区实体使用事务

作为事务的一部分发送的消息必须指定分区键。 键可以是以下属性之一:会话 ID、分区键或消息 ID。 作为同一事务的一部分发送的所有消息都必须指定相同的分区键。 如果尝试在事务中发送不含分区键的消息,Service Bus 将返回无效操作异常。 如果尝试在同一事务中发送具有不同分区键的多个消息,Service Bus将返回无效操作异常。 例如:

CommittableTransaction committableTransaction = new CommittableTransaction();
using (TransactionScope ts = new TransactionScope(committableTransaction))
{
    ServiceBusMessage msg = new ServiceBusMessage("This is a message");
    msg.PartitionKey = "myPartitionKey";
    await sender.SendMessageAsync(msg); 
    ts.Complete();
}
committableTransaction.Commit();

如果设置用作分区键的任何属性,Service Bus将消息固定到特定分区。 无论是否使用事务,都会出现此行为。 如果不需要,请不要指定分区键。

在与分区实体的会话中使用事务

若要将事务消息发送到会话感知主题或队列,请设置消息的会话 ID 属性。 如果指定分区键属性,则它必须与会话 ID 属性相同。 如果它们不同,Service Bus 会抛出无效操作异常。

与常规(非分区)队列或主题不同,不能使用单个事务将多个消息发送到不同的会话。 如果尝试此操作,Service Bus 将返回无效操作异常。 例如:

CommittableTransaction committableTransaction = new CommittableTransaction();
using (TransactionScope ts = new TransactionScope(committableTransaction))
{
    ServiceBusMessage msg = new ServiceBusMessage("This is a message");
    msg.SessionId = "mySession";
    await sender.SendMessageAsync(msg); 
    ts.Complete();
}
committableTransaction.Commit();

使用分区实体自动进行消息转发

Service Bus支持从分区实体、到分区实体或在分区实体之间自动转发消息。 创建或更新队列和订阅时,可以启用此功能。 有关详细信息,请参阅启用消息转发。 如果消息指定分区键(会话 ID、分区键或消息 ID),则分区键用于目标实体。

注意事项和指南

  • 高一致性功能:如果实体使用会话、重复检测或对分区键的显式控制等功能,则消息传送作始终路由到特定分区。 如果任何分区遇到过高的流量,或基础存储处于不正常状态,这些操作将失败,可用性会降低。 总的来说,一致性仍远高于非分区实体。 只有一部分流量遇到问题,而不是所有流量。 有关详细信息,请参阅此处对可用性和一致性的讨论
  • 管理:必须对实体的所有分区执行创建、更新及删除等操作。 如果任何分区处于不正常状态,可能会导致这些操作失败。 对于 Get 操作,必须汇总来自所有分区的信息,例如消息计数。 如果任何分区处于不正常状态,则实体可用性状态会报告为受限制。
  • 少量消息的情况:对于这类情况,尤其是使用 HTTP 协议时,可能必须执行多次接收操作,才能获取所有消息。 对于接收请求,前端对所有分区执行接收,并缓存它接收的所有响应。 同一连接上的后续接收请求受益于此缓存和接收延迟较低。 不过,如果你有多个连接或使用 HTTP,则针对每个请求建立新的连接。 因此,不能保证它位于同一节点上。 如果现有的所有消息均被锁定,而且在另一个前端中缓存,则接收操作返回 null。 消息最后会到期,可以再次接收它们。 建议使用 HTTP 保持连接。 在低容量场景中使用分区时,接收操作所用时间可能比预期的长。 因此,在这些方案中不要使用分区。 删除任何现有的分区实体,并在禁用分区的情况下重新创建它们,以提高性能。
  • 浏览/速览消息:速览操作不会始终返回要求的消息数。 这两个常见原因解释了此行为。 其中一个原因是消息集合的汇总大小超过大小上限。 另一个原因是,在分区队列或主题中,某个分区可能没有足够的消息来满足请求的数量。 通常,如果应用程序想要查看或浏览特定数量的消息,则应重复调用速览作,直到它获取该数目的消息,或者没有更多消息要查看。 有关详细信息(包括代码示例),请参阅消息浏览

分区的实体限制

目前,Service Bus 对分区队列和主题施加以下限制:

  • 分区队列和主题不支持在单个事务中发送属于不同会话的消息。
  • Service Bus目前允许基本层和标准层每个命名空间最多 100 个分区队列或主题。 每个分区队列或主题都会被计入每个命名空间的 10,000 个实体配额内。

后续步骤

可以使用 Azure 门户、PowerShell、CLI、Resource Manager 模板、.NET、Java、Python和 JavaScript 来启用分区。 有关详细信息,请参阅启用分区(基本/标准)

若要了解高级消息队列协议 (AMQP) 1.0 消息传送规范的核心概念,请参阅 AMQP 1.0 协议指南