为 oslo.messaging 提供 AMQP 1.0 实现

包含您的 Launchpad 蓝图的 URL

https://blueprints.launchpad.net/oslo.messaging/+spec/amqp10-driver-implementation

我们已经有 rabbit 和 qpid 驱动程序用于早期(且不同!)版本的 AMQP,该提案是添加一个额外的驱动程序,用于一种 _协议_,而不是特定的代理。

问题描述

允许使用不同驱动程序的目的在于为用户提供选择。但是,每个受支持的驱动程序都会增加维护负担。

通过针对清晰的协议映射,而不是特定的实现,我们将来可以简化任何希望迁移到任何其他使用 AMQP 1.0[1] 的系统的任务。那将不再需要新的驱动程序,而仅仅是不同的配置和部署。这将使 openstack 能够更轻松地利用此领域的任何新兴创新。

例如,这将允许使用 Apache Qpid 项目的 Dispatch Router。该路由器被设计为分布式路由器,而不是单进程的存储转发代理,并提供简单的可扩展性和冗余性。

提议的变更

将向 oslo.messaging 添加一个新的驱动程序(该补丁已在 https://review.openstack.org/75815 上提供以供审查)。

以下概述的映射考虑了中间情况(即驱动程序连接到消息代理或代理网络)。AMQP 1.0 允许在对等体之间直接进行通信(全部或部分),而无需使用中间体。但是,开发此方面被认为是未来的扩展(有关更详细的讨论,请参见附录 A)。

目标是支持尽可能多的已启用 1.0 的中间体,而不会过度扭曲 AMQP 1.0 协议的使用。

在 AMQP 1.0 中,代理(或类似系统)中的可寻址实体称为“节点”(例如队列或主题)。节点是消息的“接收器”或“源”,然后通常通过建立到给定节点的发送或接收“链路”来发送消息。AMQP 1.0 不提供任何标准的配置机制来动态配置这些节点(动态回复队列除外)。但是,通过允许部署特定的前缀到指定在接收和发送链接的源和目标地址中,应该可以使所需的功能在各种实现中工作,并为部署者提供相当大的灵活性。

在 RPC 中,有三种调用模式,并且将为每种模式使用不同的地址格式。

(A) 对于对特定服务器的调用,地址将由主题名称和服务器名称连接而成,并将在指定的情况下由交换机和/或部署特定的“server_request_prefix”字符串进行前缀。此前缀的默认值为“exclusive”。

(B) 对于对一组服务器中的一个的调用,地址将是主题名称,并将在指定的情况下由交换机和/或部署特定的“group_request_prefix”字符串进行前缀。此前缀的默认值为“unicast”。

(C) 对于对一组服务器中的所有服务器的调用,地址将是主题名称,由交换机指定(如果指定),并由可配置的“broadcast_prefix”字符串进行前缀。此前缀的默认值为“/broadcast/”。

因此,每种通信模式都可以通过可自定义的地址模式来识别。这允许配置代理(或等效项)以匹配驱动程序正在使用的内容,或者允许配置驱动程序以考虑给定代理的内置(且不可配置)约定。

例如,Qpid Dispatch Router 可以配置为基于默认前缀识别三种模式。任何地址以“unicast”(或“exclusive”)开头的消息都将被路由到该地址的唯一订阅者。任何地址以“broadcast”开头的消息都将被发送到该地址的所有订阅者。[有关 Qpid Dispatch Router 配置的更多详细信息,请参见附录 B,有关 qpidd 的配置,请参见附录 C。]

其他一些代理可能无法以这种方式进行配置,但可能具有围绕地址模式的内置约定。例如,所有形式为“/queues/foo”的地址都将被视为队列,发送到它们的邮件将被分配给单个消费者。形式为“/topics/bar”(或“/exchanges/bar”)的地址将被视为发布/订阅主题,消息将被分发到所有订阅者。

对于每个发送请求,将从指定的目标中推断出适当的地址。如果目标上的服务器值已指定,则表示对特定服务器的调用,并使用上述 (A) 中描述的地址格式。如果未指定服务器,则考虑 fanout 标志。如果指定了该标志,则表示对由主题标识的整个服务器组的调用,并使用 (C) 中描述的地址格式。如果既未指定服务器也未指定 fanout 标志,则表示对由主题标识的服务器组中的一个的调用,并使用 (B) 中描述的地址格式。

将维护每个目标的发送者链接的缓存,以避免在每个请求上重新创建它们所带来的开销。(另一种选择是使用单个发送链接并设置“to”字段,但并非所有中间体都支持该功能,因此这将是不必要的选择限制)。如果尚未为指定的地址创建发送者链接,则将创建一个并添加到缓存中。重新连接时将清除缓存。

将为所有发送者链接使用单个连接和会话。如果为请求指定了超时时间,则将其设置为请求上的 ttl。请求还将设置“to”字段,该字段设置为链接目标的用于目标的值。这适应了任何可能期望或更喜欢使用该寻址方式的中间体。

对于回复,每个驱动程序实例将有一个接收者链接,该链接将设置动态标志。每个请求都将设置回复地址,该地址设置为侦听器订阅的地址。每个请求都将设置消息 ID。这将回显到任何响应消息的相关 ID 中,从而将其与等待的发送请求关联。

服务器将通过创建三个接收链接来订阅消息:一个用于订阅特定于服务器的请求,一个用于订阅服务器组中的一个的请求,一个用于订阅组中所有服务器的广播请求(即,对应于上述 A、B 和 C 中描述的每种地址形式)。前两个接收链接将设置分发模式为 move,第三个将设置分发模式为 copy。(注意:AMQP 1.0 规范定义了分发模式,以确定消息如何在多个订阅者之间分发)。

RPC 的请求和响应将以预先结算的方式发送,这意味着它们不会被显式确认,因此在故障转移时可能会丢失。理想情况下,应在发布时和由侦听器接收时确认通知。(注意:目前 rabbit 驱动程序在传递时确认通知,但发布的通知未被确认。对于当前的 qpid 驱动程序,通知在发布或传递时均未被确认)。

备选方案

rabbit 驱动程序似乎是目前唯一得到充分维护的驱动程序。qpid 驱动程序在 qpid.messaging 提供的 API 和 rabbit 驱动程序使用的 API 之间存在阻抗失配。尝试为这两个截然不同的 API 使用通用架构导致 qpid 驱动程序难以理解且在映射到 AMQP 0-10 时效率低下。

添加新驱动程序的另一种选择是仅支持 rabbit 驱动程序。这减少了维护负担并为用户提供了清晰度。但是,它限制了选择。

另一种选择是选择不同的协议作为新驱动程序的基础。MQTT 专注于发布/订阅模式,并且不包含当前 oslo.messaging 语义所需的竞争消费者。STOMP 不定义可互操作的请求-响应机制。由于现有的两个驱动程序都使用早期版本的 AMQP,AMQP 支持所有必需的模式,并且 AMQP 是一种开放标准(现在已在 ISO 中标准化),因此它似乎是一个相当明显的选择。

虽然该驱动程序不被建议作为现有驱动程序的替代品(仅仅是另一种选择),但它提供了一个更大的整合途径,因为它还可以适应 ZeroMQ 采用的非中间通信风格。也许更强大的是一种混合方法,再次使用通用标准协议将是有利的。

Impact on Existing APIs

安全影响

安全影响与其它驱动程序相同。SSL 将作为一种选项来保护所需的通信流量。

性能影响

除非选择该驱动程序使用,否则不会有任何影响。总体性能将取决于选择与该驱动程序一起使用的服务器组件(与现有驱动程序不同,选择此驱动程序不会将选择限制为单个代理实现)。

初步的代码经验[2] 表明,该驱动程序与 qpidd 或 Qpid Dispatch Router 结合使用时,与现有驱动程序相比非常有利。

Configuration Impact

将通过现有的 transport_url 选项选择该驱动程序。url 中的“amqp”方案用于选择 AMQP 协议。

如果需要,将有进一步的配置选项来调整驱动程序的行为以适应不同的部署,例如上述各种前缀选项。默认值已选择以简化代理配置。通常,配置消息传递基础设施优于配置驱动程序,因为它允许更透明、集中的更改。

开发人员影响

对驱动程序 API 的任何未来更改都需要反映在另一个驱动程序中(如果添加了此驱动程序)。

实现

负责人

主要负责人

kgiusti@redhat.com

其他贡献者

gsim@redhat.com fpercoco@redhat.com

里程碑

完成目标里程碑

Juno-1

工作项

代码审查;该驱动程序已通过 Gerritt 提供以供审查一段时间。其中很多代码实际上非常通用,并且适用于超出 olso.messaging 的用途。有人建议(要求?)将其移出驱动程序并放入自己的库中。事实上,已经有一个包含此通用代码的库,pyngus,其中一项任务将是更新驱动程序补丁以将其作为依赖项。

应设置自动化测试。我提交了一个 oslo.messaging API 功能测试的补丁(我用它在开发期间测试了驱动程序)。此外,可以使用此驱动程序和适当配置的后端运行任何相关的 Tempest 测试。

我已针对 qpidd 和 Qpid dispatch router 进行测试。根据我在其他应用程序中的经验,我相信该驱动程序可以与当前的 ActiveMQ 版本(以及 ApolloMQ)一起工作。对于 RabbitMQ,目前缺乏对链接源动态标志的支持是主要问题(如果需要,可以解决)。理论上,它也应该可以与 Microsoft 的 ServiceBus 和 IIT 的 SwiftMQ 一起工作,因为它们支持所使用的协议的方面。那些可能需要更多的手动配置,目前我没有使用过,所以无法确定。

允许通过确认发布和消费者接收来可靠地传递通知。

孵化

虽然这不会添加新的(公共)模块,但让新的驱动程序成熟一段时间是谨慎的。有一个“beta”阶段会很好,感兴趣的各方可以选择该驱动程序进行测试并提供反馈。

采用

该驱动程序应可供使用 oslo.messaging 的任何服务使用

oslo.messaging

预计 API 稳定

没有 API 影响。新驱动程序选项的稳定化需要一些时间。

文档影响

最终,替代选项的可用性需要记录。

依赖项

此驱动程序依赖于 qpid-proton python 库以支持 AMQP 1.0。pyngus 可能会添加为依赖项,请参见上面的工作项目。

附录 A:关于直接通信的一些讨论

支持直接通信,即消息不通过应用程序层中间用户进程,需要通信方接受传入连接并能够确定正确的主机名和端口以连接到。

虽然可以通过约定来完成,例如使用服务器名称作为主机名并同意已知的端口,但这可能具有限制性和繁琐性。更好的方法是拥有服务器及其侦听的主机和端口的注册表。

此注册表类似于 0MQ 中使用的 matchmaker,并且适用于那里的选择也适用于此驱动程序。

但是,由于已经支持通过 n 中间体进行通信,因此可以将其用于将注册表的数据动态分发到所有通信方。这使配置简单化,并允许系统适应更改。

可以采用不同的方案。一个例子是添加一个配置选项,该选项导致服务器开始侦听特定的端口。然后,它们将通过将属性附加到发送回请求的任何回复来通告这一事实,从而直接标识它们。RPC 客户端然后可以缓存这些替代地址并在后续对同一服务器的任何请求中使用它们。在这种方法中,通信方使用消息传递中间体来定位彼此,但完成此操作后,它们会将进一步的通信“卸载”到直接连接以减少中间体的负载。

另一种方法是拥有一个特殊的“matchmaker”主题,客户端将订阅该主题,服务器将在其上通告自身。这将允许即使是第一次请求给定的服务器也能进行直接通信。

这些以及其他方案都可以以向后兼容的方式轻松完成。

附录 B:配置 Qpid Dispatch Router

使用默认前缀,如果将以下地址配置添加到 Dispatch Router 的配置文件中,将为 openstack 设置所需的语义

fixed-address {
    prefix: /unicast
    fanout: single
    bias: closest
}

fixed-address {
    prefix: /exclusive
    fanout: single
    bias: closest
}

fixed-address {
    prefix: /broadcast
    fanout: multiple
}

附录 C:配置 Qpidd

使用默认前缀,将以下选项传递给 qpidd(0.28 或更高版本)将为 openstack 设置所需的语义

--queue-patterns exclusive --queue-patterns unicast --topic-patterns broadcast

参考资料

[1] http://docs.oasis-open.org/amqp/core/v1.0/amqp-core-complete-v1.0.pdf

[2] http://people.apache.org/~gsim/oslo.messaging_scalability.pdf

注意

本作品采用知识共享署名 3.0 非移植许可协议授权。 http://creativecommons.org/licenses/by/3.0/legalcode