生产环境Kafka配置最佳实践

本指南提供了生产环境中Kafka配置的最佳实践。了解如何优化主题和分区策略,实现稳健的复制和容错(包括`min.insync.replicas`),使用SSL/TLS和ACL保护集群安全,以及调整生产者和消费者设置以获得最佳性能。发现关键监控指标和策略,确保可靠且可扩展的事件流平台。

生产环境Kafka配置最佳实践

Kafka在开发环境中宽容,在生产环境中则严厉得多。一个只有一个副本的主题在代理宕机前工作正常。一个使用弱确认的生产者看起来很快,直到消息在故障期间消失。一个自动提交偏移量的消费者看似简单,直到它提交了尚未完成的工作。生产环境的Kafka配置主要是决定你愿意容忍哪些故障,并将这些决策明确化。

本指南涵盖了生产环境中Kafka配置的最佳实践,但并不假装存在一个完美的配置文件。正确的设置取决于工作负载、延迟需求、持久性要求、运维成熟度和Kafka版本。以下示例是起点,你应该在自己的流量下进行测试。

理解关键Kafka组件及其配置

在深入具体配置之前,理解Kafka的核心组件及其设置如何影响整体系统行为至关重要。

  • Brokers:存储数据并服务客户端请求的Kafka服务器。Broker配置决定了性能、资源利用率和容错能力。
  • Topics:消息被发布到的类别或馈送。
  • Partitions:主题被划分为一个或多个分区,允许在处理和存储中实现并行性。
  • Replication:跨多个Broker复制分区以确保数据持久性和可用性的过程,以防Broker故障。
  • Consumer Groups:一组消费者协作消费来自主题的消息。Kafka确保主题中的每条消息最多传递给每个消费者组中的一个消费者。

主题和分区策略

有效的主题和分区配置是Kafka可扩展性和性能的基础。

分区数量

选择正确的分区数量是一个关键决策。更多的分区允许在消费者端实现更高的并行性,意味着更多的消费者实例可以同时处理数据。然而,过多的分区可能会给Broker资源(内存、磁盘I/O)带来压力并增加延迟。一个常见的经验法则是从反映预期峰值消费者吞吐量的分区数量开始,考虑到如果以后需要,你可能想添加更多分区。

  • 考虑因素:Broker能处理的最大分区数量受其内存限制。每个分区需要为其领导者和跟随者副本分配内存。
  • 建议:目标分区数量应与消费者并行性需求保持一致,但避免过度分区。监控Broker资源利用率以找到最佳平衡点。

分区键

当生产消息时,分区键(通常是记录键)决定消息将被写入哪个分区。一致的分区对于消费者组内的有序处理至关重要。

  • partitioner.class:此生产者配置可以设置为org.apache.kafka.clients.producer.internals.DefaultPartitioner(默认,使用键的哈希)或自定义分区器。
  • 最佳实践:使用能均匀分布消息到各分区的键。如果具有相同键的消息需要按顺序处理,Kafka仅保证在分区内的顺序。

复制和容错

复制是Kafka确保数据持久性和可用性的主要机制。

复制因子

复制因子决定了跨集群维护的每个分区的副本数量。对于生产环境,强烈建议最小复制因子为3。

  • 好处:复制因子为3时,Kafka通常可以容忍一个Broker故障,同时保持另一个副本可用。确切的可用性取决于min.insync.replicas、生产者acks、领导者选举设置以及哪些Broker故障。
  • 配置:这是在主题级别设置的,可以在主题创建期间或通过kafka-topics.sh命令进行。
# 示例:创建复制因子为3的主题
kafka-topics.sh --create --topic my-production-topic --bootstrap-server kafka-broker-1:9092 --replication-factor 3 --partitions 6

min.insync.replicas

此Broker配置设置指定在写操作被认为成功之前必须确认的最小副本数。对于复制因子为N的主题,设置min.insync.replicas=M(其中M < N)确保只有在M个副本确认后写操作才被确认。为防止数据丢失,min.insync.replicas通常应设置为N-1N/2 + 1,具体取决于你的可用性和持久性权衡。

  • 建议:对于关键主题,将min.insync.replicas设置为replication_factor - 1。这确保在确认写操作之前至少有两个副本(在3副本设置中)拥有数据,从而防止领导者故障时的数据丢失。
  • 配置:这是一个Broker级别的配置,也可以按主题设置。
# broker.properties
min.insync.replicas=2

# 主题级别配置(覆盖Broker设置)
# kafka-configs.sh --alter --topic my-critical-topic --bootstrap-server ... --add-config min.insync.replicas=2

领导者选举和控制器

Kafka使用一个控制器Broker来管理集群状态,包括分区领导权。稳健的控制器配置至关重要。

  • controller.quorum.voters:在基于KRaft的集群中,这指定了控制器仲裁投票者。基于ZooKeeper的集群使用不同的控制平面设置,因此不要盲目地在架构之间复制此设置。
  • num.io.threadsnum.network.threads:这些Broker设置控制用于处理I/O和网络请求的线程数。根据工作负载和可用CPU进行调整。

生产者和消费者配置

优化生产者和消费者设置是实现高吞吐量和低延迟的关键。

生产者配置

  • acks:控制需要从副本接收的确认数量。设置acks=all(或-1)提供最强的持久性保证。结合min.insync.replicas,这对生产环境至关重要。
  • retries:设置为高值(例如Integer.MAX_VALUE)以确保临时故障不会导致消息丢失。有效使用max.in.flight.requests.per.connection与重试。
  • max.in.flight.requests.per.connection:控制可以发送到Broker的未确认请求的最大数量。旧客户端通常使用1以避免重试导致的重新排序。现代幂等生产者可以在更高安全限制下保持顺序,但请检查你的客户端版本和设置。
  • batch.sizelinger.ms:这些设置控制消息批处理。更大的批次可以提高吞吐量,但会增加延迟。linger.ms添加一个小延迟以允许更多消息被批量处理。
# producer.properties
acks=all
retries=2147483647
max.in.flight.requests.per.connection=1
batch.size=16384
linger.ms=5

消费者配置

  • auto.offset.reset:对于生产环境,通常首选latest以避免在重启时重新处理旧消息。如果需要从头开始重新处理消息,可以使用earliest
  • enable.auto.commit:设置为false以实现可靠处理。手动提交让你控制何时提交偏移量,防止消息重新传递或丢失。使用commitSync()commitAsync()进行显式提交。
  • max.poll.records:控制单次poll()调用返回的最大记录数。调整以管理处理负载并防止消费者重新平衡。
  • isolation.level:在使用Kafka事务时设置为read_committed,以确保消费者只读取已提交的消息。
# consumer.properties
group.id=my-consumer-group
auto.offset.reset=latest
enable.auto.commit=false
isolation.level=read_committed
max.poll.records=500

安全考虑

在生产环境中保护Kafka集群是不可协商的。

认证和授权

  • SSL/TLS:加密客户端和Broker之间以及Broker之间的通信。这需要生成和分发证书。
  • SASL(简单认证和安全层):使用SASL机制如GSSAPI(Kerberos)、PLAIN或SCRAM进行客户端认证。
  • 授权(ACLs):配置访问控制列表(ACLs)以定义哪些用户或主体可以对哪些资源(主题、消费者组)执行特定操作(读取、写入、创建主题等)。

加密

  • ssl.enabled.protocols:确保使用安全协议如TLSv1.2TLSv1.3
  • ssl.cipher.suites:配置强密码套件。

配置示例(使用SASL over TLS的生产者)

security.protocol=SASL_SSL
sasl.mechanism=PLAIN
sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="myuser" password="mypassword";
ssl.truststore.location=/path/to/truststore.jks
ssl.truststore.password=password

性能调优和监控

持续监控和调优对于保持最佳性能至关重要。

Broker调优

  • num.partitions:虽然这是主题级别的设置,但Broker需要处理总分区数。监控CPU、内存和磁盘I/O。
  • log.segment.byteslog.roll.hours:控制日志段的大小和滚动频率。较小的段可能导致更多的打开文件句柄和增加的开销。较大的段可能每个段消耗更多磁盘空间,但减少开销。
  • message.max.bytes:消息的最大字节数。确保这足够大以满足你的用例,但不要过大。
  • replica.fetch.max.bytes:控制跟随者副本每次获取请求的最大字节数。调整以平衡获取效率和内存使用。

JVM调优

  • 堆大小:为运行Kafka的JVM分配足够的堆内存。监控堆使用情况和GC活动。
  • 垃圾收集器:选择合适的GC算法(例如,G1GC通常推荐用于Kafka)。

监控

使用Prometheus/Grafana、Datadog或特定于Kafka的监控解决方案实施全面监控。

  • 关键指标:监控Broker健康、主题吞吐量、消费者滞后、复制状态、请求延迟和资源利用率(CPU、内存、磁盘、网络)。
  • 告警:为关键条件设置告警,如高消费者滞后、Broker无响应或磁盘空间耗尽。

消费者组重新平衡

当消费者加入或离开组,或分区被重新分配时,会发生消费者组重新平衡。频繁的重新平衡可能会中断处理。

  • session.timeout.ms:Broker等待消费者发送心跳的时间,超过此时间则认为消费者死亡。较低的值意味着更快的检测,但可能导致由于网络故障而提前重新平衡。

  • heartbeat.interval.ms:消费者发送心跳的频率。应显著小于session.timeout.ms

  • max.poll.interval.ms:消费者两次poll调用之间的最大时间。如果消费者处理消息并再次poll的时间超过此值,它将被视为死亡,触发重新平衡。确保你的消费者能在此间隔内处理消息。

  • 提示:优化消费者处理逻辑,使其在max.poll.interval.ms内完成工作,并避免因消费者缓慢而导致不必要的重新平衡。

我会明确决定的生产默认值

不要将重要的Kafka行为留给意外的默认值。一些默认值对于一般用途是合理的,但生产系统需要与数据匹配的决策。

对于关键事件流,在集群有足够Broker和机架支持的情况下,使用复制因子3或更多。将其与生产者的acks=all和三个副本主题上的min.insync.replicas=2配对。这种组合意味着只有当领导者和至少一个同步跟随者拥有数据时,写操作才被确认。如果太多副本失去同步,生产者会收到错误,而不是默默地接受较弱的持久性。

这种权衡是有意的。在故障期间,高度持久的主题可能会拒绝写入,而不是确认仅在一个Broker上的数据。某些系统对于某些遥测或点击流数据更倾向于可用性而非持久性。如果这是一个有意识的选择,那没问题。当没有人意识到主题是这样配置的时候,就很危险。

对于数据丢失不可接受的主题,禁用不干净的领导者选举。不干净的选举可以通过选举一个不同步的副本使分区重新上线,但根据故障历史和生产设置,该副本可能缺少已确认的记录。对于关键数据,保持不可用通常比在没有警告的情况下丢失或回滚记录更好。

分区数量:为吞吐量和运维选择

分区数量控制并行性,但更多的分区并非免费。每个分区都会增加元数据、文件句柄、复制工作、领导者选举工作和恢复开销。它还会影响消费者组行为。一个具有200个分区的主题可以支持比12个分区的主题更多的消费者并行性,但它也会在Broker重启和重新平衡期间产生更多移动部件。

首先估算消费者并行性和吞吐量。如果消费服务最多运行12个实例,48个分区可能就足够了。如果你期望数百个独立的处理线程,可能需要更多。留出增长空间,因为以后增加分区可能会改变键控消息的键分布和排序行为。

排序仅在分区内保证。如果customer_id=123的所有事件必须按顺序处理,请使用基于该客户ID的稳定键。不要期望在整个主题中排序。还要注意热键。如果一个客户、租户或设备产生大量流量,基于键的分区可能会使一个分区过载,而其他分区则空闲。

对于多租户系统,考虑一个共享主题还是多个租户主题更容易操作。许多小主题可能会产生元数据开销。一个巨大的共享主题可能会使保留、访问控制和事件响应复杂化。最佳选择取决于隔离需求和流量模式。

保留是产品决策,而不仅仅是Broker设置

Kafka保留决定了数据可用于重播的时间。短保留节省磁盘但限制恢复。长保留支持回填和审计工作流,但增加存储成本和恢复时间。

根据数据的使用方式按主题设置保留。命令主题可能只需要短窗口。事件历史主题可能需要数天或数周。表示最新状态的压缩主题使用不同的模型:Kafka在压缩后保留每个键的最新值,以及删除的墓碑标记直到清理。

常见设置包括:

retention.ms=604800000
retention.bytes=-1
cleanup.policy=delete

对于压缩主题:

cleanup.policy=compact
min.cleanable.dirty.ratio=0.5
delete.retention.ms=86400000

小心大消息。当一致配置时,Kafka可以处理更大的记录,但增加message.max.bytes意味着检查生产者max.request.size、消费者fetch.max.bytes、Broker副本获取设置和内存影响。在许多系统中,将大型有效负载存储在对象存储中并通过Kafka发送引用更简单可靠。

避免痛苦的生产者设置

对于大多数生产生产者,除非有特定原因,否则启用幂等性。幂等生产有助于防止由临时故障后的重试引起的重复写入。许多现代Kafka客户端在某些配置下会自动启用它,但值得在你的生产者配置中明确做出决定。

示例生产者基线:

acks=all
enable.idempotence=true
retries=2147483647
delivery.timeout.ms=120000
request.timeout.ms=30000
linger.ms=5
batch.size=32768
compression.type=zstd

压缩选择取决于CPU预算和Kafka版本。zstd通常提供强压缩,而lz4snappy是常见的低延迟选择。使用你的有效负载进行测试。JSON日志、Avro记录、protobuf消息和已压缩的二进制数据表现不同。

批处理是另一个权衡。小的linger.ms给生产者一个短窗口来分组记录,这可以提高吞吐量和压缩。设置太高会增加延迟。对于面向用户的请求路径,请记住延迟预算。对于后台摄取,稍微多一点延迟可能是可以接受的。

不要忽略生产者错误。如果acks=allmin.insync.replicas在Broker故障期间拒绝写入,那是有用的背压。应用程序必须决定是重试、缓冲、失败请求还是路由到后备方案。记录错误并丢弃事件不是持久性策略。

匹配处理语义的消费者设置

消费者偏移量提交定义了“已处理”的含义。使用enable.auto.commit=true,客户端可能在应用程序安全完成工作之前提交偏移量。这对于可丢弃的分析可能是可以接受的,但对于支付、订单、部署或任何丢失事件会造成伤害的情况来说是有风险的。

对于可靠处理,禁用自动提交并在工作完成后提交:

enable.auto.commit=false
max.poll.records=500
max.poll.interval.ms=300000
session.timeout.ms=45000
heartbeat.interval.ms=15000
partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor

提交策略取决于应用程序。commitSync()更简单,提供清晰的故障行为,但可能增加延迟。commitAsync()可以提高吞吐量,但你需要小心处理回调失败。许多服务在成功批次后定期提交,并使下游写入幂等,以便重播安全。

如果处理一条消息可能需要很长时间,减少max.poll.records,增加max.poll.interval.ms,或将慢速工作移到内部队列后面,同时轮询循环继续负责任地运行。停止轮询太长时间的消费者会触发重新平衡,而重复的重新平衡会使整个组看起来不稳定。

对于频繁重启但返回稳定身份的消费者,使用静态成员资格。它可以减少滚动部署期间不必要的重新平衡。根据客户端支持,协作重新平衡也可以减少与急切重新平衡相比的中断。

团队可操作的安全

当流量穿越不受信任的网络或携带敏感数据时,生产Kafka应使用传输中加密。大多数组织应使用TLS进行客户端-Broker通信和Broker间通信。认证可以是双向TLS、SASL/SCRAM、Kerberos、OAuth或根据环境支持的其他机制。

ACL应足够具体以防止意外损坏。orders.created的生产者不需要写入每个主题的权限。计费的消费者组不需要更改Broker配置的权限。使用使ACL可读的命名约定,并将服务主体绑定到应用程序而不是个人。

秘密需要轮换。如果SASL凭据或密钥库手动复制到服务器上,轮换变得痛苦并最终停止发生。尽可能使用平台的秘密管理器。在暂存环境中测试轮换,包括滚动客户端重启。

还要决定谁可以创建主题。自动主题创建在开发中方便,在生产中危险。主题名称中的拼写错误可以创建一个具有默认分区、默认复制和默认保留的新主题。许多生产集群禁用自动主题创建,并通过基础设施代码或批准的工作流管理主题。

Broker和存储检查

Kafka对磁盘敏感。使用具有可预测延迟的存储,积极监控磁盘使用情况,并保持足够的空闲空间用于保留、复制追赶和操作错误。磁盘满的Broker可能造成比高CPU的Broker更大的事件。

将Kafka日志与不相关的嘈杂工作负载分开。避免将Kafka数据放在共享磁盘上,因为另一个进程可能突然消耗I/O。在云环境中,了解卷吞吐量限制、突发信用和恢复行为。一分钟内基准测试良好的磁盘在持续复制和压缩下可能仍然挣扎。

当你有多个可用区或机架时,机架感知很重要。配置Broker机架ID和主题放置,以便副本不会全部位于同一个故障域中。如果所有三个副本因一个机架或区域问题而消失,复制因子3就不那么有用了。

捕捉真正故障的监控和告警

一个有用的Kafka监控设置既监视Kafka内部,也监视客户端体验。仅Broker指标不能告诉你消费者是否跟上或生产者是否看到错误。

监控未复制分区、离线分区、活动控制器计数、请求延迟、生产和获取错误率、网络吞吐量、磁盘使用情况、磁盘I/O延迟、ISR收缩和扩展率、控制器事件队列时间、消费者滞后、重新平衡率以及客户端重试/错误计数。

消费者滞后需要上下文。在每小时接收数百万条记录的主题上,100条记录的滞后可能没问题。在低容量命令主题上,100条滞后可能很严重。尽可能根据滞后年龄或追赶时间进行告警,而不仅仅是原始记录计数。

在第一次真正故障之前,在维护窗口期间测试Broker重启。生产Kafka集群应能在计划内Broker重启后存活,而不会丢失数据,也不会让客户端感到意外。如果一个Broker重启造成重大事件,那么集群已经很脆弱了。

生产就绪检查

在宣布Kafka集群生产就绪之前,我会检查以下项目:

  1. 关键主题具有明确的分区、复制因子、保留、清理策略和min.insync.replicas
  2. 关键主题的生产者使用acks=all、支持幂等性、重试和清晰的错误处理。
  3. 消费者仅在应用程序达到其预期处理点后才提交偏移量。
  4. TLS、认证和ACL已启用并经过测试。
  5. 自动主题创建已禁用或严格控制。
  6. 监控涵盖Broker健康、客户端错误、消费者滞后、磁盘和复制。
  7. 备份或重播期望已记录。Kafka保留不能替代每个备份需求。
  8. Broker重启、客户端部署和凭据轮换程序已测试。

实际要点

Kafka生产配置是一组权衡,而不是通用配方。使持久性、排序、重播、安全和延迟选择在每个主题和每个应用程序中明确。然后通过Broker重启、客户端故障、慢速消费者和磁盘满场景测试这些选择,以免生产流量给你上一课。