kafka-消息防丢失和消息去重

如何防止数据丢失

生产者:同步发送消息,且消息配置为-1或all,leader分区和所有follwer都写到磁盘里。

异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态。

生产者:手动提交,即读取到消息后,确认消息消费完毕,才手动提交offset。但是要避免逻辑处理时间过长,导致连接超时,会使消息重复消费。

故kafka一定要配置上消息重试的机制,并且重试的时间间隔一定要长一些,默认1秒钟并不符合生产环境(网络中断时间有可能超过1秒)。
增加如下参数会较大幅度的减少kafka写入数据照成的数据丢失,在公司实测,目前还没遇到数据丢失的情况。

生产端

设计上保证数据的可靠安全性,依据分区数做好数据备份,设立副本数等。
push数据的方式:同步异步推送数据:权衡安全性和速度性的要求,选择相应的同步推送还是异步推送方式,当发现数据有问题时,可以改为同步来查找问题。

flush是kafka的内部机制,kafka优先在内存中完成数据的交换,然后将数据持久化到磁盘.kafka首先会把数据缓存(缓存到内存中)起来再批量flush.
可以通过log.flush.interval.messages和log.flush.interval.ms来配置flush间隔

可以通过replica机制保证数据不丢.
代价就是需要更多资源,尤其是磁盘资源,kafka当前支持GZip和Snappy压缩,来缓解这个问题
是否使用replica(副本)取决于在可靠性和资源代价之间的balance(平衡)

broker到 Consumer kafka的consumer提供两种接口.
high-level版本已经封装了对partition和offset的管理,默认是会定期自动commit offset,这样可能会丢数据的

low-level版本自己管理spout线程和partition之间的对应关系和每个partition上的已消费的offset(定期写到zk)
并且只有当这个offset被ack后,即成功处理后,才会被更新到zk,所以基本是可以保证数据不丢的即使spout线程crash(崩溃),重启后还是可以从zk中读到对应的offset

异步要考虑到partition leader在未完成副本数follows的备份时就宕机的情况,即使选举出了新的leader但是已经push的数据因为未备份就丢失了!
不能让内存的缓冲池太满,如果满了内存溢出,也就是说数据写入过快,kafka的缓冲池数据落盘速度太慢,这时肯定会造成数据丢失。
尽量保证生产者端数据一直处于线程阻塞状态,这样一边写内存一边落盘。
异步写入的话还可以设置类似flume回滚类型的batch数,即按照累计的消息数量,累计的时间间隔,累计的数据大小设置batch大小。
设置合适的方式,增大batch 大小来减小网络IO和磁盘IO的请求,这是对于kafka效率的思考。
不过异步写入丢失数据的情况还是难以控制
还是得稳定整体集群架构的运行,特别是zookeeper,当然正对异步数据丢失的情况尽量保证broker端的稳定运作吧

设置同步模式, producer.type = sync, Request.required.acks =  -1, replication.factor >= 2 且 min.insync.replicas >= 2

broker端:

topic设置多分区,分区自适应所在机器,为了让各分区均匀分布在所在的broker中,分区数要大于broker数。分区是kafka进行并行读写的单位,是提升kafka速度的关键。

broker能接收消息的最大字节数的设置一定要比消费端能消费的最大字节数要小,否则broker就会因为消费端无法使用这个消息而挂起。

broker可赋值的消息的最大字节数设置一定要比能接受的最大字节数大,否则broker就会因为数据量的问题无法复制副本,导致数据丢失

消费端

		//producer用于压缩数据的压缩类型。默认是无压缩。正确的选项值是none、gzip、snappy。压缩最好用于批量处理,批量处理消息越多,压缩性能越好
     props.put("compression.type", "gzip");
     //增加延迟
     props.put("linger.ms", "50");
     //这意味着leader需要等待所有备份都成功写入日志,这种策略会保证只要有一个备份存活就不会丢失数据。这是最强的保证。,
     props.put("acks", "all");
     //无限重试,直到你意识到出现了问题,设置大于0的值将使客户端重新发送任何数据,一旦这些数据发送失败。注意,这些重试与客户端接收到发送错误时的重试没有什么不同。允许重试将潜在的改变数据的顺序,如果这两个消息记录都是发送到同一个partition,则第一个消息失败第二个发送成功,则第二条消息会比第一条消息出现要早。
     props.put("retries ", MAX_VALUE);
     props.put("reconnect.backoff.ms ", 20000);
     props.put("retry.backoff.ms", 20000);
     
     //关闭unclean leader选举,即不允许非ISR中的副本被选举为leader,以避免数据丢失
     props.put("unclean.leader.election.enable", false);
     //关闭自动提交offset
     props.put("enable.auto.commit", false);
     限制客户端在单个连接上能够发送的未响应请求的个数。设置此值是1表示kafka broker在响应请求之前client不能再向同一个broker发送请求。注意:设置此参数是为了避免消息乱序
     props.put("max.in.flight.requests.per.connection", 1);

topic设置多分区,分区自适应所在机器,为了让各分区均匀分布在所在的broker中,分区数要大于broker数。分区是kafka进行并行读写的单位,是提升kafka速度的关键。

如果处理耗时很长,则建议把逻辑放到另一个线程中去做。为了避免数据丢失,有两点建议:

enable.auto.commit=false 关闭自动提交位移

在消息被完整处理之后再手动提交位移

消息重复解决方案

针对消息重复:将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可。比如redis中

消息可以使用唯一id标识

生产者(ack=all 代表至少成功发送一次)

消费者 (offset手动提交,业务逻辑成功处理后,提交offset)

落表(主键或者唯一索引的方式,避免重复数据)

业务逻辑处理(选择唯一主键存储到Redis或者mongdb中,先查询是否存在,若存在则不处理;若不存在,先插入Redis或Mongdb,再进行业务逻辑处理)

新版本的API在平衡的时候可以注册一个对象,在平衡前和后可以调用这个对象的方法,我们在这个方法里面将此topic的stream提交(这可能会造成数据丢失,因为这些数据很可能还没处理),

这个新API测试了下,基本没什么问题。
高级API如何解决?用类分布式锁最终解决了这个问题,实现思路比较简单,就是通过ZK来实现,程序启动前先定义好需要启动的消费者数量,如果还没达到这个量,线程都不能启动,达到这个线程数后,休眠几秒后启动,在启动的时候,消费者线程已经得到了平衡,除非线程死掉否则不会发生平衡了,所以暂时解决了这个问题。
思路共享出来,希望对大家有所帮助。

这里需要 HW ( HighWartermark ) 的协同配合。类似于木桶原理,水位取决于最低那块短板

某个 topic 的某 partition 有三个副本,分别为 A、B、C。A 作为 leader 肯定是 LEO 最高,B 紧随其后,C 机器由于配置比较低,网络比较差,故而同步最慢。这个时候 A 机器宕机,这时候如果 B 成为 leader,假如没有 HW,在 A 重新恢复之后会做同步(makeFollower) 操作,在宕机时 log 文件之后直接做追加操作,而假如 B 的 LEO 已经达到了 A 的 LEO,会产生数据不一致的情况

leader epoch

1500 * 1500

10023 项目名称:A QT Latex Editor
详细介绍:用QT实现一个所见即所得的Latex编辑器,能够在图形化界面的IDE里面,采用类似Word的方式,所见即所得书写文档,生成Latex语法的源文件(.tex文件)。将此文件传递到服务器端,保存到数据库中,编译成pdf文件返回客户端显示。 支持在本地打开存储在服务器端数据库的文档进行继续编辑。
使用语言:qt(当然搭建服务器可以用node
截止日期:2019年5月31日
预期达到的效果:能够获得.tex文件和pdf文件,支持生成复杂公式
重点强调:附加讲解工作,就是程序解读以及使用说明

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章