18/MDP

18/MDP

Majordomo 协议

Majordomo 协议 (MDP) 定义了一组客户端应用程序、一个 Broker 和一组工作者应用程序之间可靠的面向服务的请求-回复对话。MDP 涵盖了存在性、心跳以及面向服务的请求-回复处理。它源自《指南》第四章定义的 Majordomo 模式。这是 MDP 0.2 版,它增加了对单个请求多次回复的支持。

许可

版权所有 (c) 2012 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) 定义了一组客户端应用程序、一个 Broker 和一组工作者应用程序之间可靠的面向服务的请求-回复对话。MDP 涵盖了存在性、心跳以及面向服务的请求-回复处理。它源自《指南》第四章定义的 Majordomo 模式(参见“ØMQ - 指南”)。Majordomo 模式与同名的开源邮件列表软件无关。

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

MDP/0.2 是 MDP/0.1 的演进,增加了对单个请求多次回复的支持。MDP/0.2 与 MDP/0.1 不兼容。主要区别在于 (a) 用 PARTIAL 和 FINAL 命令取代 REPLY 命令,以及 (b) 移除命令开头的空帧。

MDP 的目标是

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

架构

Figure

总体拓扑

MDP 连接了一组客户端应用程序、一个 Broker 设备和一个工作者应用程序池。客户端连接到 Broker,工作者也连接到 Broker。客户端和工作者彼此不可见,并且两者都可以随意出现和消失。Broker 可以打开两个套接字(端口),一个用于客户端的前端,一个用于工作者的后端。但是 MDP 也设计为可以通过单个 Broker 套接字工作。

我们将“客户端”应用程序定义为发出请求的应用程序,将“工作者”应用程序定义为处理请求的应用程序。MDP 做出了以下假设

  • 工作者是幂等的,即多次执行相同的请求是安全的。
  • 工作者一次最多处理一个请求,并且对每个成功的请求只发出一个回复。

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

MDP 由两个子协议组成

  • MDP/客户端,涵盖了 MDP Broker 如何与客户端应用程序通信。
  • MDP/工作者,涵盖了 MDP Broker 如何与工作者应用程序通信。

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

ROUTER 地址分配

Broker 必须使用一个 ROUTER 套接字来接收来自客户端的请求和来自工作者的连接。Broker 可以为每个子协议使用一个单独的套接字,也可以使用一个套接字处理两个子协议。

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

接收消息时,ROUTER 套接字应在将消息传递给应用程序之前,在消息前面附加包含原始对端身份的消息部分。发送消息时,ROUTER 套接字应移除消息的第一部分,并使用它来确定消息应路由到的对端的身份。

此额外的帧在下面解释的子协议命令中未显示。

MDP/客户端

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

Repeat:
    C: REQUEST
    B: *PARTIAL
    B: FINAL
    ...

一个 REQUEST 命令由 4 个或更多帧组成的多部分消息组成,其在线上格式如下

  • 帧 0:“MDPC02”(六字节,表示 MDP/客户端 v0.2)
  • 帧 1:0x01(一字节,表示 REQUEST)
  • 帧 2:服务名称(可打印字符串)
  • 帧 3+:请求体(不透明二进制)

一个 PARTIAL 命令由 4 个或更多帧组成的多部分消息组成,其在线上格式如下

  • 帧 0:“MDPC02”(六字节,表示 MDP/客户端 v0.2)
  • 帧 1:0x02(一字节,表示 PARTIAL)
  • 帧 2:服务名称(可打印字符串)
  • 帧 3+:回复体(不透明二进制)

一个 FINAL 命令由 4 个或更多帧组成的多部分消息组成,其在线上格式如下

  • 帧 0:“MDPC02”(六字节,表示 MDP/客户端 v0.2)
  • 帧 1:0x03(一字节,表示 FINAL)
  • 帧 2:服务名称(可打印字符串)
  • 帧 3+:回复体(不透明二进制)

客户端必须使用 DEALER 套接字。客户端必须准备好处理来自 Broker 的零个或多个 PARTIAL 命令。在 FINAL 命令之后,Broker 不得向客户端发送任何进一步的命令。

客户端可以使用任何合适的策略来从无响应的 Broker 中恢复。一个推荐的策略是

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

服务名称是一个 0MQ 字符串,与工作者在其 READY 命令中指定的服务名称匹配(参见下面的 MDP/工作者)。对于尚未注册服务的客户端请求,Broker 应该将其排队,并且如果在合理且可配置的时间后仍未注册服务,则应该使这些请求过期。

MDP/工作者

MDP/工作者 是一个由服务工作者发起的同步请求-回复对话,以及一个在两个方向独立运作的异步心跳对话的混合。这是同步对话(其中“S”表示工作者,“B”表示 Broker)

W: READY
Repeat:
    B: REQUEST
    W: *PARTIAL
    W: FINAL
    ...

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

Repeat:                 Repeat:
    W: HEARTBEAT            B: HEARTBEAT
    ...                     ...
W: DISCONNECT           B: DISCONNECT

一个 READY 命令由 3 帧组成的多部分消息组成,其在线上格式如下

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

一个 REQUEST 命令由 5 个或更多帧组成的多部分消息组成,其在线上格式如下

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

一个 PARTIAL 命令由 5 个或更多帧组成的多部分消息组成,其在线上格式如下

  • 帧 0:“MDPW02”(六字节,表示 MDP/工作者 v0.2)
  • 帧 1:0x03(一字节,表示 PARTIAL)
  • 帧 2:客户端地址(信封堆栈)
  • 帧 3:空(零字节,信封分隔符)
  • 帧 4+:回复体(不透明二进制)

一个 FINAL 命令由 5 个或更多帧组成的多部分消息组成,其在线上格式如下

  • 帧 0:“MDPW02”(六字节,表示 MDP/工作者 v0.2)
  • 帧 1:0x04(一字节,表示 FINAL)
  • 帧 2:客户端地址(信封堆栈)
  • 帧 3:空(零字节,信封分隔符)
  • 帧 4+:回复体(不透明二进制)

一个 HEARTBEAT 命令由 2 帧组成的多部分消息组成,其在线上格式如下

  • 帧 0:“MDPW02”(六字节,表示 MDP/工作者 v0.2)
  • 帧 1:0x05(一字节,表示 HEARTBEAT)

一个 DISCONNECT 命令由 2 帧组成的多部分消息组成,其在线上格式如下

  • 帧 0:“MDPW02”(六字节,表示 MDP/工作者 v0.2)
  • 帧 1:0x06(一字节,表示 DISCONNECT)

打开和关闭连接

  • 工作者负责打开和关闭逻辑连接。一个工作者必须使用一个 ØMQ DEALER 套接字连接到且仅连接到一个 Broker。

  • 由于 ØMQ 在发生故障后会自动重新连接对端,因此每个 MDP 命令都包含协议头,以允许对对端接收到的所有消息进行正确验证。

  • 工作者通过创建一个新套接字、连接它,然后发送 READY 命令注册服务来打开与 Broker 的连接。一个工作者只处理一个服务,并且许多工作者可以处理同一个服务。工作者不得发送后续的 READY。

  • 对 READY 没有响应。工作者应该假定注册成功,直到或除非它收到 DISCONNECT,或者通过心跳检测到 Broker 故障。

  • 工作者可以在任何时候发送 DISCONNECT,包括在 READY 之前。当 Broker 收到工作者发送的 DISCONNECT 后,它必须不再向该工作者发送任何命令。

  • Broker 可以在任何时候发送 DISCONNECT,根据定义,是在它至少从工作者接收到一个命令之后。

  • Broker 必须对任何有效但意外的命令通过发送 DISCONNECT 来响应,然后不再向该工作者发送任何进一步的命令。Broker 应该通过丢弃无效消息并将其对端视为无效来响应无效消息。

  • 当工作者收到 DISCONNECT 后,它不得再向 Broker 发送任何命令;它必须关闭其套接字,并在新的套接字上重新连接到 Broker。这种机制允许工作者在 Broker 故障和恢复后重新注册。

请求和回复处理

  • 工作者应为单个 REQUEST 发送零个或多个 PARTIAL 命令,然后紧跟着一个 FINAL 命令。

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

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

心跳机制

  • HEARTBEAT 命令在 READY 命令之后随时有效。

  • 除 DISCONNECT 外的任何收到的命令都视为心跳。对端不应该在发送其他命令的同时也发送 HEARTBEAT 命令。

  • Broker 和工作者都必须以定期且约定的间隔发送心跳。如果对端在间隔的某个倍数(通常是 3-5 倍)内没有发送心跳,则必须将其视为“已断开连接”。

  • 如果工作者检测到 Broker 已断开连接,它应该重新开始一个新的对话。

  • 如果 Broker 检测到工作者已断开连接,它应该停止向该工作者发送任何类型的消息。

可靠性

Majordomo 模式旨在扩展基本的 ØMQ 请求-回复模式,使其能够检测和恢复一组特定的故障

  • 崩溃、运行过慢或冻结的工作者应用程序。
  • 与网络断开连接(暂时或永久)的工作者应用程序。
  • 暂时与网络断开连接的客户端应用程序。
  • 崩溃后重启的队列 Broker。
  • 遭受永久性故障的队列 Broker。
  • 由于这些故障中的任何一个而丢失的请求或回复。

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

可扩展性和性能

Majordomo 设计为可扩展到大量(数千个)工作者和客户端,仅受限于 Broker 上的系统资源。按服务对工作者进行分区允许多个应用程序共享相同的 Broker 基础设施。

单个客户端应用程序的吞吐量性能将受限于每秒数万次请求-回复事务,而不是数百万次,这是由于往返成本以及基于 Broker 方法带来的额外延迟。请求和回复消息越大,Majordomo 的效率将越高。Majordomo 可以与高速数据交付架构相结合。

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

安全性

MDP 不实现任何身份验证、访问控制或加密机制,不应在需要这些功能的任何部署中使用。然而,所有这些都可以叠加在 MDP 之上,并可能成为未来 RFC 的主题。

参考实现

MDP/0.2 的参考实现位于 https://github.com/zeromq/majordomo

已知弱点

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