RPC 文档和命名空间

https://blueprints.launchpad.net/neutron/+spec/rpc-docs-and-namespaces

该蓝图旨在增强现有 oslo.messaging 的使用,使版本方案对开发人员来说更加清晰。 许多人表示当前代码难以理解,因此该规范旨在改进这一点。

问题描述

Neutron 使用 oslo.messaging 库在 Neutron 服务之间提供内部通信通道。 此通信通常通过 AMQP 进行,但这些细节大多被 oslo.messaging 隐藏,并且将来可以使用其他协议。

RPC API 在 Neutron 中定义为两部分:客户端和服务器端。

客户端

这是一个 rpc 客户端定义的示例

from oslo import messaging

from neutron.common import rpc as n_rpc


class ClientAPI(object):
    """Client side RPC interface definition.

    API version history:
        1.0 - Initial version
        1.1 - Added my_remote_method_2
    """

    def __init__(self, topic):
        target = messaging.Target(topic=topic, version='1.0')
        self.client = n_rpc.get_client(target)

    def my_remote_method(self, context, arg1, arg2):
        cctxt = self.client.prepare()
        return cctxt.call(context, 'my_remote_method', arg1=arg1, arg2=arg2)

    def my_remote_method_2(self, context, arg1):
        cctxt = self.client.prepare(version='1.1')
        return cctxt.call(context, 'my_remote_method_2', arg1=arg1)

此类定义了 rpc API 的客户端接口。 该接口有两个方法。 第一个方法存在于接口版本 1.0 中。 第二个方法是在版本 1.1 中添加的。 当调用较新的方法时,它指定远程端必须实现至少版本 1.1 才能处理此请求。

服务器端

rpc 接口的服务器端如下所示

from oslo import messaging


class ServerAPI(object):

    target = messaging.Target(version='1.1')

    def my_remote_method(self, context, arg1, arg2):
        return 'foo'

    def my_remote_method_2(self, context, arg1):
        return 'bar'

此类实现了接口的服务器端。 定义的 messaging.Target() 表示该类当前实现接口版本 1.1。

版本控制

请注意,对 rpc 接口的更改必须始终以向后兼容的方式进行。 服务器端应始终能够处理较旧的客户端(在同一主要版本系列中,例如 1.X)。

可以升级主要版本号并删除仅用于向后兼容的代码。 有关如何执行此操作的更多信息,请参阅 https://wiki.openstack.org/wiki/RpcMajorVersionUpdates

每个版本号流都与一个目标相关联。 在客户端,您设置一个目标来标识方法调用的目标。 在该目标上公开的每个方法都是 oslo.messaging 视角的单个 API,无论另一侧的实现细节如何。

不幸的是,在今天的 Neutron 代码中,它是不一致的,并且通常很难确定哪些被认为是相同版本流的一部分,哪些是独立的。

一个演示示例:DHCP Agent

定义了一个 RPC 接口,允许 Neutron 插件远程调用 DHCP agent 中的方法。 客户端在 neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.DhcpAgentNotifyApi 中定义。 此接口的服务器端在 DHCP agent 中运行,为 neutron.agent.dhcp_agent.DhcpAgent。

DHCP agent 包含一个客户端 API,neutron.agent.dhcp_agent.DhcpPluginAPI。 DHCP agent 使用此类调用 Neutron 服务器中的远程方法。 服务器端在 neutron.api.rpc.handlers.dhcp_rpc.DhcpRpcCallback 中定义。 版本控制从这里开始变得复杂。

版本号与 oslo.messaging.Target 相关联。 使用主题(以及该主题上的可选特定主机)指定消息的目标。 通过相同目标公开的所有内容必须被版本化为相同版本流的一部分。 DhcpRpcCallback API 在“q-plugin”主题上公开。 许多其他内容也在同一主题上公开。 例如,在使用 ml2 插件时,相关代码是

self.endpoints = [rpc.RpcCallbacks(self.notifier, self.type_manager),
                  securitygroups_rpc.SecurityGroupServerRpcCallback(),
                  dvr_rpc.DVRServerRpcCallback(),
                  dhcp_rpc.DhcpRpcCallback(),
                  agents_db.AgentExtRpcCallback(),
                  metadata_rpc.MetadataRpcCallback()]

根据当前代码的编写方式,通过“q-plugin”主题公开的 API 是以下类定义的 API 的联合

neutron.plugins.ml2.rpc.RpcCallbacks
    neutron.plugins.ml2.drivers.type_tunnel.TunnelRpcCallbackMixin
neutron.api.rpc.handlers.securitygroups_rpc.SecurityGroupServerRpcCallback
neutron.api.rpc.handlers.dvr_rpc.DVRServerRpcCallback
neutron.api.rpc.handlers.dhcp_rpc.DhcpRpcCallback
neutron.db.agents_db.AgentExtRpcCallback
neutron.api.rpc.handlers.metadata_rpc.MetadataRpcCallback

这意味着对任何这些 API 的更改都意味着增加跨所有这些接口的版本号流。 但是,实现似乎表明它们被独立版本化,但并不一致。

提议的变更

该规范提出了两个更改。 这些更改的目的是帮助更清楚地了解版本号的范围。 这应该使这些版本号的处理更容易理解,并希望减少错误。

  1. 文档

对于 Neutron 中的每个客户端和服务器 rpc 接口定义,添加文档,清楚地说明接口的另一侧在代码中的位置。 此外,文档将列出包含在同一版本流中的任何其他类。

  1. 使用命名空间

在定义 oslo.messaging.Target 时,您可以添加一个命名空间。 这允许您在同一主题上公开多个接口,但仍然可以独立地对其进行版本化。

如果我们重新访问“q-plugin”主题和 ml2 驱动程序的示例,我们有几个接口在默认命名空间中的同一主题上公开。 将使用命名空间来分隔这些接口,以便可以独立地对每个接口进行版本化。

例如,公开的接口之一是 neutron.api.rpc.handlers.dhcp_rpc.DhcpRpcCallback。 该实现的开头如下所示

class DhcpRpcCallback(object):
    """DHCP agent RPC callback in plugin implementations."""

    target = messaging.Target(version='1.1')

相反,现在将使用命名空间定义目标

class DhcpRpcCallback(object):
    """DHCP agent RPC callback in plugin implementations."""

    target = messaging.Target(namespace='dhcp', version='1.1')

类似地,客户端也必须进行更新。 neutron.agent.dhcp_agent.DhcpPluginAPI.__init__ 包含以下代码

target = messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)

这里的更改与服务器端所做的工作非常相似

target = messaging.Target(namespace='dhcp', topic=topic, version='1.0')
self.client = n_rpc.get_client(target)

现在,当客户端发出请求时,它变得更加明确。 服务器端将仅在标记有“dhcp”命名空间的端点上查找请求的方法,而不是所有包含的端点。

数据模型影响

REST API 影响

安全影响

通知影响

其他最终用户影响

性能影响

IPv6 影响

其他部署者影响

从技术上讲,此更改不向后兼容。 现实是,只有当您期望任何形式的实时升级工作时,这才是重要的。 由于实际上并不期望实时升级工作,因此该规范建议接受这种理论上的不兼容性,因为目前这并不重要。

开发人员影响

开发人员的影响应该很小。 希望影响只是,当开发人员遇到需要更改这些接口之一的情况时,更清楚地了解受影响的代码的其他部分。 开发人员还必须了解任何未来添加的新接口的新命名空间用法。

社区影响

这里提出的内容只是文档和使用标准的 oslo.messaging API。

备选方案

不使用命名空间,您可以将每个接口公开到自己的主题上。 命名空间更方便,因为服务倾向于使用通用代码设置一个主题。 该通用代码需要进行修改才能支持映射到主题的任意端点列表。

实现

负责人

主要负责人

russellb

工作项

  • 将代码库转换为适当使用命名空间。

  • 将文档字符串添加到所有 rpc 客户端和服务器接口。

依赖项

测试

现有的测试将验证这些更改没有导致回归。

Tempest 测试

功能测试

API 测试

文档影响

用户文档

开发人员文档

额外的开发人员文档是该蓝图的关键交付成果。

参考资料