改进的节点间消息传递
Apache Cassandra 4.0 在节点间消息传递方面添加了一些新的改进。
优化的节点间消息传递协议
节点间消息传递协议已优化 (CASSANDRA-14485)。以前,即使在建立初始连接/会话时已发送过一次 IPAddressAndPort
,发送的每条消息中都会包含发送者的 IPAddressAndPort
。在 Cassandra 4.0 中,IPAddressAndPort
已从发送的每条单独消息中删除,仅在启动连接/会话时发送。
另一个改进是,在几个实例(列出)中,固定 4 字节整数值已被 vint
替换,因为 vint
几乎总是小于 1 字节
-
paramSize
(标头中的参数数量) -
每个单独的参数值
-
payloadSize
NIO 消息传递
在 Cassandra 4.0 中,点对点(节点间)消息传递已通过 Netty (CASSANDRA-8457) 切换到非阻塞 I/O (NIO)。
作为序列化格式,每条消息都包含一个标头,其中包含几个固定字段,一个可选的键值参数部分,以及消息有效负载本身。注意:标头中的 IP 地址可以是 IPv4(4 字节)或 IPv6(16 字节)。
下图显示了 IPv4 地址,以简明起见。
1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 6 6 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | PROTOCOL MAGIC | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Message ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Addr len | IP Address (IPv4) / +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / | Verb / +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / | Parameters size / +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / | Parameter data / +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Payload size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | / / Payload / / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
单个参数具有一个字符串键和一个字节数组值。键与其长度一起序列化,编码为两个字节,后跟字符串的 UTF-8 字节编码。主体与其长度一起序列化,编码为四个字节,后跟值的字节。
排队消息的资源限制
通过对排队出站消息的数量(以消息的 serializedSize
衡量)实施严格的资源限制 (CASSANDRA-15066),提高了系统稳定性。同时实施了三个独立的限制,以确保在任何合理的故障组合情况下都能始终取得进展,而不会影响节点的稳定性。
-
对排队以供传递到其他节点的消息以及等待从集群中的其他节点到达并进行处理的消息,实施全局、每个端点和每个连接限制。这些限制适用于发送或接收的消息的在线大小。
-
基本每个链接限制在实施任何端点或全局限制之前单独使用。每个节点对都有三个链接:紧急、小和大型。任何给定节点最多可以有
N*3 * (internode_application_send_queue_capacity in bytes + internode_application_receive_queue_capacity in bytes)
的消息数据排队,而它们之间没有任何协调,尽管在实践中,使用令牌感知路由,只有 RF*tokens 节点需要与重要的带宽进行通信。 -
每个端点限制对超过每个链接限制的所有消息实施,同时与全局限制一起,对集群中单个节点的所有链接实施。全局限制对超过每个链接限制的所有消息实施,同时与每个端点限制一起,对集群中任何节点的所有链接实施。以下配置设置已添加到
cassandra.yaml
中,用于排队消息的资源限制。
internode_application_send_queue_capacity: 4MiB internode_application_send_queue_reserve_endpoint_capacity: 128MiB internode_application_send_queue_reserve_global_capacity: 512MiB internode_application_receive_queue_capacity: 4MiB internode_application_receive_queue_reserve_endpoint_capacity: 128MiB internode_application_receive_queue_reserve_global_capacity: 512MiB
用于消息传递指标的虚拟表
通过使用虚拟表保存节点间入站和出站消息传递的指标 (CASSANDRA-15066),改进了指标。对于入站消息传递,已添加了一个虚拟表 (internode_inbound
) 来保存以下指标:
-
由于错误而无法序列化或刷新消息的字节数和数量
-
已安排的消息的字节数和数量
-
成功处理的消息的字节数和数量
-
成功接收的消息的字节数和数量
-
被节流的消息的纳秒数和数量
-
已过期消息的字节数和数量
-
已恢复和未恢复的损坏帧
已为出站节点间消息传递添加了一个单独的虚拟表 (internode_outbound
)。出站虚拟表保存以下指标:
-
待处理消息的字节数和数量
-
已发送消息的字节数和数量
-
已过期消息的字节数和数量
-
由于错误而无法发送的消息的字节数和数量
-
过载消息的字节数和数量
-
活动连接数
-
连接尝试次数
-
成功连接尝试次数
提示消息传递
已添加一个专门版本的提示消息,该消息采用已编码为 ByteBuffer
的提示并逐字发送。当将当前消息传递版本的提示文件分派到具有相同消息传递版本的节点时,这是一个优化,这是最常见的情况。它节省了额外的 ByteBuffer
分配,一个冗余的提示反序列化-序列化循环。
节点间应用程序超时
已在 cassandra.yaml
中添加了一个配置设置,用于连接在应用程序空间中不可写入的最大持续时间。
# internode_application_timeout_in_ms = 30000
其他一些新功能包括记录消息大小以跟踪消息以跟踪查询。
针对本地请求优化的 Paxos 准备和提议阶段
在 4.0 之前的版本中,即使请求要本地服务,Paxos 准备和提议消息始终在 Cassandra 中通过整个 MessagingService
堆栈,我们可以增强并使本地请求在不涉及 MessagingService
的情况下进行服务。Cassandra 中的其他地方也做了类似的事情,这些地方跳过了本地请求的 MessagingService
阶段。
如果我们在 4.0 之前启用了跟踪并运行了一个轻量级事务,则它看起来像这样
Sending PAXOS_PREPARE message to /A.B.C.D [MessagingService-Outgoing-/A.B.C.D] | 2017-09-11 21:55:18.971000 | A.B.C.D | 15045 … REQUEST_RESPONSE message received from /A.B.C.D [MessagingService-Incoming-/A.B.C.D] | 2017-09-11 21:55:18.976000 | A.B.C.D | 20270 … Processing response from /A.B.C.D [SharedPool-Worker-4] | 2017-09-11 21:55:18.976000 | A.B.C.D | 20372
提议阶段也是如此。
在 4.0 版本中,针对本地请求优化的 Paxos 准备和提议阶段 (CASSANDRA-13862)。
质量保证
在 4.0 版本中 (CASSANDRA-15066),进行了一些其他质量保证改进。
防止损坏
以前,默认情况下,数据中心内部的节点间消息不受损坏保护,因为只有 LZ4 提供了任何完整性检查。所有发送到 4.0 后节点的消息都写入显式帧,这些帧可以是
-
LZ4 编码
-
CRC 保护
未保护选项仍然可用。
弹性
为了提高弹性,所有帧都使用单独的 CRC 保护标头写入,分别为 8 字节和 6 字节。如果此标头发生损坏,则必须像以前一样重置连接。如果在标头之外的任何地方发生损坏,则会跳过损坏的帧,保持连接完整,并避免不必要地丢失任何消息。
以前,流中的任何问题都会导致连接重置,并导致任何正在传输的消息丢失。
效率
入站和出站消息的总体内存使用量和字节混洗次数都减少了。
出站 Netty LZ4 编码器维护一个块大小缓冲区 (64KiB),在生成任何压缩帧之前,该缓冲区会被填充。我们的帧编码器避免了这种冗余复制,并释放了每个端点 192KiB。
入站,帧解码器保证仅复制解析帧所需的字节数,并且绝不存储超过必要的字节数。此改进对 LZ4 连接应用了两次,改进了消息解码和 LZ4 帧解码。
入站路径
4.0 版本对入站路径进行了一些改进。
根据在标志中设置的特定连接上是否预期大小消息,使用适当的消息处理程序。NonblockingBufferHandler
在事件循环中运行,用于处理小消息,而 BlockingBufferHandler
在事件循环之外运行,用于处理大消息。InboundMessageHandler
的单个实现通过从字节流中推导出传入消息的大小,有效地处理任何大小的消息。除了从流中推导出消息的大小之外,在尝试反序列化整个消息之前,会主动读取传入消息的过期时间。如果在遇到消息时它已过期,则该消息将完全跳过字节流。如果消息在接收方仍然无法反序列化 - 例如,由于表 ID 或列未知 - 字节将被跳过,而不会丢弃整个连接并丢失所有缓冲的消息。立即将失败原因的回复发送回协调器节点,而不是等待协调器回调过期。此逻辑扩展到损坏的帧;损坏的帧将被安全地跳过,而不会丢弃连接。
入站路径对内存利用率施加严格的限制。具体来说,所有已解析但未处理的消息所占用的内存是有限制的 - 在每个连接、每个端点和全局基础上。一旦连接超过其本地未处理容量并且无法从每个端点和全局预留中借用任何许可证,它就会简单地停止处理更多消息,提供自然的背压 - 直到重新获得足够的容量。
添加了消息大小限制
Cassandra 4.0 之前的版本无法保护服务器免受为节点间消息对象分配巨大缓冲区的攻击。添加消息大小限制将有助于处理诸如集群参与者故障等问题。4.0 版本引入了类似于最大变异大小的最大消息大小配置参数 - 默认情况下设置为端点预留容量。
在反序列化节点间消息时从未知表中恢复
如 (CASSANDRA-9289) 中所述,从另一个节点的消息中看到未知表时能够优雅地恢复会很好。4.0 之前的版本,我们会关闭连接并重新连接,这会导致其他并发查询失败。4.0 版本通过使用 TrackedDataInputPlus
包装消息流,捕获 UnknownCFException
并跳过此消息中的剩余字节来解决此问题。TCP 不会关闭,它将保持连接以处理其他消息。