46/DAFKA
ZeroMQ Dafka 协议 1.0
- 状态:草案
- 编辑:Kevin Sapper mail@kevinsapper.de
Dafka 是一个去中心化分布式流媒体平台。本文档描述了 Dafka 1.0。
前言
版权所有 (c) 2020 Kevin Sapper
本规范是自由软件;您可以根据自由软件基金会发布的 GNU 通用公共许可证的条款重新分发和/或修改它;可以是许可证的第 3 版,或者(由您选择)任何更高版本。本规范的发布是为了希望它有用,但没有任何担保;甚至没有适销性或特定用途适用性的默示担保。有关更多详细信息,请参阅 GNU 通用公共许可证。您应该已经随本程序收到了 GNU 通用公共许可证的副本;如果没有,请参阅 https://gnu.ac.cn/licenses。
本规范是自由和开放标准,并受数字标准组织面向共识的规范系统的管辖。
本文档中的关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”应按照RFC 2119中的描述进行解释。
目标
Dafka 是一个去中心化分布式流媒体平台。这到底意味着什么?
流媒体平台具有三个关键能力
- 发布和订阅记录流,类似于消息队列或企业消息系统。
- 以容错的持久方式存储记录流。
- 在记录流发生时处理它们。
Dafka 通常用于两大类应用
- 构建实时流数据管道,可靠地在系统或应用之间获取数据
- 构建实时流应用,用于转换或响应数据流
实现
节点类型
Dafka 定义了三种节点类型
- 生产者
- 消费者
- 存储节点
节点标识与生命周期
一个 Dafka 节点代表一个生产者、消费者或存储节点。Dafka 节点通过一个 16 字节的通用唯一标识符 (UUID) 来标识。Dafka 不定义节点的创建或销毁方式,但假定节点具有一定的持久性。
节点发现与存在
Dafka 使用信标塔和信标广播来发现节点。每个 dafka 节点 SHALL 定期向信标塔发布其存在。
因此,节点发送包含 4 个消息帧的节点信标。消息类型“B”,节点的 UUID 以及其他对等节点可以访问此节点的 IP 地址和端口。
+---+ +------+ +----+ +------+
| B | | UUID | | IP | | Port |
+---+ +------+ +----+ +------+
Node-Beacon
当信标塔收到节点信标时,它 SHALL 发布一个适当的信标塔信标,该信标包含 3 个消息帧。消息类型“B”,节点的 UUID 以及其他节点连接的 tcp 端点。
+---+ +------+ +--------------+
| B | | UUID | | tcp endpoint |
+---+ +------+ +--------------+
Tower-Beacon
互连模型
Dafka 使用发布-订阅模式在其所有节点之间建立网状网络。
每个节点 SHALL 创建一个 ZeroMQ PUB 和一个 ZeroMQ SUB 套接字与信标塔通信,下文分别称为信标发布者套接字和信标订阅者套接字。信标发布者套接字 SHALL 连接到一个或多个信标塔的 SUB 套接字,信标订阅者套接字 SHALL 连接到一个或多个信标塔的 PUB 套接字。
每个节点 SHALL 创建一个 ZeroMQ XPUB 套接字,下文称为发布者套接字,并将其绑定到一个其他节点可以访问的地址。节点 SHALL 定期将此地址发送给所有连接的信标塔。XPUB 套接字用于向其他节点发送消息并接收订阅通知。
每个节点 SHALL 创建第二个 ZeroMQ SUB 套接字,下文称为订阅者套接字。当一个节点发现另一个节点时,它 SHALL 将此套接字连接到其他节点的发布者套接字。此订阅者套接字用于接收来自其他节点的消息。
如果对等节点在一定时间内未响应(参见心跳机制),节点 MAY 断开其订阅者套接字。
主题、分区和记录
Dafka 版本 1 定义了以下关于主题、分区和记录的规定。
Dafka 集群 SHALL 不对主题的总数施加任何限制,除非是由于可用内存或磁盘空间造成的物理限制。Dafka 主题的名称可以是任意长度的字节序列。尽管 RECOMMENDED 仅使用 ASCII 字符集中的字符。
每个 Dafka 主题 SHALL 至少包含一个分区。为了保持协议简单,每个生产者 MUST 只能向一个分区发布记录,并且进一步,同一个分区 SHALL 只能由一个生产者发布。这意味着一个分区可以映射到正好一个生产者。为了使这种连接显而易见,分区的名称 SHALL 是生产者的节点 UUID。
每个分区的第一个记录的偏移量 SHALL 为 0。每个后续记录的偏移量 SHALL 为前一个记录的偏移量加 1。
除了节点中可用的内存量外,Dafka 不对记录的大小做任何限制。
协议签名
每个发送的 Dafka 消息 SHALL 以 DAFKA 协议签名 %xAA %xA0 开头。节点 SHALL 默默丢弃收到的任何不以这两个字节开头的消息。
此机制专门为绑定到临时端口的应用而设计,这些端口可能之前被其他协议使用过,并且仍然有节点试图连接。它也是一种通用的快速失败机制,用于检测格式错误的消息。
版本控制
签名后 SHALL 跟随一个版本号字节 %x01。节点 SHALL 丢弃不包含有效版本号的消息。不支持向后兼容。
协议语法
以下 ABNF 语法定义了 dafka_proto
DAFKA = join-consumer / publish / offsets
join-consumer = [S:STORE-HELLO C:CONSUMER-HELLO] / [ C:GET_HEADS ] *( S:DIRECT-HEAD [ consumer-fetch ] )
consumer-fetch = C:FETCH 1*( P:DIRECT-RECORD / S:DIRECT-RECORD )
publish = P:RECORD [ consumer-fetch / store-fetch S:ACK ]
store-fetch = S:FETCH 1*( ( P:DIRECT-RECORD / S:DIRECT-RECORD ) [ S:ACK ] )
offsets = P:HEAD [ consumer-fetch / store-fetch ]
; Record from producer to consumers and stores. Topic is the name of the
; topic. Subject is the name of the topic. Address is the address of the
; producer (partition).
RECORD = signature %d'M' version address subject sequence content
signature = %xAA %xA5 ; two octets
version = number-1 ; Version = 1
address = string ;
subject = string ;
sequence = number-8 ;
content = frame ;
; Direct record from a producer or a store to a consumer. Topic is the
; address of the requestor. Subject is the name of the topic. Address is
; the address of the producer (partition).
DIRECT-RECORD = signature %d'D' version address subject sequence content
version = number-1 ; Version = 1
address = string ;
subject = string ;
sequence = number-8 ;
content = frame ;
; Consumer or store publish this message when a record is missing.
; Either producer or a store can answer. Topic is the address of the
; producer (partition). Subject is the name of the topic. Address is the
; address of this message's sender. Count is the number of messages to
; fetch starting with the record identified by sequence.
FETCH = signature %d'F' version address subject sequence count
version = number-1 ; Version = 1
address = string ;
subject = string ;
sequence = number-8 ;
count = number-4 ;
; Ack from a stores to a producer. Topic is the address of the producer
; (partition). Subject is the name of the topic.
ACK = signature %d'K' version address subject sequence
version = number-1 ; Version = 1
address = string ;
subject = string ;
sequence = number-8 ;
; Head from producer to consumers and stores. Topic is the name of the
; topic. Subject is the name of the topic. Address is the address of the
; producer (partition).
HEAD = signature %d'H' version address subject sequence
version = number-1 ; Version = 1
address = string ;
subject = string ;
sequence = number-8 ;
; Head from producer or store to a consumers. Topic is the name of the
; topic. Subject is the name of the topic. Address is the address of the
; producer (partition).
DIRECT-HEAD = signature %d'E' version address subject sequence
version = number-1 ; Version = 1
address = string ;
subject = string ;
sequence = number-8 ;
; Get partition heads from stores send by a consumer. Topic is the name
; of the topic. Address is the address of the consumer.
GET-HEADS = signature %d'G' version address
version = number-1 ; Version = 1
address = string ;
; Hello message from a consumer to a store. Topic is the store's
; address. Address is the address of the consumer. Subjects is a list of
; all topic the consumer is subscribed to.
CONSUMER-HELLO = signature %d'W' version address subjects
version = number-1 ; Version = 1
address = string ;
subjects = strings ;
; Hello message from a store to a consumer. Topic is the consumer's
; address. Address is the address of the store.
STORE-HELLO = signature %d'L' version address
version = number-1 ; Version = 1
address = string ;
; A list of string values
strings = strings-count *strings-value
strings-count = number-4
strings-value = longstr
; A frame is zero or more octets encoded as a ZeroMQ frame
frame = *OCTET
; 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-4 = 4OCTET
number-8 = 8OCTET
Dafka 命令
所有命令都以协议签名 (%xAA %xA5) 开头,然后是命令标识符,然后是协议版本号 (%x30)。
每个命令 MUST 包含作为第一个帧的 ZeroMQ 主题名称,它发布到该主题。此帧称为主题帧。
STORE-HELLO 命令
当存储节点在其发布者套接字上收到 STORE-HELLO 订阅时,它 SHALL 在其发布者套接字上向订阅者发送 STORE-HELLO 命令。STORE-HELLO 命令有一个字段:存储节点的地址。
发布者套接字的主题帧被设置为 STORE-HELLO 命令的 ID 与通过订阅消息接收到的消费者地址的拼接。
CONSUMER-HELLO 命令
当消费者在其消费者套接字上收到 STORE-HELLO 命令时,它 SHOULD 回复一个 CONSUMER-HELLO 命令。CONSUMER-HELLO 命令有两个字段:消费者的地址以及它订阅的 dafka 主题列表。如果消费者没有订阅任何 dafka 主题,它 MUST NOT 发送 CONSUMER-HELLO 命令。
发布者套接字的主题帧被设置为 CONSUMER-HELLO 命令的 ID 与通过 STORE-HELLO 命令接收到的存储节点地址的拼接。
RECORD 命令
当生产者希望发布记录时,它 SHALL 使用 RECORD 命令。RECORD 命令包含三个字段:此记录发布到的 dafka 主题、此记录在分区中的偏移量以及记录内容,记录内容定义为一个 ZeroMQ 帧。Dafka 不支持多帧消息内容。
生产者 MUST NOT 在收到至少一个 ACK 命令之前删除已发送的记录。
XPUB 的主题帧被设置为 RECORD 命令的 ID 与记录发布到的 dafka 主题名称的拼接。
ACK 命令
当存储节点保存了来自生产者的记录时,它 SHALL 通过向同一生产者发送 ACK 命令来确认。ACK 命令包含三个字段:此记录发布到的 dafka 主题,以及已保存记录在分区中的偏移量。
XPUB 的主题帧被设置为 ACK 命令的 ID 与需要确认的生产者地址的拼接。
HEAD 命令
在生产者发布其第一个记录后,它 SHALL 以固定的间隔发送 HEAD 命令。HEAD 命令包含两个字段:此生产者发布记录到的 dafka 主题,以及最后发布的记录的偏移量。
XPUB 的主题帧被设置为 HEAD 命令的 ID 与生产者(分区)地址的拼接。
FETCH 命令
如果消费者或存储节点检测到它在分区中丢失了记录,它 SHALL 发送 FETCH 命令。FETCH 命令包含三个字段:丢失记录的 dafka 主题、第一个丢失记录的偏移量以及丢失记录的数量。
XPUB 的主题帧被设置为 FETCH 命令的 ID 与消费者地址的拼接。
DIRECT-RECORD 命令
收到 FETCH 命令后,生产者或存储节点 SHALL 检查是否已存储请求的记录。如果请求的记录可用,它 SHALL 发送 DIRECT-RECORD 命令。DIRECT-RECORD 命令包含三个字段:此记录发布到的 dafka 主题、记录在分区中的偏移量以及记录内容,记录内容定义为一个 ZeroMQ 帧。
FETCH 命令请求的记录 SHALL 按其偏移量以升序发送。
XPUB 的主题帧被设置为 DIRECT-RECORD 命令的 ID 与生产者或存储节点地址的拼接。
GET-HEADS 命令
如果消费者订阅了一个 dafka 主题,它 SHALL 发送 GET-HEADS 命令以获取该主题每个分区的头部信息。
XPUB 的主题帧被设置为 GET-HEADS 命令的 ID 与请求分区头部信息的主题的拼接。
DIRECT-HEAD 命令
收到 GET-HEADS 命令后,如果生产者向 GET-HEADS 命令请求的 dafka 主题发布记录,它 SHALL 发送 DIRECT-HEAD 命令。
收到 GET-HEADS 命令后,存储节点 SHALL 为其在 GET-HEADS 命令请求的 dafka 主题上存储记录的每个分区发送一个 DIRECT-HEAD 命令。
XPUB 的主题帧被设置为 DIRECT-HEAD 命令的 ID 与生产者或存储节点地址的拼接。
ZeroMQ 订阅
因为 Dafka 节点通过 PUB 和 SUB 套接字连接,每个节点 MUST 向其他节点注册订阅以便接收消息。以下部分描述了哪些节点类型需要哪些订阅。
生产者订阅
生产者 SHALL 通过拼接 ACK 命令 ID 和等于生产者地址的分区名称来订阅其自身分区的 ACK 命令。
生产者 SHALL 通过拼接 FETCH 命令 ID 和等于生产者地址的分区名称来订阅其自身分区的 FETCH 命令。
消费者订阅
消费者 SHALL 通过拼接 DIRECT-RECORD 命令 ID 和其消费者地址来订阅发送给它的 DIRECT-RECORD 命令。
消费者 SHALL 通过拼接 DIRECT-HEAD 命令 ID 和其消费者地址来订阅发送给它的 DIRECT-HEAD 命令。
消费者 SHALL 通过拼接 STORE-HELLO 命令 ID 和消费者地址来订阅 STORE-HELLO 命令。
对于消费者订阅的每个 dafka 主题,它 SHALL 通过拼接 RECORD 命令 ID 和主题名称来订阅 RECORD 命令。
对于消费者订阅的每个 dafka 主题,它 SHALL 通过拼接 HEAD 命令 ID 和主题名称来订阅 HEAD 命令。
对于消费者订阅的每个 dafka 主题的每个分区,它 MAY 通过拼接 ACK 命令 ID 和等于生产者地址的分区名称来订阅 ACK 命令。
存储节点订阅
存储节点 SHALL 通过订阅 RECORD 命令 ID 来订阅所有 RECORD 命令。
存储节点 SHALL 通过订阅 HEAD 命令 ID 来订阅所有 HEAD 命令。
存储节点 SHALL 通过拼接 DIRECT-RECORD 命令 ID 和其存储节点地址来订阅发送给它的 DIRECT-RECORD 命令。
存储节点 SHALL 通过订阅 FETCH 命令 ID 来订阅所有 FETCH 命令。
存储节点 SHALL 通过订阅 GET-HEADS 命令 ID 来订阅 GET-HEADS 命令。
存储节点 SHALL 通过订阅 CONSUMER-HELLO 命令 ID 来订阅 CONSUMER-HELLO 命令