你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
多租户解决方案中的消息传递体系结构方法
构建由多个内部和外部服务组成的分布式应用程序时,异步消息传送和事件驱动的通信是关键资产。 设计多租户解决方案时,必须进行初步分析,以定义如何共享属于不同租户的消息或对这些消息进行分区。
共用同一个消息传送系统或事件流式处理服务可以显著降低运营成本和管理复杂性。 但是,为每个租户使用专用的消息传送系统不仅可以更好地隔离数据,降低数据泄露的风险,消除近邻干扰问题,还可以轻松地向租户收取 Azure 费用。
在本文中,你将发现消息和事件之间的区别,了解解决方案架构师在决定将哪种方法用于多租户解决方案中的消息传送或事件基础结构时可以遵循的准则。
消息、数据点和离散事件
所有消息传送系统都具有相似的功能、传输协议和使用方案。 例如,大多数新式消息传送系统支持使用易失或持久队列、AMQP 和 HTTPS 传输协议、至少一次传递等功能的异步通信。 但是,通过更仔细地观察已发布信息的类型以及客户端应用程序如何使用和处理数据,我们可以区分不同类型的消息和事件。 传递事件的服务与发送消息的系统之间有一个本质区别。 有关更多信息,请参见以下资源:
事件
事件是条件或状态更改的轻量通知。 事件可以是离散单元或一系列的一部分。 事件是一种消息,通常不传达发布者的意图,而仅做通知之用。 事件捕获事实并将其传达给其他服务或组件。 事件的使用者可以随心所欲地处理事件,无需满足发布者的任何特定期望。 我们可以将事件分为两大类:
- 离散事件包含发布应用程序已执行的特定操作的相关信息。使用事件驱动的异步通信时,应用程序会在其域内发生某些情况时发布通知事件。 一个或多个使用方应用程序需要注意此状态更改,例如产品目录应用程序中的价格变化。 使用者订阅事件以异步接收它们。 发生给定事件时,使用方应用程序可能会更新其域实体,这会导致发布更多集成事件。
- 系列事件包含信息数据点(例如用于分析的设备温度读数或一键式分析解决方案中的用户操作)作为持续不断的流中的元素。 事件流与特定上下文相关,例如现场设备报告的温度或湿度。 与同一上下文相关的所有事件都具有严格的时态顺序,这在使用事件流处理引擎处理这些事件时很重要。 若要分析包含数据点的事件流,需要将这些事件累积在跨越所需时间窗口的缓冲区中。 或者,它可以位于所选数量的项中,然后使用近实时解决方案或机器训练算法来处理这些事件。 对一系列事件的分析可能会产生信号,例如在超过阈值的时间窗口内测量的平均值。 然后,可以将这些信号作为离散的可操作事件引发。
离散事件报告状态更改,并且可操作。 事件有效负载包含有关所发生事件的信息,但一般来说,它不包含触发事件的完整数据。 例如,某个事件通知使用者,报告应用程序在存储帐户中创建了一个新文件。 事件有效负载可能包含文件的常规信息,但不包含文件本身。 离散事件非常适合于需要缩放的无服务器解决方案。
系列事件报告情况,并且可分析。 离散事件按时间排序并彼此相关。 在某些上下文中,使用者需要接收完整的事件序列,以分析所发生的情况并采取必要的操作。
消息
消息包含由将要使用的服务生成或存储在其他位置的原始数据。 消息通常包含一些信息,这些信息是接收服务以执行工作流或处理链中的步骤所必需的。 命令模式就是此类通信的一个示例。 发布者还可能期望消息的接收者执行一系列操作并使用异步消息报告其处理结果。 消息发布者和消息接收者之间存在协定。 例如,发布者发送一条包含部分原始数据的消息,并期望使用者基于这些数据创建文件且在工作完成后发送响应消息。 在其他情况下,发送者进程可以发送单向异步消息,请求另一个服务执行给定操作,但不期望从它返回确认或响应消息。
这种协定式消息处理与发布者向使用者代理的受众发布离散事件大相径庭,它对受众如何处理这些通知没有任何特定的期望。
示例方案
下面列出了消息、数据点和离散事件的一些多租户方案示例:
- 离散事件:
- 音乐共享平台跟踪特定租户中的特定用户收听特定音乐曲目的事实。
- 在在线商店 Web 应用程序中,目录应用程序使用发布者-订阅者模式向其他应用程序发送事件,以通知它们商品价格已更改。
- 一家制造公司向客户和第三方推送有关设备维护、系统运行状况和合同更新的实时信息。
- 数据点:
- 楼宇控制系统接收遥测事件,例如属于多个客户的传感器提供的温度或湿度读数。
- 事件驱动的监视系统以近乎实时的方式接收和处理来自多个服务(例如 Web 服务器)的诊断日志。
- 网页上的客户端脚本收集用户操作并将其发送到一键式分析解决方案。
- 消息:
- B2B 财务应用程序收到一条消息,用于开始处理租户的银行记录。
- 长时间运行的工作流收到一条触发下一步执行的消息。
- 在线商店应用程序的购物车应用程序通过使用持久的异步消息向订购应用程序发送 CreateOrder 命令。
关键考虑因素和要求
为解决方案选择的部署和租户模型将对安全性、性能、数据隔离、管理以及向租户交叉收取资源费用的能力产生深远影响。 此分析包括为消息传送和事件基础结构选择的模型。 在本部分中,我们将回顾在多租户解决方案中规划消息传送系统时必须做出的一些关键决策。
缩放
规划消息传送或事件基础结构时,租户的数量、消息流和事件流的复杂性、消息量、预期流量配置文件和隔离级别应作为决策驱动因素。
第一步是进行详尽的容量规划,并在吞吐量方面为消息传送系统建立必需的最大容量,以便在正常或高峰流量下正确处理预期的消息量。
某些服务产品(例如 Azure 服务总线高级层)在 CPU 和内存级别提供资源隔离,以便每个客户工作负载在隔离环境中运行。 此资源容器称为 消息传送单元。 每个高级命名空间至少会分配一个消息传送单元。 可以为每个服务总线高级命名空间购买 1、2、4、8 或 16 个消息传送单元。 单个工作负载或实体可以跨多个消息传送单元,你可以根据需要更改消息传送单元数。 这会为基于服务总线的解决方案提供可预测和稳定的性能。 有关详细信息,请参阅服务总线高级和标准消息传送层。 同样,Azure 事件中心定价层允许根据入站和出站事件的预期数量来调整命名空间的大小。 例如,高级层产品/服务按处理单位 (PU) 计费,PU 对应于底层基础结构中共享的隔离资源(CPU、内存和存储)。 每个 PU 的有效引入和流式处理吞吐量取决于以下因素:
- 生成者和使用者的数量
- 有效负载大小
- 分区计数
- 流出请求速率
- 事件中心捕获、架构注册表和其他高级功能的使用情况
有关详细信息,请参阅事件中心高级层概述。
当解决方案处理大量租户,并且你决定为每个租户采用单独的消息传送系统时,需要通过一致的策略来自动执行每个基础结构(彼此独立)的部署、监视、警报和缩放。
例如,可以在预配过程中使用基础结构即代码 (IaC) 工具(如 Terraform、Bicep 或 Azure 资源管理器 (ARM) JSON 模板以及 Azure DevOps 或 GitHub Actions 等 DevOps 系统)部署给定租户的消息传送系统。 有关详细信息,请参阅多租户解决方案的部署和配置体系结构方法。
消息传送系统可以根据每时间单位的最大消息吞吐量来调整大小。 如果系统支持动态自动缩放,则可以根据流量状况和指标自动增加或减少其容量,以满足预期的服务级别协议。
性能可预测性和可靠性
在为有限数量的租户设计和构建消息传送系统时,使用单个消息传送系统可能是满足功能需求(在吞吐量方面)的绝佳解决方案,它可以降低总拥有成本。 多租户应用程序可以共享相同的消息传送实体,例如跨多个客户的队列和主题。 或者,它们可以为每个租户使用一组专用的组件,以提高租户隔离度。 另一方面,在多个租户之间共享相同的消息传送基础结构可能会使整个解决方案面临近邻干扰问题。 一个租户的活动可能会损害其他租户的性能和可操作性。
在这种情况下,应适当调整消息传送系统的大小,以在高峰时间维持预期的流量负载。 理想情况下,它应支持自动缩放。 消息传送系统应在流量增加时动态横向扩展,在流量减少时动态横向缩减。 为每个租户使用专用的消息传送系统也可以缓解近邻干扰风险,但管理大量消息传送系统可能会增加解决方案的复杂性。
多租户应用程序最终可以采用混合方法,其中核心服务在单个共享消息传送系统中使用相同的队列和主题集,以实现内部异步通信。 相反,其他服务可以为每个租户采用一组专用的消息传送实体,甚至采用一个专用的消息传送系统,以缓解近邻干扰问题并保证数据隔离。
将消息传送系统部署到虚拟机时,应将这些虚拟机与计算资源放在一起,以减少网络延迟。 这些虚拟机不应与其他服务或应用程序共享,以避免消息传送基础结构与其他系统争用系统资源,例如 CPU、内存和网络带宽。 虚拟机还可以分布在可用性区域中,以提高业务关键型工作负载(包括消息传送系统)的区域内复原能力和可靠性。 假设你决定在虚拟机上托管一个消息传送系统,例如 RabbitMQ 或 Apache ActiveMQ。 在这种情况下,建议将其部署到虚拟机规模集,并根据 CPU 或内存等指标对其进行配置,以实现自动缩放。 这样,托管消息传送系统的代理节点的数量将根据流量状况适当地横向扩展和缩减。 有关详细信息,请参阅 Azure 虚拟机规模集的自动缩放概述。
同样,在 Kubernetes 群集上托管消息传送系统时,请考虑使用节点选择器和排斥来安排其 Pod 在专用节点池上的执行,而不是与其他工作负载共享,从而避免近邻干扰问题。 将消息传送系统部署到区域冗余 AKS 群集时,可使用 Pod 拓扑分布约束来控制 Pod 在 AKS 群集中的故障域(如区域、可用性区域和节点)之间的分布方式。 在 AKS 上托管第三方消息传送系统时,可使用 Kubernetes 自动缩放在流量增加时动态横向扩展工作器节点的数量。 借助水平 Pod 自动缩放程序和 AKS 群集自动缩放程序,节点和 Pod 卷会动态实时调整,以匹配流量状况,避免因容量问题而导致停机时间。 有关详细信息,请参阅自动缩放群集以满足 Azure Kubernetes 服务 (AKS) 中的应用程序需求。 若要根据给定队列的长度横向扩展 Kubernetes 托管的消息传送系统(例如 RabbitMQ 或 Apache ActiveMQ)的 Pod 数量,请考虑使用 Kubernetes 事件驱动的自动缩放 (KEDA)。 借助 KEDA,可以基于需要处理的事件数来驱动 Kubernetes 中任何容器的缩放。 例如,可以使用 KEDA 根据 Azure 服务总线队列、RabbitMQ 队列或 ActiveMQ 队列的长度来缩放应用程序。 有关 KEDA 的详细信息,请参阅 Kubernetes 事件驱动的自动缩放。
隔离
为多租户解决方案设计消息传送系统时,应考虑到不同类型的应用程序需要不同类型的隔离,这取决于租户的性能、隐私和审核要求。
- 多个租户可以在同一个消息传送系统中共享相同的消息传送实体,例如队列、主题和订阅。 例如,多租户应用程序可以从单个 Azure 服务总线队列接收包含多个租户的数据的消息。 此解决方案可能会导致性能和可伸缩性问题。 如果某些租户生成的订单量比其他租户大,这可能会导致消息积压。 此问题也称为近邻干扰问题,可能会在延迟方面降低某些租户的服务级别协议。 如果使用者应用程序按严格的时间顺序从队列中读取和处理消息(一次一个),并且处理消息所需的时间取决于有效负载中的项数,则问题会更加明显。 当跨多个租户共享一个或多个队列资源时,消息传送基础结构和处理系统应能够适应基于规模和吞吐量要求的预期流量。 这种体系结构方法非常适合以下场景:多租户解决方案采用单个工作进程池,以便为所有租户接收、处理和发送消息。
- 多个租户共享相同的消息传送系统,但使用不同的主题或队列资源。 这种方法可以提供更好的隔离和数据保护,但成本更高、操作更复杂、灵活性更低,因为系统管理员必须预配、监视和维护更多数量的队列资源。 此解决方案可确保所有租户在性能和服务级别协议方面获得一致且可预测的体验,因为它可以防止任何租户造成可能影响其他租户的瓶颈。
- 每个租户使用一个单独的专用消息传送系统。 例如,多租户解决方案可以为每个租户使用不同的 Azure 服务总线命名空间。 此解决方案提供最大程度的隔离,但复杂性和运营成本较高。 这种方法可保证稳定的性能,并允许根据特定的租户需求微调消息传送系统。 当新租户加入时,除了完全专用的消息传送系统外,预配应用程序可能还需要创建一个单独的托管标识或具有适当 RBAC 权限的服务主体,以访问新创建的命名空间。 例如,当 Kubernetes 群集由同一 SaaS 应用程序的多个实例(每个租户一个实例)共享,并且每个实例在单独的命名空间中运行时,每个应用程序实例可以使用不同的服务主体或托管标识来访问专用消息传送系统。 在这种情况下,消息传送系统可以是完全托管的 PaaS 服务,例如 Azure 服务总线命名空间,也可以是 Kubernetes 托管的消息传送系统,例如 RabbitMQ。 从系统中删除租户时,应用程序应删除消息传送系统和之前为租户创建的所有专用资源。 这种方法的主要缺点是增加了操作复杂性并降低了灵活性,尤其是当租户数量随时间推移而增长时。
查看以下附加注意事项和意见:
- 如果租户需要使用自己的密钥来加密和解密消息,多租户解决方案应提供为每个租户采用单独的 Azure 服务总线高级命名空间的选项。 有关详细信息,请参阅配置客户管理的密钥以加密静态 Azure 服务总线数据。
- 如果租户需要高级别的复原能力和业务连续性,多租户解决方案应提供预配服务总线高级命名空间(启用了异地灾难恢复和可用性区域)的功能。 有关详细信息,请参阅 Azure 服务总线异地灾难恢复。
- 为每个租户使用单独的队列资源或专用消息传送系统时,合理的做法是为每个租户采用单独的工作进程池,以提高数据隔离级别,降低处理多个消息传送实体的复杂性。 处理系统的每个实例都可以采用不同的凭据,例如连接字符串、服务主体或托管标识,以便访问专用消息传送系统。 这种方法提供更好的安全级别和租户之间的隔离,但它会增加标识管理的复杂性。
同样,事件驱动的应用程序可以提供不同级别的隔离:
- 事件驱动的应用程序通过单个共享 Azure 事件中心实例接收来自多个租户的事件。 此解决方案提供高级别的灵活性,因为加入新租户不需要创建专用的事件引入资源,但它提供的数据保护级别较低,因为它将来自多个租户的消息混合在同一数据结构中。
- 当某个租户的事件不得与其他租户的事件混合,以实现安全和数据保护时,该租户可以选择专用的事件中心或 Kafka 主题,以避免近邻干扰问题并满足其数据隔离要求。
- 如果租户需要高级别的复原能力和业务连续性,多租户解决方案应提供预配事件中心高级命名空间(启用了异地灾难恢复和可用性区域)的功能。 有关详细信息,请参阅 Azure 事件中心 - 异地灾难恢复。
- 出于法规遵从性原因和义务,应为需要将事件存档到 Azure 存储帐户的客户创建启用了事件中心捕获的专用事件中心。 有关详细信息,请参阅通过 Azure Blob 存储或 Azure Data Lake Storage 中的 Azure 事件中心来捕获事件。
- 单个多租户应用程序可以通过 Azure 事件网格向多个内部和外部系统发送通知。 在这种情况下,应为每个使用者应用程序创建单独的事件网格订阅。 如果应用程序使用多个事件网格实例向大量外部组织发送通知,请考虑使用事件域(例如每个租户一个事件域),它允许应用程序将事件发布到数千个主题。 有关详细信息,请参阅了解用于管理事件网格主题的事件域。
实现的复杂性
设计多租户解决方案时,必须考虑系统在中长期内将如何发展,以防止其随着时间推移而愈加复杂,直到有必要重新设计部分或整个解决方案。 这种重新设计将帮助你应对越来越多的租户。 设计消息传送系统时,应考虑未来几年消息量和租户的预期增长,然后创建一个可以横向扩展的系统,以跟上预测的流量并降低预配、监视和维护等操作的复杂性。
每当预配或取消预配租户应用程序时,解决方案都应自动创建或删除必要的消息传送实体,而无需手动操作。
多租户数据解决方案的一个特别关注点是你支持的自定义级别。 每当部署单租户或多租户应用程序时,应用程序预配系统(如 DevOps 系统)都应自动配置和应用所有自定义项,这些自定义项基于一组初始参数。
管理和操作的复杂性
从一开始就计划好如何操作、监视和维护消息传送和事件基础结构,以及多租户方法如何影响操作和流程。 例如,请考虑以下可能性:
- 你可能计划将应用程序使用的消息传送系统托管在一组专用虚拟机中,每个租户一个系统。 如果是这样,你打算如何部署、升级、监视和横向扩展这些系统?
- 同样,如果将消息传送系统容器化并托管在共享 Kubernetes 群集中,你打算如何部署、升级、监视和横向扩展各个消息传送系统?
- 假设你在多个租户之间共享一个消息传送系统。 你的解决方案如何收集和报告每个租户的使用指标或限制每个租户可以发送或接收的消息数量?
- 当消息传送系统使用 PaaS 服务(如 Azure 服务总线)时,应提出以下问题:
- 如何根据租户请求的功能和隔离级别(共享或专用)自定义每个租户的定价层?
- 如何按租户创建托管标识和 Azure 角色分配,以仅将适当的权限分配给租户可以访问的消息传送实体(如队列、主题和订阅)? 有关详细信息,请参阅使用 Microsoft Entra ID 对托管标识进行身份验证以访问 Azure 服务总线资源。
- 如果租户需要迁移到不同类型的消息传送服务、不同的部署或其他区域,应如何迁移?
成本
通常,部署基础结构的租户密度越高,预配该基础结构的成本就越低。 但是,共享基础结构会增加出现近邻干扰问题等问题的可能性,因此请仔细权衡利弊。
要考虑的方法和模式
Azure 体系结构中心的几种云设计模式可以应用于多租户消息传送系统。 你可以选择始终遵循一种或多种模式,也可以根据需要考虑混合和匹配模式。 例如,对于大多数租户,可以使用多租户服务总线命名空间,但对于在隔离和性能方面支付更多费用或有更高要求的租户,则可以部署专用的单租户服务总线命名空间。 同样,使用部署缩放单元进行缩放通常是一种很好的做法,即使在缩放单元中使用多租户服务总线命名空间或专用命名空间时也是如此。
部署戳模式
有关部署缩放单元模式和多租户的详细信息,请参阅多租户体系结构方法的“部署缩放单元模式”部分。 有关租户模型的详细信息,请参阅可考虑用于多租户解决方案的租户模型。
共享消息传送系统
可以考虑部署一个共享消息传送系统(如 Azure 服务总线),并在所有租户之间共享。
此方法为基础结构提供最高密度的租户,因此降低了整体的总拥有成本。 它通常还会减少管理开销,因为只需管理和保护一个消息传送系统或资源。
但是,当你跨多个租户共享资源或整个基础结构时,请考虑以下注意事项:
- 始终牢记并考虑相关资源的约束、缩放能力、配额和限制。 例如,当体系结构发展到支持更多租户时,Azure 订阅中的服务总线命名空间的最大数量、单个命名空间中的事件中心的最大数量或最大吞吐量限制最终可能会成为硬性规定。 仔细考虑每个 Azure 订阅的命名空间数或每个命名空间的队列数需要达到的最大规模。 然后在选择此模式之前,将当前和未来的估计值与所选消息传送系统的现有配额和限制进行比较。
- 如以上部分所述,当你在多个租户之间共享资源时,近邻干扰问题可能会成为一个因素,尤其是在某些租户特别繁忙或它们生成的流量高于其他租户时。 在这种情况下,请考虑应用限制模式或速率限制模式来缓解这些影响。 例如,可以限制单个租户在单位时间内可以发送或接收的最大消息数。
- 对于单个租户,可能难以监视活动和测量资源消耗。 某些服务(如 Azure 服务总线)对消息传送操作收费。 因此,在多个租户之间共享命名空间时,应用程序应能够跟踪代表每个租户完成的消息传送操作数及其退款成本。 其他服务不提供相同级别的详细信息。
不同租户可能对安全性、区域内复原能力、灾难恢复或位置有不同的要求。 如果这些要求与消息传送系统配置不匹配,则可能无法通过单个资源满足它们。
分片模式
分片模式涉及部署多个消息传送系统(称为分片),其中包含一个或多个租户的消息传送实体,例如队列和主题。 与部署缩放单元不同,分片并不意味着整个基础结构都是重复的。 你可以对消息传送系统进行分片,而不对解决方案中的其他基础结构进行复制或分片。
每个消息传送系统或分片在可靠性、SKU 和位置方面可以有不同的特征。 例如,可以根据租户的位置或其性能、可靠性、数据保护或业务连续性需求,将租户跨具有不同特征的多个消息传送系统分片。
使用分片模式时,需要使用分片策略,以便将给定租户映射到包含其队列的消息传送系统。 查找策略根据租户名称或 ID 使用映射来区分目标消息传送系统。 多个租户可以共享同一分片,但单个租户使用的消息传送实体不会分布在多个分片中。 可以使用单个字典来实现映射,该字典将租户的名称映射到目标消息传送系统的名称或引用。 映射可以存储在可供多租户应用程序的所有实例访问的分布式缓存中,也可以存储在持久存储中,例如关系数据库中的表或存储帐户中的表。
分片模式可以扩展到大量租户。 此外,根据工作负载,你可能能够对分片实现较高的租户密度,因此成本可能会很有吸引力。 分片模式还可用于解决 Azure 订阅和服务配额、限制和约束问题。
为每个租户提供专用消息传送系统的多租户应用
另一种常见的方法是部署单个多租户应用程序,其中每个租户都有专用的消息传送系统。 在此租户模型中,你拥有一些共享组件(如计算资源),而其他服务则使用单租户专用部署方法进行预配和管理。 例如,可以构建单个应用层,然后为每个租户部署单独的消息传送系统,如下图所示。
如果已确定系统上的大部分负载是由于可为每个租户单独部署的特定组件,水平分区部署有助于缓解近邻干扰问题。 例如,可能需要为每个租户使用单独的消息传送或事件流式处理系统,因为单个实例无法应付多个租户生成的流量。 为每个租户使用专用的消息传送系统时,如果单个租户导致大量消息或事件,这可能会影响共享组件,但不会影响其他租户的消息传送系统。
因为会为每个租户预配专用资源,所以此方法的成本可能高于共享托管模型。 另一方面,采用此租户模型时,更容易向使用专用系统的唯一租户收取资源费用。 这种方法允许你为其他服务(例如计算资源)实现高密度,并降低这些组件的成本。
对于水平分区部署,需要采用自动化流程来部署和管理多租户应用程序的服务,尤其是单个租户使用的服务。
地理节点模式
地理节点模式涉及将一组后端服务部署到一组地理节点中。 每个地理节点都可以为任何区域的任何客户端处理任何请求。 此模式允许你以主动-主动方式处理请求,通过在全球分布请求处理来改善延迟和提高可用性。
Azure 服务总线和 Azure 事件中心支持跨主要和次要灾难恢复命名空间以及不同区域和可用性区域进行元数据灾难恢复,以便为最佳区域内复原能力提供支持。 此外,Azure 服务总线和 Azure 事件中心实现了一组联合模式,这些模式主动复制相同的逻辑主题、队列或事件中心,以便在最终位于不同区域的多个命名空间中可用。 有关更多信息,请参见以下资源:
作者
本文由 Microsoft 维护, 它最初是由以下贡献者撰写的。
主要作者:
- Paolo Salvatori | FastTrack for Azure 首席客户工程师
其他参与者:
- John Downs | 首席软件工程师
- Clemens Vasters | 消息传送服务和标准首席架构师
- 阿森·弗拉基米尔斯基|首席工程师,FastTrack for Azure
后续步骤
有关消息传送设计模式的详细信息,请参阅以下资源: