文章目录
kafka 的复制机制和分区的多分布架构是kafka 可靠性保证的核心。把消息写入多个副本可以使kafka 在发生崩溃时仍能保证消息的持久性。
kafka 可靠性提供的保证
- kafka 可以保证分区消息的顺序。如果使用同一个生产者往同一个分区写入消息,消息B 在消息A之后写入,那么kafka 保证消费者会先读取消息A 再读取消息B。
- 只有当消息被写入分区的所有同步副本时,它才被认为是“已提交”的。消费者只能读取已提交的消息。
- 只要还有一个副本是活跃的,那么已经提交的消息就不会丢失。
kafka 的复制机制
kafka 的主题被分为多个分区,分区是基本的数据块。分区存储在单个磁盘上,kafka 可以保证分区里的事件是有序的。每个分区可以有多个副本,其中一个是首领。所有的事件都直接发送给首领,或者直接从首领副本读取。其他副本只需要与首领保持同步,并及时复制最新的事件。当首领副本不可用时,其中一个同步副本将被选举为新首领。
相关配置项
broker 有3个配置参数会影响kafka 消息存储的可靠性。
复制系数
分区副本的数量。主题级别的配置参数是replication.factor
, broker 级别是default.replication.factor
。
默认值为3,具体设置为多少呢?这是可用性与硬件成本之间的权衡!
不完全的首领选举
当分区首领不可用时,一个跟随者副本会被选举为新首领。如果跟随者副本不可用或者不同步,就会出现以下两个选择:
- 如果不同步的副本可以被选举为新首领,那么因为其不同步,它被选为首领后就会出现消息不一致的问题。
- 如果不同步的副本不能被选举为新首领,那么分区在旧首领(最后一个同步副本)恢复之前是不可用的,这就需要持续等待同步副本恢复到可用状态,可能需要很长时间。
配置项unclean,leader.election.enable
被设置true 就是选项1:允许不同步副本成为新首领,面临丢失数据的风险;设置为false 就是选项2:不允许不同步副本成为新首领,牺牲一部分实时性。
最少同步副本
min.insync.replicas
在主题级别和broker 级别都可设置。
根据kafka 可靠性保证的定义,消息只有被写入到所有同步副本之后才被认定为已提交的。假定有3个副本中的两个副本已经崩溃了,那此时的”所有副本“就只表示一个同步副本,那么只要这个副本崩溃掉,数据就会丢失。
如果将配置项min.insync.replicas
设置为2,表示至少要存在两个同步副本才能向分区写入数据。否则broker 会停止接收生产者的请求。
生产端可靠性配置
发送确认
生产端acks 参数指定了必须有多少个分区副本收到消息,生产者才会认为消息写入是成功的。该参数有如下选项:
- acks=0; 生产者在写入消息之前不会等待broker 的响应。如果broker 没有收到消息,那么生产者无从得知,消息也就丢失了。但这种方式能够得到最大的吞吐量。
- acks=1; 只要集群的首领节点收到消息,生产者就会收到一个来自broker 的成功响应。如果生产者收到了首领的响应,此时跟随者节点的副本尚未同步,然后首领崩溃了,这时没有同步的节点成为新首领,就会出现数据丢失的情况。
- acks=all; 只有所有参与复制的节点全都收到消息,生产者才会收到成功响应。这种模式是最安全的,还可以使用
min.insync.replicas
再增加一层保险。不过延迟会更高,可以通过使用异步模式和更大的批次来加快速度,但这样会降低吞吐量。
生产者重试参数 retries
生产者向broker 发送消息时, broker 可以返回一个成功响应码或错误响应码。错误分为两种,一种是可以通过重试解决的,还有一种是无法通过重试解决。
重试发送一个消息也会带来一些风险,如果两次发送都写入成功,就会造成消息重复的问题。重试和恰当的错误处理可以保证每个消息“至少被保存一次”,当前kafka 版本无法保证消息“只被保存一次”。实际场景的解决方案就是:往消息里加入唯一标识符,用于检测重复消息,消费者再进行处理。
额外的错误处理
使用生产者内置的重试机制可以轻松的处理大部分错误,不过依然会有一些其他类型的错误重试处理不了,这时可以编写额外的错误处理器。
消费端可靠性
只有已提交的消息,也就是那些被写入所有同步副本的消息,才能被消费者获取到,这就意味着消费者得到的消息已经具备了一致性。消费者唯一要做的就是确认哪些消息是已经读取过的,哪些是还没有读取过的。
相关配置项
group.id
如果两个消费者具有相同的group.id,并且订阅了同一个主题,那么每个消费者只会分到主题分区的一个子集。auto.offset.reset
这个参数指定了在没有偏移量可提交时或者请求的偏移量在broker 上不存在时,消费者会做什么?这个参数有两种配置,一个是earliest表示消费者从分区的开始位置读取数据,这样会导致消费者读取大量的重复数据;还有一种是latest表示消费者会从分区末尾开始读取数据,这样可能会错过一些消息。enable.auto.commit
可以让消费者基于任务调度自动提交偏移量。自动提交的优点就是实现逻辑时变得简单了。缺点是无法控制重复处理消息(比如消费者在自动提交之前就停止了处理消息)。auto.commit.interval.ms
这个参数与第三个参数有直接的关系。如果通过第三个参数选择了自动提交,那可以通过该参数配置提交的频度。