一、前言
大家好,我是老周,有快二十多天没有更新文章了,很多小伙伴始终在催更。先阐明下最近的状况,最近我的项目上线很忙,没有工夫写,并且组里有个共事应用 Kafka 不当,导致线上音讯失落,在修复一些线上的数据,人都麻了。事件是这样,有个 Kafka 消费者实例,部署到线下来,生产到了线上的数据,而新版本做了新的逻辑,新版本的业务逻辑与老版本的业务逻辑不兼容,间接导致生产失败,没有进行重试操作,要害还提交了 offset。间接这部分数据没有被业务解决,导致音讯失落,而后紧急修复线上数据。
刚好这些天忙完了有空,所以记录一下,同时看是否对大家能起到防止踩坑的作用,能有一些作用,那我写的也就值了。
咱们上面会从以下三个方面来说一下 Kafka 音讯失落的场景以及最佳实际。
- 生产者失落音讯
- Kafka Broker 服务端失落音讯
- 消费者失落音讯
二、Kafka 的三种音讯语义
先说 Kafka 音讯失落的场景之前,咱们先来说下 Kafka 的三种音讯语义,不会还有人不晓得吧?这个不应该了,音讯零碎基本上形象成这以下三种音讯语义了:
- 最多传递一次
- 起码传递一次
- 仅有一次传递
类型 | 音讯是否会反复 | 音讯是否会失落 | 劣势 | 劣势 | 实用场景 |
---|---|---|---|---|---|
最多一次 | 否 | 是 | 生产端发送音讯后不必期待和解决服务端响应,音讯发送速度会很快。 | 网络或服务端有问题会造成音讯的失落 | 音讯零碎吞吐量大且对音讯的失落不敏感。例如:日志收集、用户行为等场景。 |
起码一次 | 是 | 否 | 生产端发送音讯后须要期待和解决服务端响应,如果失败会重试。 | 吞吐量较低,有反复发送的音讯。 | 音讯零碎吞吐量个别,然而绝不能丢音讯,对于反复音讯不敏感。 |
有且仅有一次 | 否 | 否 | 音讯不反复,音讯不失落,音讯可靠性很好。 | 吞吐量较低 | 对音讯的可靠性要求很高,同时能够容忍较小的吞吐量。 |
三、Kafka 音讯失落的场景
3.1 生产者失落音讯
- 目前 Kafka Producer 是异步发送音讯的,如果你的 Producer 客户端应用了
producer.send(msg)
办法来发送音讯,办法会立刻返回,但此时并不能代表音讯曾经发送胜利了。 - 如果音讯再发送的过程中产生了网络抖动,那么音讯可能没有传递到 Broker,那么音讯可能会失落。
- 如果发送的音讯自身不合乎,如大小超过了 Broker 的承受能力等。
3.2 Kafka Broker 服务端失落音讯
- Leader Broker 宕机了,触发选举过程,集群选举了一个落后 Leader 太多的 Broker 作为 Leader,那么落后的那些音讯就会失落了。
- Kafka 为了晋升性能,应用页缓存机制,将音讯写入页缓存而非间接长久化至磁盘,采纳了异步批量刷盘机制,也就是说,依照肯定的音讯量和工夫距离去刷盘,刷盘的动作由操作系统来调度的,如果刷盘之前,Broker 宕机了,重启后在页缓存的这部分音讯则会失落。
3.3 消费者失落音讯
- 消费者拉取了音讯,并解决了音讯,但解决音讯异样了导致失败,并且提交了偏移量,消费者重启后,会从之前已提交的位移的下一个地位从新开始生产,生产失败的那些音讯不会再次解决,即相当于消费者失落了音讯。
- 消费者拉取了音讯,并提交了生产位移,然而在音讯解决完结之前忽然产生了宕机等故障,消费者重启后,会从之前已提交的位移的下一个地位从新开始生产,之前未解决实现的音讯不会再次解决,即相当于消费者失落了音讯。
四、最佳实际
4.1 生产端
- 不要应用
producer.send(msg)
,而要应用producer.send(msg, callback)
。带有回调告诉的 send 办法能够针对发送失败的音讯进行重试解决。 - 设置
acks = all
。代表了你对“已提交”音讯的定义。如果设置成 all,则表明所有正本 Broker 都要接管到音讯,该音讯才算是“已提交”。这是最高等级的“已提交”定义。
- 设置
retries = 3
,当呈现网络的刹时抖动时,音讯发送可能会失败,此时配置了 retries > 0 的 Producer 可能主动重试音讯发送,防止音讯失落。
如果重试达到设定的次数,那么生产者就会放弃重试并返回异样。不过并不是所有的异样都是能够通过重试来解决的,比方音讯太大,超过 max.request.size
参数配置的值时,这种形式就不可行了。
- 设置
retry.backoff.ms = 300
,正当估算重试的工夫距离,能够防止有效的频繁重试。
它用来设定两次重试之间的工夫距离,防止有效的频繁重试。在配置 retries
和 retry.backoff.ms
之前,最好先估算一下可能的异样复原工夫,这样能够设定总的重试工夫大于这个异样复原工夫,以此来防止生产者过早地放弃重试。
4.2 Broker 端
- 设置
unclean.leader.election.enable = false
。它管制的是哪些 Broker 有资格竞选分区的 Leader。如果一个 Broker 落后原先的 Leader 太多,那么它一旦成为新的 Leader,必然会造成音讯的失落。故个别都要将该参数设置成 false,即不容许这种状况的产生。
- 设置
replication.factor >= 3
。其实这里想表述的是,最好将音讯多保留几份,毕竟目前避免音讯失落的次要机制就是冗余。
- 设置
min.insync.replicas > 1
。这管制的是音讯至多要被写入到多少个正本才算是“已提交”。设置成大于 1 能够晋升音讯持久性。在理论环境中千万不要应用默认值 1。
- 确保
replication.factor > min.insync.replicas
。如果两者相等,那么只有有一个正本挂机,整个分区就无奈失常工作了。咱们不仅要改善音讯的持久性,避免数据失落,还要在不升高可用性的根底上实现。举荐设置成replication.factor = min.insync.replicas + 1
。
4.3 生产端
- 确保音讯生产实现再提交。最好把它设置成
enable.auto.commit = false
,并采纳手动提交位移的形式。这对于单 Consumer 多线程解决的场景而言是至关重要的。
尽管采纳手动提交位移的形式能够解决生产端音讯失落的场景,但同时会存在反复生产问题,对于反复生产问题咱们下一篇再讲。
-
像咱们下面说的那个线上问题,即便你设置了手动提交,生产异样了同时也提交了位移,还是会存在音讯失落。
Kafka 没有重试机制不反对音讯重试,也没有死信队列,因而应用 Kafka 做音讯队列时,须要本人
实现音讯重试的性能。这里我先说下大抵的思路,后续有工夫再分享代码进去:
- 创立一个 Topic 作为重试 Topic,用于接管期待重试的音讯。- 一般 Topic 消费者设置待重试音讯的下一个重试 Topic。- 从重试 Topic 获取待重试音讯存储到 Redis 的 ZSet 中,并以下一次生产工夫排序。- 定时工作从 Redis 获取达到生产工夫的音讯,并把音讯发送到对应的 Topic。- 同一个音讯重试次数过多则不再重试。