亚马逊AWS官方博客
使用机架感知功能降低 Amazon MSK 流量成本
![]() |
本文主要介绍 Amazon MSK 流量成本优化的最佳实践,通过 KIP-392 实现 consumer 对 MSK Broker 在同可用区内消息就近读取,可以降低跨可用区数据传输成本、提升 consumer 的消费效率。
Amazon MSK 拓扑结构
Amazon MSK 是完全托管、高度可用的 Apache Kafka 服务,为了保证 MSK 服务自身的高可用性,MSK 的多个 Kafka Broker 均衡分布在一个 AWS Region 的多个 availability zone 中,这样有效避免了底层硬件的单点故障或availability zone级别故障导致的 MSK 服务不可用。此外 MSK 在开源的 Kafka 的基础上做了一些优化和增强,在 100% 兼容 Kafka 的基础上还额外提供分层存储、自动存储扩展、自动 partition 管理、MSK Serverless、Broker 配置灵活调整以及 AWS Graviton CPU 等特色功能;这些功能给客户带来了高性能、低成本、灵活可靠、免运维的 Kafka 使用体验。
当我们创建 MSK 的时候,我们需要选择 availability zone 的数量(至少为 2),并指定每个 availability zone 中的 broker 数量,因此 MSK 的 broker 总是在 availability zone 中处于均衡分布状态。
如下图是一个 3 availability zone,每个 availability zone 中 2 个 broker 的 MSK 集群结构:
![]() |
当我们按照 MSK 最佳实践和 MSK 可靠性最佳实践为 Kafka 中的 topic 设置了 partition 的默认 replication factor 为 3、in-sync replicas 为 2 时,producer 向 MSK 写入的 message 会先送达到 partition leader 所在的 broker,然后 message 会在 MSK 集群内部自动向另外的 2 台 partition replica 所在的 broker 同步;只有这个 partition 所处的 3 台 broker 中的至少 2 个确认写入时这条 message 才被认为是 committed。通过设置 MSK 集群中 topic 的 partition 多副本备份和多数副本确认机制,结合 producer 的 acks=all 能保证 MSK 集群中消息的可靠性。
producer 写入 message 的示意图如下:
![]() |
在默认情况下,消费 MSK 中的消息和开源的 Apache Kafka 并无差别,producer 向 topic 中发送的消息可以大致均衡的分布在这个 topic 的多个 partition 上;每个 partition 的 leader partition 负责承载 producer 的写操作和 consumer 的读操作,其他的 replica partition 在 Kafka 集群内部同步 leader 的消息,replica partition 只负责保证数据的可靠性但是不承载读写操作压力。
![]() |
经过上面的拓扑分析,我们可以发现在理想情况下上述的结构可以在多个 broker 间均匀的分散读写压力,但是也有 3 个细节问题:
- 存在跨可用区流量成本,结合结合 network traffic cost 我们可以发现对 producer 产生的一批消息而言,大致有 2/3 的消息是跨可用区被 consumer 消费的。理论上当可用区为 n 时,可能有 1-1/n 的消息被消费时产生额外的跨可用区流量成本。
- 当消费者跨可用区读取消息时,可能会有轻微的网络延迟上升。
- 极端情况下,如果用户对消息的 partition key 设置不合理时会产生 partition 倾斜,leader partition 会承载更大的读写压力并加剧上述 1、2 项问题。当然在任意情况下我们都要合理设置 partition key 来避免倾斜问题。
![]() |
Amazon MSK 支持 KIP-392 机架感知
KIP-392 在 Apache Kafka 2.4 中被引入,旨在通过机架感知来为 consumer 提供同机架内的消息消费能力;这会降低 consumer 跨机架消费消息的网络延迟、充分利用同机架的更高的网络带宽提升消费速度。Amazon MSK 从 2.4.1 开始引入 KIP-392 特性并提供 KIP-480 粘性分区器提升消费组分区分配效率,详情请看:Amazon MSK 增加对 Apache Kafka 版本 2.4.1 的支持。
利用 KIP-392 新特性,MSK 集群的消费者可以在同可用区内就近消费 partition 中的数据,这个特性允许消费者从 replica partition 所在的节点进行数据拉取并消费,而以往消费者只能在 leader partition 上进行数据拉取。
![]() |
通过上图,我们可以发现 consumer 消费消息的流量可以保持在同可用区内,理论上这将消费者产生的流量费降为 0。在某些情况下 consumer 所在的可用区中没有 broker 或者 broker 短暂处于维护期时,这个 consumer 的消费逻辑可以回退到标准的 Apache Kafka 状态。
使用 spring integration kafka 完成就近读取
要正确启用 KIP-392,我们必须让 broker 和 consumer 间完成 rack 协商,这需要 broker 和 consumer 都拥有正确的 rack id消息。在 Amazon MSK 中这个 rack id 就是 availability zone id,实际上 availability zone id 是 Amazon EC2 实例元数据的一部分,这里我们后续再展开讲解。
为 MSK Broker 开启 KIP-392 机架感知
在 MSK 中 broker 自带了 availability zone id,我们还需要先设置好 MSK 集群配置,加上 replica.selector.class=org.apache.kafka.common.replica.RackAwareReplicaSelector 来让 MSK 集群使用 RackAwareReplicaSelector 来正确匹配到 consumer 传来的 rack id。配置修改好后需要我们重启 MSK 集群生效配置。
![]() |
为 consumer 开启 KIP-392 机架感知
按照 KIP-392 的规范,在 broker 设置好 RackAwareReplicaSelector 后,consumer 只需要传入 rack id 即可,目前大多数语言的 Kafka 客户端都支持该功能,比如 java、go、python 等。
下面我以 java 为例进行演示,首先一个 AWS 上运行的程序无论是在 EC2 还是容器或者其他的地方,它的宿主机必然是处于某一个 aws region 内的某个可用区的。但是为了保证高可用性,我们一般会对这个程序进行多可用区部署,程序所在的availability zone id就是一个变量,所以我们无法将 availability zone id 写死在配置文件里面或者在编译阶段固定下来。最佳的方式是在程序启动时做到动态加载。
我们可以使用 AWS SDK 或者直接使用 http 访问 EC2 实例的实例元数据来获取 availability zone id;对 AWS Java SDK v2 而言,我们可以使用 Ec2MetadataClient 来读取它。
具体的核心代码为:
![]() |
在上图中,我使用核心代码读取了程序所在的 ec2 宿主机上的 metadata 后得到了 availability zone id,上述代码得到 availability zone id 后立即将它设置为环境变量,名称为 AVAILABILITY_ZONE_ID,代码片段会在 spring 程序容器加载前执行,发生于 Kafka 客户端初始化前。
然后我们在 spring 的 application.yaml 配置文件中设置 client rack,spring 启动时会读取 AVAILABILITY_ZONE_ID 环境变量来初始化 Kafka 客户端,这样就完成了全部的 KIP-392 设置,如下图红色部分。图中绿色部分也是推荐的最佳实践,通过开启 producer 消息压缩可以在牺牲少量 CPU 的情况下减少客户端和 MSK Broker 间的数据传输量、减少 EC2 的带宽消耗、减少 MSK 的数据存储成本。
![]() |
最后我们将 org.apache.kafka.clients.consumer.internals.AbstractFetch 的日志设置为 debug 级别,这会打印关键的调试信息。
![]() |
最后编译后运行的日志如下:
启动时程序打印出了 consumer 的连接设置,我们可以看到,正确地得到了 availability zone id。
![]() |
持续观察 debug 日志,我们会看到 consumer 会不断地在同 availability zone id 内拉取 committed 后的消息。
![]() |
总结
KIP-392 是一个很实用的 Kafka 特性,当我们消费 MSK 中的消息时通过它提供的机架感知技术可以实现就近数据拉取。和以前从 leader partition 拉取相比,它减少了 leader partition 所在的 broker 的压力并消除了跨可用区流量费,是一个非常经济高效的流量降本技术。如果结合消息压缩功能,使用 MSK 的整体流量成本会进一步的降低。
本文参考
- Reduce network traffic costs of your Amazon MSK consumers with rack awareness
- KIP-392: Allow consumers to fetch from closest replica
- Amazon MSK 增加对 Apache Kafka 版本4.1 的支持
- Overview of Data Transfer Costs for Common Architectures
- Consuming messages from closest replicas in Apache Kafka 2.4.0 and AMQ Streams
- Improving Performance and Reducing Cost Using Availability Zone Affinity
- MSK 可靠性最佳实践
- 如何通过互联网安全地访问Amazon Managed Streaming for Apache Kafka (Amazon MSK) 集群
- Message compression in Apache Kafka
- Apache Kafka Message Compression