7/MDP

7/MDP

Majordomo 协议

Majordomo 协议 (MDP) 定义了一组客户端应用、一个中介和一组工作器应用之间的可靠的面向服务的请求-应答对话。MDP 涵盖了在场状态、心跳以及面向服务的请求-应答处理。它源自《指南》第 4 章中定义的 Majordomo 模式。

许可证

版权所有 (c) 2011 iMatix Corporation。

本规范是自由软件;您可以根据自由软件基金会发布的 GNU 通用公共许可证的条款重新分发和/或修改它;可以选择许可证的第 3 版或任何后续版本。

本规范的发布是希望它会有用,但没有任何担保;甚至不包括适销性或特定用途适用性的默示担保。详情请参见 GNU 通用公共许可证。

您应该已经随本程序收到了 GNU 通用公共许可证的副本;如果没有,请参见 https://gnu.ac.cn/licenses

变更流程

本规范是一个自由和开放的标准(参见“自由和开放标准的定义”),并由数字标准组织(Digital Standards Organization)的面向共识的规范系统 (COSS) 管理(参见“面向共识的规范系统”)。

语言

本文档中的关键词“MUST”(必须)、“MUST NOT”(禁止)、“REQUIRED”(要求)、“SHALL”(将)、“SHALL NOT”(不应)、“SHOULD”(应)、“SHOULD NOT”(不应)、“RECOMMENDED”(推荐)、“MAY”(可)和“OPTIONAL”(可选)应按照 RFC 2119 中的描述进行解释(参见“RFC 中用于指示需求级别的关键词”)。

目标

Majordomo 协议 (MDP) 定义了一组客户端应用、一个中介和一组工作器应用之间的可靠的面向服务的请求-应答对话。MDP 涵盖了在场状态、心跳以及面向服务的请求-应答处理。它源自《指南》第 4 章中定义的 Majordomo 模式(参见“ØMQ - 《指南》”)。Majordomo 模式与同名的开源邮件列表软件无关。

MDP 是 6/PPP 的演进,增加了基于名称的服务解析和更结构化的协议命令。

MDP 的目标是:

  • 允许基于抽象服务名称将请求路由到工作器。
  • 通过使用心跳机制,允许双方对等体检测到对方的断开。
  • 允许中介对给定服务的任务分发给工作器实现“最近最少使用”模式。
  • 允许中介通过将请求重新发送给其他工作器来从死亡或断开的工作器中恢复。

架构

Figure

整体拓扑

MDP 连接一组客户端应用、一个中介设备和一个工作器应用池。客户端连接到中介,工作器也连接到中介。客户端和工作器彼此不可见,两者都可以随意加入和离开。中介可以(MAY)打开两个套接字(端口),一个用于客户端的前端,一个用于工作器的后端。但是,MDP 也设计为可以通过单个中介套接字工作。

我们将‘客户端’应用定义为发出请求的应用,将‘工作器’应用定义为处理请求的应用。MDP 作出以下假设:

  • 工作器是幂等的,即多次执行同一请求是安全的。
  • 工作器每次最多处理一个请求,并且对于每个成功请求,都会发出恰好一个应答。

Majordomo 中介处理一组共享请求队列,每个服务一个队列。每个队列有多个写入者(客户端)和多个读取者(工作器)。中介应(SHOULD)公平地为客户端提供服务,并且可以(MAY)基于任何方式将请求发送给工作器,包括轮询和最近最少使用。

MDP 由两个子协议组成:

  • MDP/客户端,涵盖了 MDP 中介如何与客户端应用通信。
  • MDP/工作器,涵盖了 MDP 中介如何与工作器应用通信。

中介可以是一个中间设备,也可以是一个直接实现 MDP/工作器的客户端应用。类似地,中介可以直接集成服务,而不是使用 MDP/工作器。

ROUTER 地址

中介必须(MUST)使用 ROUTER 套接字来接受来自客户端的请求以及来自工作器的连接。中介可以(MAY)为每个子协议使用单独的套接字,或者可以(MAY)为两个子协议使用单个套接字。

摘自 ØMQ 参考手册(参见“[]()")

当接收消息时,ROUTER 套接字应(shall)在将消息传递给应用之前,在消息前面附加一个包含发起对等体身份的消息部分。当发送消息时,ROUTER 套接字应(shall)移除消息的第一个部分,并使用它来确定消息应(shall)路由到的对等体的身份。

这个额外的帧没有在下面解释的子协议命令中显示。

MDP/客户端

MDP/客户端是一个由客户端发起的严格同步对话(其中“C”代表客户端,“B”代表中介)

Repeat:
    C: REQUEST
    B: REPLY
    ...

一个 REQUEST 命令由包含 4 个或更多帧的多部分消息组成,在网络上传输时格式如下:

  • 帧 0:空帧(零字节,对 REQ 应用不可见)
  • 帧 1:“MDPC01”(六字节,表示 MDP/客户端 v0.1)
  • 帧 2:服务名称(可打印字符串)
  • 帧 3+:请求体(不透明二进制数据)

一个 REPLY 命令由包含 4 个或更多帧的多部分消息组成,在网络上传输时格式如下:

  • 帧 0:空帧(零字节,对 REQ 应用不可见)
  • 帧 1:“MDPC01”(六字节,表示 MDP/客户端 v0.1)
  • 帧 2:服务名称(可打印字符串)
  • 帧 3+:应答体(不透明二进制数据)

客户端在实现同步请求-应答模式时应(SHOULD)使用 REQ 套接字。REQ 套接字将为传出请求静默创建帧 0,并在将应答传递给调用应用之前将其移除。客户端在实现异步模式时可以(MAY)使用 DEALER (XREQ) 套接字。在这种情况下,客户端必须(MUST)显式创建空帧 0。

客户端可以(MAY)使用任何合适的策略来从无响应的中介中恢复。一种推荐的策略是:

  • 在请求套接字上使用轮询而不是阻塞接收。
  • 如果在某个超时时间内没有应答,则关闭请求套接字并打开一个新的套接字,然后在该新套接字上重新发送请求。
  • 如果在几次重试后仍然没有应答,则将事务标记为失败。

服务名称是一个 0MQ 字符串,它必须与工作器在其 READY 命令中指定的服务名称相匹配(参见下面的 MDP/工作器)。如果尚未注册服务,中介应(SHOULD)将客户端请求加入队列,并且如果在合理的可配置时间后仍未注册服务,应(SHOULD)使这些请求过期。

MDP/工作器

MDP/工作器是服务工作器发起的同步请求-应答对话和在两个方向上独立运行的异步心跳对话的混合。这是同步对话(其中“S”代表服务工作器,“B”代表中介):

S: READY
Repeat:
    B: REQUEST
    S: REPLY
    ...

异步心跳对话在相同的套接字上运行,工作原理如下:

Repeat:                 Repeat:
    S: HEARTBEAT            B: HEARTBEAT
    ...                     ...
S: DISCONNECT           B: DISCONNECT

一个 READY 命令由包含 4 个帧的多部分消息组成,在网络上传输时格式如下:

  • 帧 0:空帧
  • 帧 1:“MDPW01”(六字节,表示 MDP/工作器 v0.1)
  • 帧 2:0x01(一字节,表示 READY)
  • 帧 3:服务名称(可打印字符串)

一个 REQUEST 命令由包含 6 个或更多帧的多部分消息组成,在网络上传输时格式如下:

  • 帧 0:空帧
  • 帧 1:“MDPW01”(六字节,表示 MDP/工作器 v0.1)
  • 帧 2:0x02(一字节,表示 REQUEST)
  • 帧 3:客户端地址(信封栈)
  • 帧 4:空帧(零字节,信封分隔符)
  • 帧 5+:请求体(不透明二进制数据)

一个 REPLY 命令由包含 6 个或更多帧的多部分消息组成,在网络上传输时格式如下:

  • 帧 0:空帧
  • 帧 1:“MDPW01”(六字节,表示 MDP/工作器 v0.1)
  • 帧 2:0x03(一字节,表示 REPLY)
  • 帧 3:客户端地址(信封栈)
  • 帧 4:空帧(零字节,信封分隔符)
  • 帧 5+:应答体(不透明二进制数据)

一个 HEARTBEAT 命令由包含 3 个帧的多部分消息组成,在网络上传输时格式如下:

  • 帧 0:空帧
  • 帧 1:“MDPW01”(六字节,表示 MDP/工作器 v0.1)
  • 帧 2:0x04(一字节,表示 HEARTBEAT)

一个 DISCONNECT 命令由包含 3 个帧的多部分消息组成,在网络上传输时格式如下:

  • 帧 0:空帧
  • 帧 1:“MDPW01”(六字节,表示 MDP/工作器 v0.1)
  • 帧 2:0x05(一字节,表示 DISCONNECT)

MDP/工作器命令都以一个空帧开头,以便在单个套接字上,中介中客户端和工作器帧的处理保持一致。空帧没有其他意义。

连接的建立和关闭

  • 工作器负责建立和关闭逻辑连接。一个工作器必须(MUST)使用单个 ØMQ DEALER (XREQ) 套接字连接到恰好一个中介。

  • 由于 ØMQ 在故障后会自动重新连接对等体,因此每个 MDP 命令都包含协议头,以便对对等体接收到的所有消息进行适当验证。

  • 工作器通过创建一个新套接字、连接它,然后发送 READY 命令来注册服务,从而建立与中介的连接。一个工作器精确地处理一个服务,许多工作器可以(MAY)处理同一个服务。工作器禁止(MUST NOT)再次发送 READY。

  • READY 命令没有应答。工作器应(SHOULD)假定注册成功,除非它收到 DISCONNECT,或者通过心跳检测到中介故障。

  • 工作器可以(MAY)在任何时候发送 DISCONNECT,包括在 READY 之前。当中介收到工作器发送的 DISCONNECT 后,它必须(MUST)停止向该工作器发送任何进一步的命令。

  • 中介可以(MAY)在任何时候发送 DISCONNECT,根据定义是在它至少从工作器接收到一条命令之后。

  • 中介必须(MUST)通过发送 DISCONNECT 并随后停止向该工作器发送任何进一步的命令来响应任何有效但意外的命令。中介应(SHOULD)通过丢弃无效消息并将其视为无效对等体来响应。

  • 当工作器收到 DISCONNECT 后,它必须停止向中介发送任何进一步的命令;它必须(MUST)关闭其套接字,并在新的套接字上重新连接到中介。这种机制允许工作器在中介故障和恢复后重新注册。

请求和应答处理

  • REQUEST 和 REPLY 命令必须(MUST)精确包含一个客户端地址帧。此帧后面必须(MUST)紧跟一个空帧(零大小)。

  • ROUTER 套接字会将每个直接连接的客户端的地址附加到所有来自客户端的请求消息前面。该 ROUTER 套接字也期望在发送给客户端的每个应答消息前面附加一个客户端地址。

心跳

  • HEARTBEAT 命令在 READY 命令之后,在任何时候都有效。

  • 除了 DISCONNECT 之外的任何收到的命令都视为心跳。对等体不应(SHOULD NOT)在发送其他命令的同时发送 HEARTBEAT 命令。

  • 中介和工作器都必须(MUST)以定期和约定的间隔发送心跳。如果对等体在约定间隔的某个倍数(通常为 3-5 倍)内没有收到心跳,则必须(MUST)将对方对等体视为“已断开”。

  • 如果工作器检测到中介已断开连接,它应(SHOULD)重新开始一个新的对话。

  • 如果中介检测到工作器已断开连接,它应(SHOULD)停止向其发送任何类型的消息。

可靠性

Majordomo 模式旨在扩展基本的 ØMQ 请求-应答模式,增加检测和从特定故障集中恢复的能力:

  • 崩溃、运行过慢或冻结的工作器应用。
  • 与网络断开连接(暂时或永久)的工作器应用。
  • 与网络暂时断开连接的客户端应用。
  • 崩溃并重启的队列中介。
  • 遭受永久性故障的队列中介。
  • 由于上述任何故障而丢失的请求或应答。

通用方法是重试和重新连接,在需要时使用心跳机制。Majordomo 通过在中介中不保留重要状态来间接支持活-活中介故障转移。实际故障转移到备用中介是由客户端和工作器处理的,而不是协议。有关这些故障以及 Majordomo 用于检测和恢复的策略的更全面讨论,请参见《指南》第 4 章(参见“ØMQ - 《指南》”)。

可伸缩性和性能

Majordomo 设计用于可伸缩到大量(数千个)工作器和客户端,仅受中介系统资源的限制。按服务划分工作器允许多个应用共享相同的中介基础设施。

单个客户端应用的吞吐量性能将受限于每秒数万次,而非数百万次请求-应答事务,这归因于往返成本和基于中介方法的额外延迟。请求和应答消息越大,Majordomo 的效率就越高。Majordomo 可以与高速数据传输架构互补。

中介的系统要求适中:每个客户端排队的未完成请求不超过一个,并且消息内容可以在客户端和工作器之间切换,无需复制或处理。因此,单个中介线程每秒可以切换数百万条消息,而多线程实现(提供多个虚拟中介,每个中介在其自己的端口上)可以根据需要扩展到任意数量的核心。

安全性

MDP 不实现任何认证、访问控制或加密机制,不应(should not)在需要这些功能的任何部署中使用。但是,所有这些都可以分层在 MDP 之上,并可能成为未来 RFC 的主题。

参考实现

《指南》第 4 章中的 C99 Majordomo 示例(参见“ØMQ - 《指南》”)是 MDP 的主要参考实现。Guido Goldstein 提供了一个 Python 实现的 MDP。

已知弱点

  • 中介和工作器中的心跳速率必须设置为相似的值,否则会发生错误断开。未来将开发更好的心跳设计。
  • 使用多个帧进行命令格式化会对性能产生影响。未来的 MDP 版本可能会将命令放在单个帧中。