一、 可靠性保证
Kafka到底在什么情况下才能保证消息不丢失呢?
一句话概括,Kafka 只对“已提交”的消息(committed message)做有限度的持久化保证。
Kafka 的可靠性保证:
- Kafka 可以保证分区消息的顺序。
- 只有当消息被写入分区的若干同步副本时,才被认为是已提交的。为什么是若干个 Broker 呢?这取决于你对“已提交”的定义。你可以选择只要 Leader 成功保存该消息就算是已提交,也可以是令所有 Broker 都成功保存该消息才算是已提交。
- 只要还有一个副本是存活的,那么已提交的消息就不会丢失。
- 消费者只能读取已提交的消息。
二、复制
kafka 的复制机制和分区的多副本架构是 kafka 可靠性保证的核心。
Kafka 的主题被分为多个分区,分区时基本的数据块。每个分区可以有多个副本,其中一个是首领。所有事件都直接发送给主副本,或者直接从主副本读取事件。其他副本只需要与首领保持同步,并即时复制最新的事件。当主副本不可用时,其中一个同步副本将成为新的主副本。
三、Broker 的可靠性
broker 有 3 个配置参数会影响 Kafka 消息存储的可靠性。
复制系数
replication.factor的作用是设置每个分区的副本数。replication.factor是主题级别配置;
default.replication.factor是 broker 级别配置。
副本数越多,数据可靠性越高;但由于副本数增多,也会增加同步副本的开销,可能会降低集群的可用性。一般,建议设为 3,这也是 Kafka 的默认值。
不完全的选主
unclean.leader.election.enable是 broker 级别(实际上是集群范围内)配置,默认值为 true。
- 如果设为 true,代表着允许不同步的副本成为主副本(即不完全的选举),那么将面临丢失消息的风险;
- 如果设为 false,就要等待原先的主副本重新上线,从而降低了可用性。
最少同步副本
min.insync.replicas控制的是消息至少要被写入到多少个副本才算是“已提交”。min.insync.replicas 是主题级别和 broker 级别配置。
尽管可以为一个主题配置 3 个副本,但还是可能会出现只有一个同步副本的情况。如果这个同步副本变为不可用,则必须在可用性和数据一致性之间做出选择。Kafka 中,消息只有被写入到所有的同步副本之后才被认为是已提交的。但如果只有一个同步副本,那么在这个副本不可用时,则数据就会丢失。
如果要确保已经提交的数据被已写入不止一个副本,就需要把最小同步副本的设置为大一点的值。
注意:要确保 replication.factor >min.insync.replicas。如果两者相等,那么只要有一个副本挂机,整个分区就无法正常工作了。我们不仅要改善消息的持久性,防止数据丢失,还要在不降低可用性的基础上完成。推荐设置成replication.factor = min.insync.replicas + 1。
四、生产者的可靠性
Kafka 生产者 中提到了,Kafka 有三种发送方式:同步、异步、异步回调。
如果使用异步方式发送消息,通常会立即返回,但消息可能丢失。
解决生产者丢失消息:
Producer 使用异步回调方式 producer.send(msg, callback) 发送消息。callback(回调)能准确地告诉你消息是否真的提交成功了。一旦出现消息提交失败的情况,你就可以有针对性地进行处理。
- 如果是因为那些瞬时错误,那么仅仅让 Producer 重试就可以了;
- 如果是消息不合格造成的,那么可以调整消息格式后再次发送。
然后,需要基于以下几点来保证 Kafka 生产者的可靠性:
ACK
生产者可选的确认模式有三种:acks=0、acks=1、acks=all。
acks=0、acks=1 都有丢失数据的风险。
acks=all 意味着会等待所有同步副本都收到消息。再结合 min.insync.replicas ,就可以决定在得到确认响应前,至少有多少副本能够收到消息。
这是最保险的做法,但也会降低吞吐量。
重试
如果 broker 返回的错误可以通过重试来解决,生产者会自动处理这些错误。
- 可重试错误,如:LEADER_NOT_AVAILABLE,主副本不可用,可能过一段时间,集群就会选举出新的主副本,重试可以解决问题。
- 不可重试错误,如:INVALID_CONFIG,即使重试,也无法改变配置选项,重试没有意义。
需要注意的是:有时可能因为网络问题导致没有收到确认,但实际上消息已经写入成功。生产者会认为出现临时故障,重试发送消息,这样就会出现重复记录。所以,尽可能在业务上保证幂等性。
设置 retries 为一个较大的值。这里的 retries 同样是 Producer 的参数,对应前面提到的 Producer 自动重试。当出现网络的瞬时抖动时,消息发送可能会失败,此时配置了 retries > 0 的 Producer 能够自动重试消息发送,避免消息丢失。
错误处理
开发者需要自行处理的错误:
- 不可重试的 broker 错误,如消息大小错误、认证错误等;
- 消息发送前发生的错误,如序列化错误;
- 生产者达到重试次数上限或消息占用的内存达到上限时发生的错误。
五、消费者的可靠性
只有被写入所有同步副本的消息,对消费者才是可用的,这就保证了消费者接收到消息时已经具备了数据一致性。
消费者唯一要做的是确保哪些消息是已经读取过的,哪些是没有读取过的(通过提交的偏移量来判断)。
如果消费者提交了偏移量却未能处理完消息,那么就有可能造成消息丢失,这也是消费者丢失消息的主要原因。
消费者的可靠性配置
- group.id - 如果希望消费者可以看到主题的所有消息,那么需要为它们设置唯一的 group.id。
- auto.offset.reset - 有两个选项:
- earliest - 消费者会从分区的开始位置读取数据
- latest - 消费者会从分区末尾位置读取数据
- enable.auto.commit - 消费者自动提交偏移量。如果设为 true,处理流程更简单,但无法保证重复处理消息。
- auto.commit.interval.ms - 自动提交的频率,默认为每 5 秒提交一次。
显式提交偏移量
如果 enable.auto.commit 设为 true,即自动提交,就无需考虑提交偏移量的问题。
如果选择显式提交偏移量,需要考虑以下问题:
- 总是在处理完事件后再提交偏移量
- 提交频率是性能和重复消息数之间的权衡
- 确保对提交的偏移量心中有数
- 再均衡
- 消费者可能需要重试
- 消费者可能需要维护状态
- 长时间处理
- 幂等性设计
六、验证系统可靠性
建议从 3 个层面验证系统的可靠性:
- 配置验证
- 应用验证
- 客户端和服务器断开连接
- 选举
- 依次重启 broker
- 依次重启生产者
- 依次重启消费者
- 监控可靠性
- 对于生产者来说,最重要的两个指标是消息的 error-rate 和 retry-rate。如果这两个指标上升说明系统出了问题。
- 对于消费者来说,最重要的指标是 consumer-lag,该指标表明了消费者的处理速度与最近提交到分区里的偏移量之间还有多少差距。