本文介绍不同类型的消息以及参与消息传送基础结构的实体。 根据每种消息类型的要求,本文建议使用 Azure 消息传送服务。 选项包括 Azure 服务总线消息传送、Azure 事件网格和 Azure 事件中心。 有关产品比较,请参阅 比较消息服务。
在体系结构级别,消息是由实体(生成者)创建的数据报,用于分发信息,以便其他实体(使用者)能够了解并采取相应措施。 生成者和使用者可以直接通信,或选择通过中间实体(消息代理)通信。 本文重点介绍使用消息代理的异步消息传送。
我们可以将消息分为两种主要类别。 如果生成者希望使用者执行操作,则该消息就是命令。 如果消息通知使用者发生了某项操作,则该消息就是事件。
命令
生成者发送一个命令,意图是使用者将在业务事务范围内执行操作。
命令是高价值的消息,必须至少传递一次。 如果命令丢失,整个业务事务可能会失败。 此外,不应多次处理命令。 这样做可能会导致错误的事务。 客户可能会收到重复订单或计费两次。
命令通常用于管理多步骤业务事务的工作流。 根据业务逻辑,生成者可能期望使用者确认消息并报告操作结果。 根据该结果,生成者可以选择适当的操作过程。
事件
事件是生成者为宣布事实而引发的一种消息。
生成者(在此上下文中称为发布者)对事件产生的操作没有预期。
感兴趣的使用者可以订阅、侦听事件并根据其消耗方案采取措施。 事件可以有多个订阅者或根本没有订阅者。 两个不同的订阅者可以用不同的操作对事件做出反应,并且互不知晓。
生成者和使用者松散耦合、独立管理。 生成者不要求使用者向生成者确认事件。 不再对事件感兴趣的使用者可以取消订阅,这将从管道中删除使用者,且不会影响生成者或系统的整体功能。
事件分为两类:
生成者引发事件以宣布离散事实。 常见的用例是事件通知。 例如,Azure 资源管理器在创建、修改或删除资源时引发事件。 这些事件的订阅者可能是发送警报电子邮件的逻辑应用。
生成者在一段时间内按顺序或事件流引发相关事件。 通常,流用于统计评估。 评估可以在临时窗口内进行,也可以在事件到达时进行。 遥测是一种常见用例(例如系统的运行状况和负载监视)。 另一个用例是来自 IoT 设备的事件流式处理。
实现事件消息传送的常见模式是发布者-订阅者模式。
消息代理的作用和好处
中间消息代理提供将消息从生成者转移到使用者的功能,并能提供其他优势。
分离
消息代理以分别生成和使用消息的逻辑将生成者与使用者分离。 在复杂的工作流中,代理可以促使业务操作分离,帮助协调工作流。
例如,单个业务事务需要按业务逻辑序列执行的不同操作。 生成者发出命令,通知使用者开始操作。 使用者在为排列生成者响应而保留的单独队列中确认消息。 只有在收到响应后,生成者才会发送新消息,开始序列中的下一个操作。 另一个使用者处理该消息并将完成消息发送到响应队列。 通过使用消息传送,服务会在它们之间协调事务工作流。
消息代理提供临时分离。 生成者和使用者不必同时运行。 无论使用者是否可用,生成者都可以向消息代理发送消息。 反过来,使用者也不受生成者可用性的限制。
例如,Web 应用的用户界面生成消息并使用队列作为消息代理。 使用者准备就绪后,可以从队列中检索消息并执行工作。 临时分离有助于用户界面保持响应。 它不会在系统异步处理消息时受阻。
某些操作可能需要很长时间才能完成。 发出命令后,生成者不必等待使用者完成该命令。 消息代理可帮助异步处理消息。
负载均衡
生成者可能会发布由许多使用者处理的大量消息。 可使用消息代理在服务器之间分配处理并提高吞吐量。 使用者可以在不同的服务器上运行以分散负载。 可在需要时动态添加使用者以横向扩展系统,也可以在不需要时删除。
使用者竞争模式说明了如何同时处理多个消息,以优化吞吐量、改进可伸缩性和可用性,以及均衡工作负载。
负载分级
生成者或一组生成者生成的消息量是可变的。 有时,可能会有大量消息导致出现消息高峰。 不必添加使用者来处理这项工作。消息代理可以充当缓冲区,使用者可以按照自己的节奏逐渐排出消息,而不会给系统带来压力。
基于队列的负载均衡模式提供了更多信息。
可靠消息传送
消息代理有助于确保即使生成者和使用者之间的通信失败,消息也不会丢失。 生成者可以将消息发布到消息代理,而使用者可以在重新建立通信时检索它们。 生成者不会被阻止,除非它与消息代理的连接断开。
可复原的消息传送
消息代理可以提高系统中使用者的复原能力。 如果使用者在处理消息时失败,使用者的另一个实例可以处理该消息。 由于消息保留在代理中,因此可以重新处理。
消息代理的技术选择
Azure 提供了多个消息代理服务,每个服务都有一系列功能。 在选择服务之前,应确定消息的意图和要求。
Azure 服务总线消息传递
Azure 服务总线消息传送队列非常适合将命令从生成者传输到使用者。 以下是一些注意事项。
拉取模型
服务总线队列的使用者不断轮询服务总线,以检查是否有新消息。 客户端 SDK 和用于服务总线的 Azure Functions 触发器可抽象化该模型。 当有新消息时,系统会调用使用者的回调,并将消息发送给使用者。
确保送达
服务总线允许使用者快速查看队列并锁定来自其他使用者的消息。
使用者负责报告消息的处理状态。 仅当使用者将消息标记为已使用时,服务总线才会从队列中删除该消息。 如果发生故障、超时或崩溃,服务总线会解锁消息,以便其他使用者可以检索它。 这样,消息就不会在传输过程中丢失。
生成者可能会不小心将相同的消息发送两次。 例如,生成者实例在发送消息后失败。 另一个生成者取代原始实例并再次发送消息。 Azure 服务总线队列提供内置的重复数据删除功能,用于检测和删除重复消息。 即便如此,仍有将消息传递两次的可能。 例如,如果使用者在处理过程中失败,消息将返回到队列并由同一个或另一个使用者检索。 使用者中的消息处理逻辑应是幂等的,这样即使重复工作,系统的状态也不会改变。
消息排序
如果你希望使用者按发送顺序获取消息,服务总线队列可通过会话保证先进先出 (FIFO) 有序传递。 一个会话可以有一个或多个消息。 消息与 SessionId 属性相关。 会话中的消息永不过期。 可以锁定会话使用者,以防止会话消息被其他使用者处理。
有关详细信息,请参阅消息会话。
消息持久性
服务总线队列支持临时分离。 即使使用者不可用或无法处理消息,它也会保留在队列中。
为长时间运行的事务设置检查点
业务事务可以长时间运行。 事务中的每个操作都可以有多个消息。 可使用检查点来协调工作流并在事务失败时提供复原能力。
服务总线队列允许通过会话状态功能来设置检查点。 状态信息以增量方式记录在属于会话的消息的队列 (SetState) 中。 例如,使用者可以通过不时检查状态 (GetState) 来跟踪进度。 如果一个使用者失败,另一个使用者可以使用状态信息来确定最后一个已知的检查点,以恢复会话。
死信队列 (DLQ)
服务总线队列有一个默认子队列,称为死信队列 (DLQ),用于保存无法传递或处理的消息。 服务总线或使用者中的消息处理逻辑可以将消息添加到 DLQ。 DLQ 会保留消息,直到系统从队列中检索这些消息。
以下是消息可能最终出现在 DLQ 中的示例:
有害消息是由于格式错误或包含意外信息而无法处理的消息。 在服务总线队列中,可以通过设置队列的 MaxDeliveryCount 属性来检测有害消息。 如果接收到相同消息的次数超过该属性值,服务总线会将该消息移至 DLQ。
如果消息在一段时间内未得到处理,它可能不再有用。 服务总线队列允许生成者发布具有生存时间属性的消息。 如果未在此期限内收到消息,则将消息放入 DLQ。
检查 DLQ 中的消息以确定失败的原因。
混合解决方案
服务总线桥接本地系统和云解决方案。 由于防火墙限制,本地系统通常难以访问。 生成者和使用者(可以是本地或云)都可以使用服务总线队列终结点作为消息的取放位置。
消息传送桥模式是处理这些方案的另一种方法。
主题和订阅
服务总线通过服务总线主题和订阅支持发布者-订阅者模式。
此功能为生成者提供了一种向多个使用者广播消息的方法。 当某个主题收到消息时,它会转发给所有已订阅的使用者。 (可选)订阅可以具有筛选条件,让使用者能够获取消息子集。 每个使用者以与队列类似的方式从订阅中检索消息。
有关详细信息,请参阅 Azure 服务总线主题。
Azure 事件网格
对于离散事件,建议使用 Azure 事件网格。 事件网格遵循发布者-订阅者模式。 当事件源触发事件时,它们会发布到事件网格主题。 这些事件的使用者通过指定事件类型以及将处理事件的事件处理程序,来创建事件网格订阅。 如果没有订阅者,则放弃事件。 每个事件可以有多个订阅。
推送模型
事件网格通过推送模型将消息传播给订阅者。 假设你有一个包含 Webhook 的事件网格订阅。 当新事件到达时,事件网格会将事件发布到 Webhook 终结点。
与 Azure 集成
如果要获取有关 Azure 资源的通知,请选择事件网格。 许多 Azure 服务充当具有内置事件网格主题的事件源。 事件网格还支持可配置为事件处理程序的各种 Azure 服务。 你可以轻松订阅这些主题,以将事件路由到所选的事件处理程序。 例如,你可以使用事件网格在创建或删除 Blob 存储时调用 Azure 函数。
自定义主题
如果要从应用程序或未与事件网格集成的 Azure 服务发送事件,请创建自定义事件网格主题。
例如,若要查看整个业务事务的进度,需要参与的服务在处理各自的业务操作时引发事件。 Web 应用可显示这些事件。 要完成此任务,一种方法是创建自定义主题,并使用通过 HTTP WebHook 注册的 Web 应用添加订阅。 随着业务服务将事件发送到自定义主题,事件网格会将事件推送到 Web 应用。
筛选的事件
你可以在订阅中指定筛选器,以指示事件网格仅将事件子集路由到特定事件处理程序。 在订阅架构中指定筛选器。 任何发送到主题且其值与筛选器匹配的事件都会自动转发到该订阅。
例如,将各种格式的内容上传到 Blob 存储。 每次添加文件时,都会引发事件并将其发布到事件网格。 事件订阅可能有一个筛选器,该筛选器仅发送图像事件,以便事件处理程序可以生成缩略图。
有关筛选的详细信息,请参阅筛选事件网格的事件。
高吞吐量
事件网格在每个区域每秒可以路由 10,000,000 个事件。 每月前 100,000 个操作是免费的。 有关成本方面的考虑,请参阅事件网格的费用是多少?
可复原的传递
尽管事件的成功传递不如命令重要,但你可能仍想得到一些保证,具体取决于事件的类型。 事件网格提供你可以启用和自定义的功能,例如重试策略、到期时间和死信。 有关详细信息,请参阅事件网格消息传递和重试。
事件网格的重试过程有助于提高复原能力,但它不防故障。 在重试过程中,如果终结点长时间无响应,事件网格可能会多次传递消息、跳过或延迟某些重试操作。 有关详细信息,请参阅重试计划。
你可以通过启用死信将未传递的事件持久保存到 Blob 存储帐户。 将消息传递到 Blob 存储终结点时存在延迟,如果该终结点无响应,事件网格会放弃该事件。 有关详细信息,请参阅设置死信位置和重试策略。
Azure 事件中心
使用事件流时,建议使用 Azure 事件中心作为消息代理。 从本质上讲,它是一个能够以低延迟接收大量数据的大型缓冲区。 接收到的数据可以通过并发操作快速读取。 你可以使用任何实时分析提供程序转换接收到的数据。 事件中心还提供将事件存储在存储帐户中的功能。
快速引入
事件中心每秒可以引入数百万个事件。 事件仅追加到流中并按时间排序。
拉取模型
与事件网格一样,事件中心也提供发布者-订阅者功能。 事件网格与事件中心之间的主要区别在于向订阅者提供事件数据的方式。 事件网格将引入的数据推送给订阅者,而事件中心通过拉取模型提供数据。 收到事件后,事件中心会将其追加到流中。 订阅者管理其游标,并且可以在流中前后移动,选择时间偏移量,以及按自己的速度重播序列。
流处理器是从事件中心拉取数据以进行转换和统计分析的订阅者。 可使用 Azure 流分析和 Apache Spark 进行复杂处理,例如随时间窗口聚合或异常情况检测。
如果要对每个分区的每个事件执行操作,可以使用事件处理主机或使用内置连接器(如 Azure 逻辑应用)拉取数据,以提供转换逻辑。 另一种方法是使用 Azure Functions。
分区
分区是事件流的一部分。 事件使用分区键进行划分。 例如,多个 IoT 设备将设备数据发送到事件中心。 分区键是设备标识符。 引入事件时,事件中心会将其移至不同的分区。 在每个分区内,所有事件都按时间排序。
使用者是处理事件数据的代码实例。 事件中心遵循分区使用者模式。 每个使用者只读取一个特定的分区。 拥有多个分区可以加快处理速度,因为流可以由多个使用者同时读取。
同一使用者的实例构成一个使用者组。 多个使用者组可以以不同的意图读取同一个流。 假设事件流包含来自温度传感器的数据。 一个使用者组可以读取流来检测异常,例如温度峰值。 另一个使用者组可以读取相同的流来计算临时窗口中的移动平均温度。
事件中心通过允许多个使用者组来支持发布者-订阅者模式。 每个使用者组都是订阅者。
有关事件中心分区的详细信息,请参阅分区。
事件中心捕获
使用捕获功能可将事件流存储到 Azure Blob 存储或 Data Lake Storage。 这种事件存储方式非常可靠,因为即使存储帐户不可用,捕获功能也会将数据保留一段时间,然后在存储帐户可用后将数据写入存储。
存储服务还可以提供用于分析事件的附加功能。 例如,通过利用 Blob 存储帐户的访问层,可以将事件存储在用于需要频繁访问的数据的热层中。 你可以使用这些数据实现可视化效果。 或者,可以将数据存储在存档层中,并偶尔检索数据以进行审核。
捕获功能可存储事件中心引入的所有事件,对于批处理很有用。 你可以使用 MapReduce 函数生成数据报告。 捕获的数据还可以充当事实来源。 如果在聚合数据时遗漏了某些事实,则可以参考捕获的数据。
有关此功能的详细信息,请参阅通过 Azure Blob 存储或 Azure Data Lake Storage 中的 Azure 事件中心来捕获事件。
支持 Apache Kafka 客户端
事件中心为 Apache Kafka 客户端提供终结点。 现有客户端可以更新其配置以指向该终结点,并开始将事件发送到事件中心。 不需要进行任何代码更改。
有关详细信息,请参阅适用于 Apache Kafka 的事件中心。
交叉方案
在某些情况下,结合使用两种消息传送服务非常有利。
结合使用服务可以提高消息传送系统的效率。 例如,在业务事务中使用 Azure 服务总线队列来处理消息。 大部分时间闲置、偶尔接收消息的队列效率低下,因为使用者会不断轮询队列以获取新消息。 你可以使用 Azure 函数作为事件处理程序来设置事件网格订阅。 每当队列收到消息并且没有使用者在侦听时,事件网格都会发送一个通知,该通知会调用用于排出队列的 Azure 函数。
有关将服务总线连接到事件网格的详细信息,请参阅 Azure 服务总线到事件网格的集成概述。
使用消息队列和事件的企业集成参考体系结构展示了如何实现服务总线到事件网格的集成。
另举一例:事件网格收到一组事件,其中一些事件需要工作流,而其他事件用于通知。 消息元数据指示了事件的类型。 要区分这些事件,一种方法是使用事件订阅中的筛选功能检查元数据。 如果事件需要工作流,事件网格会将其发送到 Azure 服务总线队列。 该队列的接收者可以采取必要的操作。 通知事件将发送到逻辑应用,以发送警报电子邮件。
相关模式
实现异步消息传送时,请考虑以下模式:
- 使用者竞争模式。 多个使用者可能需要竞争才能从队列中读取消息。 此模式说明了如何同时处理多个消息,以优化吞吐量、改进可伸缩性和可用性,以及均衡工作负载。
- 优先级队列模式。 对于业务逻辑要求优先处理某些消息的情况,此模式描述了由生成者发布的具有较高优先级的消息如何比具有较低优先级的消息更快地被使用者接收和处理。
- 基于队列的负载调节模式。 此模式使用消息代理充当生成者和使用者之间的缓冲区,以帮助最大程度地减少对这两个实体的间歇性高负载的可用性和响应能力的影响。
- 重试模式。 生成者或使用者可能无法连接到队列,但这种失败的原因可能是暂时的,很快就会过去。 此模式描述如何处理这种情况,以提高应用程序的复原能力。
- 计划程序代理监督程序模式。 消息传送通常用作工作流实现的一部分。 此模式演示消息传送如何跨分布式服务集和其他远程资源协调一组操作,并使系统能够恢复和重试失败的操作。
- 协调模式。 此模式介绍服务如何使用消息传送来控制业务事务的工作流。
- 认领凭证模式。 此模式介绍如何将大型消息拆分为声明检查和有效负载。