1/ 生产端 Producer
消息格式:每个消息是一个 ProducerRecord 对象,必须指定消息所属的 Topic 和消息值 Value ,此外还可以指定消息所属的 Partition 以及消息的 Key。Producer 生产数据默认是先写到内存(PageCache)中的,定期 flush 到磁盘上。默认的这个参数是:
设置较小定期 flush 的时间,并不能真正保证数据不会丢失。通过查看官方提供的默认配置 Long.MaxValue 也可以看出,官方是不建议我们通过这个参数来改变什么的,也就是说设置 flush 的时间,不能从根本上保证我们的数据丢失问题。
前面一开始提到,kafka 作为一个可分区和可复制的消息队列。我们可以利用它的分区副本机制,为每一个 Topic 的数据分为多个分区,每个分区放在不同的节点上,每个分区默认只有一分数据,通过设置参数 --replication-factor 2 ,可以执行每个分区有三份数据,其中有两份是用来备份的。
分区有副本了,也会存在 leader 和 follower 的角色,zookeeper 中维护了一个 ISR 列表。
这时,生产者往 broker 中写入数据的时候,默认的消息确认级别是 request.required.acks = 0, 表示生产者发送消息之后,不会等待 leader的确认。
所以,需要把这个设置为 -1 ,表示生产者发送消息要等到 leader 以及所有的副本都同步了,才会返回确认消息。当然,这种方案也不是非常万无一失的,官方提出的建议是,还需要配置 min.insync.replicas 最小同步的副本数,如果没有得到这个最小数的要求,生产者会抛出异常,本次写入不会成功。比如,我有三个副本,那么最好找个参数要设置成 2 ,要保证大部分的副本同步了数据。
官方原文:
总结:request.required.acks = -1 虽然保证了数据的一致性,但是同时也影响了性能。在实际业务中应该考虑数据少量丢失对于业务的影响,要根据自己的情况来,结合业务来平衡数据一致性和系统的性能。
有兴趣的读者可以去研究一下 CAP, 分布式系统的三个指标,推荐阮一峰老师写的 -- CAP 定义的原理。
2/ 消费端 Consumer
消费端数据丢失的原因是 offset 的自动提交。
由于在使用kafka的高级API时,消费者会自动每隔一段时间将offset保存到zookeeper上,此时如果刚好将偏移量提交到zookeeper上后,但这条数据还没消费完,机器发生宕机,此时数据就丢失了。
解决方法:关闭自动提交,改成手动提交,每次数据处理完后,再提交。
数据重复消费,在消费者自动提交offset到zookeeper后,程序又消费了几条数据,但是还没有到下次自动提交offset到zookeeper之时,如果机器宕机了,然后重启,此时消费者会去读zookeeper上的偏移量进行消费,这就会导致数据重复消费。 解决方法:关闭自动提交,改成手动提交。
这里再提一下 kafka 的两种方式API,以供大家选择合适的。
高级 API 的特点
优点
● 高级API写起来简单
● 不需要去自行去管理offset,系统通过zookeeper自行管理
● 不需要管理分区,副本等情况,系统自动管理
● 消费者断线会自动根据上一次记录在 zookeeper中的offset去接着获取数据
缺点
● 不能自行控制 offset(对于某些特殊需求来说)
● 不能细化控制如分区、副本、zk 等
低级 API 的特点
优点
● 能够开发者自己控制offset,想从哪里读取就从哪里读取。
● 自行控制连接分区,对分区自定义进行负载均衡
● 对 zookeeper 的依赖性降低(如:offset 不一定非要靠 zk 存储,自行存储offset 即可,比如存在文件或者内存中)
缺点
● 太过复杂,需要自行控制 offset,连接哪个分区,找到分区 leader 等
本文就到这里,实际生产中需要结合自己的业务去选择。