消息序列化和时间戳
序列化和时间戳是所有服务总线实体上始终启用的两项功能,通过收到或检索到的消息的 SequenceNumber
和 EnqueuedTimeUtc
属性体现。
如果消息的绝对顺序至关重要,和/或使用者需要消息的可信唯一标识符,中转站会向消息分发相对于队列或主题的无间隔递增序列号。 对于已分区实体,序列号是相对于分区进行分发。
序列号
SequenceNumber
值是在代理接受并存储消息时分配给消息的唯一 64 位整数,可用作消息的内部标识符。 对于已分区实体,最前面的 16 位数反映的是分区标识符。 当 64 位或 48 位(不包括分区标识符的 16 位)范围耗尽时,序列号将滚动到零。
序列号可作为唯一标识符受到信任,因为它是由中央主管中立机构(而不是客户端)分配。 它还表示真正的到达顺序,比作为顺序条件的时间戳更为精确,因为时间戳在消息传递速度极快时的解析度可能不够高,并且可能会在代理所有权跨节点转让时受到时钟偏差的影响(虽然影响很小)。
绝对到达顺序非常重要。例如,在供货量有限的业务方案中,在供应期内货物供应遵循“先到先得”的原则;音乐会门票销售就是一个例子。
时间戳
时间戳功能将充当一个中立且值得信赖的机构,可准确捕获消息的到达时间 (UTC),并通过 EnqueuedTimeUtc
属性进行体现。 此值非常适用于依赖截止时间、但处理进度远远落后于队列积压工作 (backlog) 的业务方案,如需要确定工作项是否在特定日期的午夜前提交。
注意
序列号本身可以保证消息的排队顺序和提取程序顺序,但不能保证需要会话的处理顺序。
假设队列中有 5 条消息,有 2 个使用者。 使用者 1 选取消息 1。 使用者 2 选取消息 2。 使用者 2 完成对消息 2 的处理并选取消息 3,而使用者 1 尚未完成对消息 1 的处理。 使用者 2 完成对消息 3 的处理,但使用者 1 仍未完成对消息 1 的处理。 最后,使用者 1 完成对消息 1 的处理。 因此,消息按以下顺序进行处理:消息 2、消息 3 和消息 1。 如果需要按顺序处理消息 1、2 和 3,则需要使用会话。
因此,如果只需按顺序检索消息,则无需使用会话。 如果需要按顺序处理消息,请使用会话。 属于同一组的消息应设置相同的会话 ID,可以是一组中的消息1、4 和 8,也可以是另一组中的消息 2、3 和 6。
有关详细信息,请参阅服务总线消息会话。
计划的消息
可以将消息提交到队列或主题以供延迟处理;例如,将作业安排为在特定时间可供系统处理。 此功能实现了可靠的分布式时间计划程序。
在定义的排队时间前,计划的消息不会在队列中具体化。 在此之前,可以取消计划的消息。 取消操作会将消息删除。
可以通过两种方式使用我们的任何客户端来计划消息:
- 使用常规发送 API,但在发送之前设置消息上的
ScheduledEnqueueTimeUtc
属性。 - 使用计划消息 API,同时传递常规消息和计划时间。 API 将返回计划消息的
SequenceNumber
,以便稍后用于根据需要取消计划消息。
也可以使用消息浏览,发现计划的消息及其序列号。
只有当消息处于计划状态时,计划消息的 SequenceNumber
才有效。 当消息转换为活动状态时,消息就会被追加到队列中,就像瞬时排入队列一样,包括分配新的 SequenceNumber
。
因为此功能与各个消息绑定在一起,并且消息只能排入队列一次,所以服务总线不支持定期安排消息。
注意
- 消息入队时间并不意味着将同时发送消息。 它将加入队列,但实际发送时间取决于队列的工作负载及其状态。
- 出于性能考虑,定时消息的激活和取消是独立的操作,无需互相锁定。 如果在消息激活过程中取消消息,则不会撤回激活过程,消息仍会被激活。 此外,这可能会导致计划消息出现负计数。 为了最大程度地减少这种争用情况,建议避免在紧密连续的时间内安排激活和取消操作。
将计划消息与工作流结合使用
长期运行的业务工作流通常具有显式时间组件,例如双重身份验证具有 5 分钟超时,确认电子邮件地址的用户具有 1 小时超时,银行和保险等领域中有数天、数周或数月时间组件。
这些工作流通常在处理一些消息时启动,处理消息会存储一些状态,然后计划消息以在稍后时间继续执行进程。 通过 NServiceBus 和 MassTransit 等框架,可以更轻松地将所有这些元素集成在一起。
相关内容
若要了解有关服务总线消息传送的详细信息,请参阅以下主题: