本文作者是一位软件工程师, 他对 20 位开发人员和数据科学家使用 Apache Kafka 的方式进行了最大限度得深入研究, 最终将生产实践环节需要注意的问题总结为本文所列的 20 条建议.
Apache Kafka 是一个广受欢迎的分布式流媒体平台, New Relic,Uber 以及 Square 等数千家公司都在使用它构建可扩展, 高吞吐量, 可靠的实时流媒体系统. 例如, New Relic 的 Kafka 集群每秒处理超过 1500 万条消息, 总数据速率接近 1 Tbps.
Kafka 在应用程序开发人员和数据科学家中非常受欢迎, 因为它极大简化了数据流的处理过程. 但是, Kafka 在 Scala 上实践会比较复杂. 如果消费者无法跟上数据流, 并且消息在他们看到之前就消失了, 那么具有自动数据保留限制的高吞吐量发布 / 订阅模式并没有多大用. 同样, 如果托管数据流的系统无法扩展以满足需求或者不可靠, 也没有什么用.
为了降低这种复杂性, 作者将可能的问题分为 4 大类共 20 条, 以方便用户理解:
- Partitions(分区)
- Consumers(消费者)
- Producers(生产者)
- Brokers
Kafka 是一种高效分布式消息传递系统, 可提供内置数据冗余和弹性, 同时保留高吞吐量和可扩展性. 它包括自动数据保留限制, 使其非常适合将数据视为流的应用程序, 并且还支持对键值对映射建模的 "压缩" 流.
了解最佳实践之前, 你需要熟悉一些关键术语:
Message 消息: Kafka 中的记录或数据单元. 每条消息都有一个键 (key) 和一个值(value), 以及可选标题.
生产者: 生产者向 Kafka 的 topic 发布消息. 生产者决定要发布哪个 topic 分区, 可以随机 (循环) 或使用基于消息密钥的分区算法.
Broker:Kafka 在分布式系统或集群中运行, 集群中的每个节点都称为 broker.
Topic:Topic 是发布数据记录或消息的类别. 消费者订阅 topic 以读取写入其中的数据.
Topic partition:topic 分为多个分区, 每个消息都有一个偏移量. 每个分区通常至少复制一或两次. 每个分区都有一个 leader 和至少一个副本(数据副本), 这些副本存在于 follower 身上, 可以防止 broker 失败. 集群中的所有 broker 都是 leader 和 follower, 但是代理最多只有一个 topic partition 副本, leader 用于所有读写操作.
偏移: 为分区内的每条消息分配一个偏移量, 这是一个单调递增整数, 用作分区内消息的唯一标识符.
消费者: 消费者通过订阅 topic partition 读取 Kafka 主题的消息, 消费应用程序, 并处理消息以完成所需工作.
Consumer group: 消费者可以组织成消费者群组, 分配 topic partition 以平衡组中所有使用者. 在消费者群组中, 所有消费者都在负载均衡模式下工作. 换句话说, 组中每个消费者都将看到每条消息. 如果一个消费者离开, 则将该分区分配给该组中的其他消费者, 这个过程称为再平衡. 如果组中的消费者多于分区, 则一些消费者将闲置. 如果组中的消费者少于分区, 则某些消费者将使用来自多个分区的消息.
Lag: 当消费者无法从分区中读取消息, 消费者就会出现 Lag, 表示为分区顶部后的偏移数. 从 Lag 状态恢复所需的时间取决于消费者每秒消耗消息的速度:
time = messages / (consume rate per second - produce rate per second)
第一部分: 使用分区的最佳实践!
在分区部分, 我们需要了解分区的数据速率, 以确保拥有正确的保留空间. 分区的数据速率是生成数据的速率. 换句话说, 它是平均消息大小乘以每秒消息数. 数据速率决定了给定时间内所需的保留空间(以字节为单位). 如果不知道数据速率, 则无法正确计算满足基本保留目标所需的空间大小. 数据速率指定了单个消费者需要支持的最低性能而保证不会出现 Lag.
除非有其他架构需求, 否则在写入 topic 时使用随机分区. 当进行大规模操作时, 分区之间的数据速率不均可能难以管理. 需要注意以下三方面:
1, 首先,"热点"(更高吞吐量)分区的消费者必须处理比消费者群组中其他消费者更多的消息, 这可能导致处理和网络瓶颈.
2, 其次, 必须为具有最高数据速率的分区调整 topic 保留空间大小, 这可能会导致 topic 中其他分区的磁盘使用量增加.
3, 最后, 在分区领导方面实现最佳平衡比简单地扩展到所有 brokers 更复杂."热点" 分区的份量可能是同一 topic 中另一分区的 10 倍.
第二部分: 使用消费者最佳实践!
如果消费者运行的 Kafka 版本低于 0.10, 请升级. 在 0.8.x 版本中, 消费者使用 Apache ZooKeeper 进行消费者群组协调, 并且许多已知错误可能导致长期运行的平衡甚至是重新平衡算法的失败(我们称之为 "重新平衡风暴"). 在重新平衡期间, 将一个或多个分区分配给使用者群组中的每个使用者. 在再平衡中, 分区所有权在消费者中不断变通, 阻止任何消费者在消费方面取得实际进展.
4, 调整消费者套接字缓冲区以进行高速获取. 在 Kafka 0.10.x 中, 参数为 isreceive.buffer.bytes, 默认为 64kB. 在 Kafka 0.8.x 中, 参数是 socket.receive.buffer.bytes, 默认为 100kB. 对于高吞吐量环境, 这两个默认值都太小, 特别是如果 brocker 和消费者之间的网络带宽延迟大于局域网(LAN). 对于延迟为 1 毫秒或更长的高带宽网络(10 Gbps 或更高), 请考虑将套接字缓冲区设置为 8 或 16 MB. 如果内存不足, 请考虑 1 MB, 也可以使用值 - 1, 这样底层操作系统可以根据网络条件调整缓冲区大小. 但是, 对于需要启动 "热点" 消费者的系统而言, 自动调整的速度可能或比较慢.
5, 设计高吞吐量消费者, 以便在有保证的情况下实施背压, 最好只消耗可以有效处理的东西, 而不是消耗太多, 以至于过程停止, 退出消费者群组. 消费者应该使用固定大小的缓冲区 (参见 Disruptor 模式), 如果在 Java 虚拟机(JVM) 中运行, 最好是在堆外使用. 固定大小的缓冲区将阻止消费者将大量数据拖到堆上, JVM 花费所有时间来执行垃圾收集而不是做你想让它处理的工作 -- 处理消息.
6, 在 JVM 上运行消费者时, 请注意垃圾回收可能对消费者产生的影响. 例如, 垃圾收集较长时间暂停可能导致 ZooKeeper 会话或者消费者组失去平衡. 对于 brocker 来说也是如此, 如果垃圾收集暂停时间过长, 则可能会从集群中退出.
第三部分: 使用生产者最佳实践!
7, 配置生产者等待确认. 这就是生产者如何知道消息实际已经发送到 brocker 上的分区. 在 Kafka 0.10.x 中, 设置为 acks; 在 0.8.x 中, 它是 request.required.acks.Kafka 通过复制提供容错功能, 因此单个节点的故障或分区 leader 的更改不会影响可用性. 如果将生产者配置为没有 ack(也称为 "fire and forget"), 则消息可能会无声地丢失.
8, 配置生产者重试次数. 默认值为 3, 通常太低. 正确的值取决于需求, 对于无法容忍数据丢失的应用程序, 请考虑 Integer.MAX_VALUE(实际上是无穷大), 这可以防止 leader 分区的 brocker 无法立即响应生产请求.
9, 对于高吞吐量生产者, 调整缓冲区大小, 特别是 buffer.memory 和 batch.size(以字节为单位). 由于 batch.size 是按分区设置的, 因此生产者性能和内存使用量可与 topic 中的分区数相关联. 这里的值取决于几个因素: 生产者数据速率(消息的大小和数量), 生成的分区数以及可用的内存量. 请记住, 较大的缓冲区并不总是好的, 如果生产者由于某种原因而停顿(例如, 一个领导者通过确认响应较慢), 在堆上缓存更多数据可能会导致更多垃圾收集.
10, 制定应用程序跟踪指标, 例如生成的消息数, 平均生成的消息大小和消耗的消息数.
第四部分: brocker 最佳实践!
11,Topic 需要 brocker 的内存和 CPU 资源, 日志压缩需要 brocker 上的堆 (内存) 和 CPU 周期才能成功完成, 并且失败的日志压缩会使 brocker 处于无限增长的分区风险中. 你可以在 brocker 上使用 tunelog.cleaner.dedupe.buffer.size 和 log.cleaner.threads, 但请记住, 这些值会影响 brocker 上的堆使用情况. 如果 brocker 抛出 OutOfMemoryError 异常, 它将关闭并可能丢失数据. 缓冲区大小和线程数将取决于要清理的主题分区数量以及这些分区中消息的数据速率和密钥大小. 从 Kafka 0.10.2.1 版本开始, 监视日志清理程序日志文件以查找 ERROR 条目是检测日志清理程序线程问题的最可靠方法.
12, 监控 brocker 的网络吞吐量. 确保使用发送 (TX) 和接收(RX), 磁盘 I/O, 磁盘空间和 CPU 使用率来执行此操作. 容量规划是维护集群性能的关键部分.
13, 在集群中的 brocker 之间分配分区 leader, 其需要大量的网络 I/O 资源. 例如, 当使用复制因子 3 运行时, leader 必须接收分区数据, 并同步传递给所有副本, 再传输给想要使用该数据的消费者. 因此, 在这个例子中, 作为领导者, 在使用网络 I/O 方面至少是 follower 的四倍, leader 必须从磁盘读取, follower 只需要写.
14, 不要忽略监视 brocker 的同步副本 (ISR) 缩减, 重复不足的分区和不受欢迎的 lesder. 这些是集群中潜在问题的迹象. 例如, 单个分区的频繁 ISR 收缩可能表明该分区的数据速率超过了 leader 为消费者和副本线程提供服务的能力.
15, 根据需要修改 Apache Log4j 属性. Kafka 代理日志记录可能会占用过多磁盘空间. 但是, 不要完全放弃日志记录, brocker 日志可能是在事件发生后重建事件序列的最佳方式, 有时也是唯一方式.
16, 禁用 topic 自动创建有关的明确策略, 定期清理未使用的 topic. 例如, 如果 x 天没有看到任何消息, 请考虑 topic 失效并将其从集群中删除, 这样可以避免在集群中创建必须管理的其他元数据.
17, 对于持续的高吞吐量代理, 请提供足够的内存以避免从磁盘系统读取, 应尽可能直接从操作系统的文件系统缓存中提供分区数据. 但是, 这意味着必须确保消费者能够跟上, 滞后的消费者将迫使 brocker 从磁盘读取.
18, 对于具有高吞吐量服务级别目标 (SLO) 的大型集群, 请考虑将 topic 隔离到 brocker 子集. 如何确定要隔离的 topic 取决于业务需求, 例如, 如果有多个使用相同集群的联机事务处理 (OLTP) 系统, 则将每个系统的 topic 隔离到 brocker 的不同子集以帮助限制事件的潜在爆炸半径.
19, 使用较新 topic 消息格式的旧客户端 (反之亦然) 会在 brocker 客户端转换格式时对 brocker 程序施加额外负担, 尽可能避免这种情况.
20, 不要认为在本地台式机上测试 brocker 代表在实际生产环境中的性能. 使用复制因子 1 对分区的环回接口进行测试是与大多数生产环境完全不同的拓扑. 通过环回可以忽略网络延迟, 并且在不涉及复制时, 接收 leader 确认所需的时间可能会有很大差异.
来源: http://bigdata.51cto.com/art/201808/582025.htm