36/ZRE
ZeroMQ 实时交换协议
- 状态:稳定
- 编辑:Pieter Hintjens ph@imatix.com
ZeroMQ 实时交换协议 (ZRE) 管理网络上的一组对等节点如何相互发现、组织成组,并相互发送事件。ZRE 运行在 ZeroMQ 消息传输协议 (ZMTP) 之上。
序言
版权所有 (c) 2009-2014 iMatix Corporation
本规范是自由软件;您可以根据自由软件基金会发布的 GNU 通用公共许可证(本许可证的第 3 版,或(由您选择)任何更高版本)的条款重新分发和/或修改本规范。发布本规范的目的是希望它会有用,但不提供任何担保;甚至不提供适销性或特定用途适用性的默示担保。更多详细信息请参阅 GNU 通用公共许可证。您应该随本程序一起收到了 GNU 通用公共许可证的副本;如果没有,请访问 https://gnu.ac.cn/licenses。
本规范是一个自由开放的标准,受数字标准组织(Digital Standards Organization)的共识导向规范系统(Consensus-Oriented Specification System)管辖。
本文档中的关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”应按 RFC 2119 中所述进行解释。
目标
ZRE 协议提供了一种方式,让本地网络上的一组节点可以相互发现、跟踪对等节点的进出、向单个对等节点发送消息(单播)以及向一组对等节点发送消息(多播)。其目标是:
- 无需中心化服务或中介即可工作,除非网络默认提供这些服务。
- 在低质量网络,尤其是无线网络上具有鲁棒性。
- 最大限度地减少检测新对等节点在网络上出现所需的时间。
- 从短暂的连接故障中恢复。
- 对操作系统、编程语言和硬件保持中立。
- 允许任意数量的节点在一个进程中运行,以便进行大规模模拟和测试。
相比第 1 版的变更
这是 ZRE 的第 2 版。相比第 1 版的变更如下:
- 重新设计了‘字符串’和‘字典’类型,以提高可扩展性。
- 版本号增加到 2。
- 移除了对 TCP 传输的硬编码依赖(将 ip 地址和端口合并到一个单独的端点字段中)。
- 移除了日志扩展,日志功能最好由单独的框架处理。
- 协议签名是 1,而不是 0。
实现
节点标识与生命周期
ZRE 节点表示消息的源或目标。节点通常映射到应用程序。ZRE 节点通过一个 16 字节的通用唯一标识符 (UUID) 进行标识。ZRE 没有定义如何创建或销毁节点,但假定节点具有一定的持久性。
节点发现与存在
ZRE 使用 UDP IPv4 信标广播来发现节点。每个 ZRE 节点 应 侦听 ZRE 发现服务,该服务为 UDP 端口 5670(IANA 分配的 ZRE-DISC 端口)。每个 ZRE 节点 应 定期在 UDP 端口 5670 上广播一个信标,以向网络上任何正在侦听的节点标识自身。
ZRE 信标由一个 22 字节的 UDP 消息组成,格式如下:
+---+---+---+------+ +------+------+
| Z | R | E | %x01 | | UUID | port |
+---+---+---+------+ +------+------+
Header Body
头部 应 由字母‘Z’、‘R’和‘E’组成,后跟信标版本号,该版本号 应 为 %x01。
主体 应 由发送方的 16 字节 UUID 组成,后跟一个按网络字节序排列的两字节信箱端口号。如果端口号非零,则表示对等节点将在该端口号上接受 ZeroMQ TCP 连接。如果端口号为零,则表示对等节点正在断开与网络的连接。
有效的信标 应 使用可识别的头部和正确大小的主体。接收到无效信标的节点 应 悄悄地丢弃它。节点 可以 为了调试目的记录发送方的 IP 地址。节点 应 丢弃从自身接收到的信标。
当 ZRE 节点从它尚未知道的节点接收到带有非零端口号的信标时,它 应 将其视为一个新对等节点。
当 ZRE 节点从已知节点接收到带有零端口号的信标时,它 应 断开与该对等节点的连接。
互连模型
每个节点 应 创建一个 ZeroMQ ROUTER 套接字,并将其 绑定 到一个临时 TCP 端口(范围在 %C000x - %FFFFx 之间)。节点 应 在其发送的所有信标中广播此信箱端口号。请注意,节点不广播其 IP 地址,因为这是由 UDP recvfrom 函数提供的。
此 ROUTER 套接字 应 用于接收来自其他节点的所有传入 ZeroMQ 消息。节点 不应 通过此套接字向对等节点发送消息。
当节点发现一个新对等节点时,它 应 创建一个 ZeroMQ DEALER 套接字,在该套接字上设置其身份(一个包含 1 的字节,后跟 16 字节的 UUID),并将此连接到对等节点的信箱端口。节点可以在连接后立即开始通过此 DEALER 套接字向对等节点发送消息。
一个节点 应 将每个 DEALER 套接字 连接 到 至多一个对等节点。如果对等节点在一定时间内未响应,节点 可以 断开其 DEALER 套接字(参见心跳机制)。
此 DEALER 套接字 应 用于所有发送到特定对等节点的 ZeroMQ 传出消息。节点 不应 在此套接字上接收消息。发送方 可以 设置高水位标记 (HWM),例如每秒 100 条消息(如果超时周期为 30 秒,这意味着 HWM 为 3,000 条消息)。发送方 应该 将套接字上的发送超时设置为零,以便可以检测到发送缓冲区已满,并将其视为“对等节点无响应”。
请注意,ROUTER 套接字会为接收到的任何消息的调用方提供发送方的 UUID,作为消息中其他帧之前的身份帧。因此,对等节点可以使用接收到的消息中的身份来查找用于回发给该对等节点的相应 DEALER 套接字。该身份 应 是一个二进制的 17 字节 UUID 值,其中第一个字节始终为 1。
当节点在其 ROUTER 套接字上接收到来自未知节点的有效消息时,它 应 将其视为新对等节点,其方式与接收到来自未知节点的 UDP 信标完全相同。
注意:ZRE 使用的 ROUTER 到 DEALER 模式旨在确保消息永远不会因同步问题而丢失。发送到尚未与对等节点建立连接的 ROUTER 套接字会导致消息被丢弃。
协议签名
通过 TCP 发送的每条 ZRE 消息 应 以 ZRE 协议签名 %xAA %xA1 开头。节点 应 悄悄地丢弃收到的任何不以此两个字节开头(消息)的消息。
此机制专门设计用于绑定到临时端口的应用程序,这些端口可能以前被其他协议使用过,并且仍然有节点尝试连接到这些端口。它也是一种通用的快速失败机制,用于检测格式错误的消息。
版本控制
一个版本号字节 %x02 应 紧跟在签名后面。节点 应 丢弃不包含有效版本号的消息。不支持向后兼容。
TCP 协议语法
以下 ABNF 语法定义了 ZRE 协议:
zre = greeting *traffic
greeting = hello
traffic = whisper / shout / join / leave / ping / ping-ok
; Greet a peer so it can connect back to us
hello = signature %d1 version sequence endpoint groups status name headers
signature = %xAA %xA1 ; two octets
version = number-1 ; Version number (2)
sequence = number-2 ; Cyclic sequence number
endpoint = string ; Sender connect endpoint
groups = strings ; List of groups sender is in
status = number-1 ; Sender groups status value
name = string ; Sender public name
headers = dictionary ; Sender header properties
; Send a multi-part message to a peer
whisper = signature %d2 version sequence content
version = number-1 ; Version number (2)
sequence = number-2 ; Cyclic sequence number
content = msg ; Wrapped message content
; Send a multi-part message to a group
shout = signature %d3 version sequence group content
version = number-1 ; Version number (2)
sequence = number-2 ; Cyclic sequence number
group = string ; Group to send to
content = msg ; Wrapped message content
; Join a group
join = signature %d4 version sequence group status
version = number-1 ; Version number (2)
sequence = number-2 ; Cyclic sequence number
group = string ; Name of group
status = number-1 ; Sender groups status value
; Leave a group
leave = signature %d5 version sequence group status
version = number-1 ; Version number (2)
sequence = number-2 ; Cyclic sequence number
group = string ; Name of group
status = number-1 ; Sender groups status value
; Ping a peer that has gone silent
ping = signature %d6 version sequence
version = number-1 ; Version number (2)
sequence = number-2 ; Cyclic sequence number
; Reply to a peer's ping
ping_ok = signature %d7 version sequence
version = number-1 ; Version number (2)
sequence = number-2 ; Cyclic sequence number
; A list of string values
strings = strings-count *strings-value
strings-count = number-4
strings-value = longstr
; A list of name/value pairs
dictionary = dict-count *( dict-name dict-value )
dict-count = number-4
dict-value = longstr
dict-name = string
; A msg is zero or more distinct frames
msg = *frame
; Strings are always length + text contents
string = number-1 *VCHAR
longstr = number-4 *VCHAR
; Numbers are unsigned integers in network byte order
number-1 = 1OCTET
number-2 = 2OCTET
number-4 = 4OCTET
ZRE 命令
所有命令都以协议签名 (%xAA %xA1) 开头,后跟命令标识符,然后是协议版本号 (%d2),最后是序列号(网络字节序的两个字节)。对等节点发送的第一条消息 (HELLO) 必须 具有序列号 1,并且每条消息必须具有严格递增的序列号。当对等节点检测到序列中的间隙或乱序消息时,它 应 将该对等节点视为无效,并断开其连接。
HELLO 命令
每个节点 应 在与对等节点的连接上发送 HELLO 作为第一个命令来开始对话。
当节点从新对等节点接收到消息时,它 应 静默忽略任何在 HELLO 命令之前的命令。HELLO 的序列号 应 为 1。
HELLO 命令包含以下字段:
- 端点- 发送方将接受连接的 ZeroMQ 端点。
- 组- 发送方当前所在的组列表,以字符串列表形式表示。
- 状态- 发送方的组状态序列号。
- 头部- 发送方设置的零个或多个属性。
如果接收方尚未连接到此对等节点,它 应 创建一个 ZeroMQ DEALER 套接字,并将其连接到指定为“tcp://ipaddress:mailbox”的端点。
“组状态序列号”是一个单字节数字,每当对等节点加入或离开组时递增。每个对等节点 可以 使用此字段来断言其自身组管理信息的准确性。
WHISPER 命令
当节点希望向单个对等节点发送消息时,它 应 使用 WHISPER 命令。WHISPER 命令包含一个字段,即定义为 0MQ 多部分消息的消息内容。
SHOUT 命令
当节点希望向参与某个组的一组节点发送消息时,它 应 使用 SHOUT 命令。SHOUT 命令包含两个字段:组的名称,以及定义为 0MQ 多部分消息的消息内容。
请注意,消息通过 ZeroMQ 经由 TCP 发送,因此 SHOUT 命令会单播到每个应该接收它的对等节点。ZRE 不提供任何 UDP 多播功能。
JOIN 命令
当节点加入组时,它 应 向其所有对等节点广播一个 JOIN 命令。JOIN 命令有两个字段:要加入的组的名称,以及加入组后的组状态序列号。组名区分大小写。
LEAVE 命令
当节点离开组时,它 应 向其所有对等节点广播一个 LEAVE 命令。LEAVE 命令有两个字段:要离开的组的名称,以及离开组后的组状态序列号。
PING 命令
节点 应该 向在一定时间内(通常为五秒)未收到其 UDP 信标的任何对等节点发送 PING 命令。请注意,在严重饱和的网络上,UDP 流量可能会被丢弃。如果节点在较长时间内(通常为 30 秒)未收到 PING 命令的回复,且未收到来自该对等节点的其他流量,它 应该 将该对等节点视为已死亡。
请注意,PING 命令 应该 仅在对等节点处于静默状态的定向情况下使用。否则,PING 命令的成本将随连接的对等节点数量呈指数级增长,并可能降低网络性能。
PING-OK 命令
当节点收到 PING 命令时,它 应 回复一个 PING-OK 命令。
节点发现与存在
ZRE 使用 UDP IPv4 信标广播来发现节点并跟踪其存在。其工作方式如下:
- ZRE 节点 应 侦听 ZRE 发现服务,该服务为 UDP 端口 5670(IANA 分配的 ZRE-DISC 端口)。
- 每个 ZRE 节点 应 定期广播一个 UDP 信标,以向网络上任何正在侦听的节点标识自身。
- 当 ZRE 节点从它尚未知道的节点接收到信标时,它 应 将其视为一个新对等节点。
- 当 ZRE 节点停止接收来自已知对等节点的信标,经过一定间隔后,它 应 将此对等节点视为已断开连接或已死亡。
ZRE 信标由一个 22 字节的 UDP 消息组成,格式如下:
+---+---+---+------+ +------+------+
| Z | R | E | %x01 | | UUID | port |
+---+---+---+------+ +------+------+
Header Body
实现者须知
- 头部 应 由字母‘Z’、‘R’和‘E’组成,后跟信标版本号,该版本号 应 为 %x01。
- 主体 应 由发送方的 16 字节 UUID 组成,后跟一个按网络字节序排列的两字节信箱端口号。
- 有效的信标 应 满足以下条件:使用可识别的头部;使用正确大小的主体;并提供非零的信箱端口号。
- 接收到无效信标的节点 应 悄悄地丢弃它。节点 可以 为了调试目的记录发送方的 IP 地址。
- 节点 应 丢弃从自身接收到的信标。
安全方面
ZRE 的安全性由底层传输处理。对于 ZMTP v2 和 v1,没有安全模型,所有信息都以明文交换。对于 ZMTP v3,可以使用任何已定义的安全机制。