Kafka学习笔记(三)—Kafka消息丢失,消费重复

一、数据传输事务的定义

  • 最多一次(at most once):消息不会被重复发送,但是可能丢失
  • 最少一次 (at least once)消息可能会被重复发送,但是不会漏发送
  • 精确一次(exactly once) 不会少发送也不会重复发送,只会发送一次

第三种定义是大家所希望的消息队列的保证,但是,Kafka默认提供的是at least once。同时,Kafka只对已提交的消息做有限度的持久化保证。

二、生产者丢失消息

Kafka生产者消息丢失和乱序:

  • 同步发送消息时未同步到所有副本,leader replica所在broker宕机,消息丢失
  • 异步发送消息,新版本会将消息写入缓冲区,利于后台IO线程扫描缓冲区,将满足条件的消息封装并发送到broker,导致异常数据的两种情况:
    • 消息量过大,导致缓冲区写满,此时缓冲区如果抛出异常或者数据被清除,都导致消息丢失
    • 异步发送消息后使用回调函数,会进行重试,造成乱序

生产环境一般不使用同步发送,因为同步发送会严重影响Kafka的吞吐量

生产者同步代码:

Future<RecordMetadata> future = producer.send(newProducerRecord<String, String>("test.testTopic", "key","value"));

RecordMetadata metadata = future.get(); //等待发送结果返回

生产者异步代码:

//消息发送失败,可通过 callback 记录失败并处理
producer.send(record, new Callback() 
{
    public void onCompletion(RecordMetadata recordMetadata, Exception e) 
    {
        if (null != e)
        {
            System.out.println("send error" + e.getMessage());
        }
        else 
        {
            System.out.println(String.format("offset:%s,partition:%s",recordMetadata.offset(),recordMetadata.partition()));
        }
    }
});
生产参数配置:
  • 使用producer.send(msg,callback)带回调函数的API,消息提交失败可以做相应处理
参数 request.required.acks
  • request.required.acks = 0 消息的强制备份数量为 0,Producer 不停向 Leader 发送数据,而不需要 Leader 反馈成功消息,可能在发送过程中丢失数据,可能在 Leader 宕机时丢失数据,不推荐使用

  • request.required.acks = 1 默认情况,即消息的强制备份数量为 1,Producer 发送数据到 Leader,只要 Leader 成功写入本地日志成功,即返回客户端成功,不要求 ISR 中的其它副本与 Leader 保持同步

  • request.required.acks = -1(all) 消息的强制备份数量为 ISR 列表中副本的数量,生产者写消息到leader副本中,必须要等leader副本数据完全同步到ISR中所有follower,消息才算commit

参数producer.type

老版使用该参数指定发送消息类型,新版使用上述API即可

这个参数指定了在后台线程中消息的发送方式是同步的还是异步的,默认是同步的方式,即producer.type=sync。如果设置成异步的模式,即producer.type=async,可以是producer以batch的形式push数据,这样会极大的提高broker的性能,但是这样会增加丢失数据的风险。

  • block.on.buffer.full = true 该参数0.9.0.0标为废弃参数 true表示缓冲区写满Producer会阻塞,既不会报异常也不会清空缓冲区
参数 retries

retries:int 型,默认值 0,retries = maxNum Producer进行自动重试的次数,需要设置成一个较大的值

参数max.in.flight.requests.per.connection

该参数可以一定程度避免消息重排序

设置为1可以解决消息乱序问题。该参数是客户端可以设置在单个连接上发送的未确认请求的最大数目,默认值是5。如果设置大于 1,并且发送失败,则存在由于重试而导致消息重新排序的风险,这个前提是retries>0,启动了消息发送重试。增加此值应该可以增加IO线程的吞吐量,从而整体上提升producer的性能

参数unclean.leader.election.enable

unclean.leader.election.enable=false 关闭unclean leader选举,即不允许非ISR中的副本被选举为leader,因为可能副本消息落后leader太多,以避免数据丢失

参数 batch.size

batch.size:int 型,默认值 16384,单位字节,即默认大小 16KB。当多条消息的目标 Partition 相同时,Producer 会尝试将它们组装成一个批量消息,以便减少请求的数量,有助于提升客户端和服务器的性能。批量消息的大小由参数“batch.size”控制

注意:参数“batch.size”的不宜过大或过小,过大浪费内存,过小则有可能降低吞吐量(大小为零将完全禁用批处理)

参数 linger.ms

linger.ms:long 型,默认值 0,单位毫秒,该参数就是控制生产者发送数据的延迟,生产者将等待直到给定延迟才允许发送其它消息,从而可以将待发送的消息批量在一起,而不是立即发送。但是如果已经达到一个分区的 batch.size 值的记录,它将立即发送,而不管该设置如何

参数 buffer.memory 和 max.block.ms
  • buffer.memory;long 型,默认值 33554432,单位字节,即默认大小为 32MB
  • max.block.ms:long 型,默认值 60000,单位毫秒,即大小为 60s

如果当程序的发送速率大于后台线程发送到 partition 的速率时,就会产生消息堆积,上述两个参数是指定缓存区大小,以及到达缓冲区上限后阻塞多久抛出异常

小结

上述配置可以保证消息不丢失,但是不能保证消息不重复,生产者向leader写入数据,在这期间发生宕机,如果部分follower完成同步,部分未完成,此时会在follower中选举新的leader,这时成为新leader的副本可能保存部分这次提交的数据,生产者收到接收失败的消息,会对上次数据进行重推,新leader接收数据这样就造成写入Kafka数据重复。

生产者配置:

roperties props = new Properties();
props.put("bootstrap.servers", "100.120.130.170:9092,100.120.130.171:9092, 100.120.130.172:9092");
props.put("acks", "all"); //保证高可靠性,设置成"all"或者"-1"
props.put("retries", 3);  //重试次数阈值,这里设置为3
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); //这里是key的序列化类
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");//这里是value的序列化类
Producer<String, String> producer = new KafkaProducer<String,String>(props);

三、broker消息丢失

broker的topic一般多分区,每个分区保存在多个副本上,就算leader副本宕机也不会造成数据丢失。

参数 replication.factor 和 min.insync.replicas

replication.factor指定Kafka副本数量,包含leader副本

min.insync.replicas该参数用于设定 ISR 中的最小副本数,默认值为 1,当且仅当 request.required.acks 参数设置为 -1 或者 all 时,此参数才生效。当 ISR 中的副本数少于 min.insync.replicas 配置的数量时,客户端会返回异常

  • replication.factor >= 3 副本数量大于3,根据具体broker数量配置副本数量,尽量让副本在broker上均匀分配
  • min.insync.replicas > 1 该参数用于设定 ISR 中的最小副本数,默认值为 1
  • replication.factor > min.insync.replicas 如果两者相等,当一个副本挂掉了分区也就没法正常工作了,推荐设置replication.factor = min.insync.replicas + 1即可

broker配置:

default.replication.factor=3
min.insync.replicas=2
replication.factor = min.insync.replicas + 1

可以容忍一台Broker宕机

四、消费者偏移量提交方式

Consumer中记录消费位移,注意,它记录的是要消费的下一条消息的位移,不是目前最新消息消费的位移

用户角度:

  • 自动提交
  • 手动提交

consumer角度:

  • 同步提交
  • 异步提交

1)自动提交
参数: enable.auto.commit为true 手动提交,默认方式,消费者每隔5秒提交上轮poll方法全部处理完的消息的位移。

假设在poll后3秒发生重平衡,消费者这时只提交了上一轮的位移,所以再次消费时会发生重复消费

2)手动提交
参数: enable.auto.commit为false 手动提交,一般推荐使用该方式。

手动同步提交:

  • 先调用poll方法,立即使用commitSync(),如果在处理消息的时候发送异常或者再均衡,这期间的数据就丢失了
  • 先调用poll方法,处理完数据再commitSync(),和上面自动提交一样,中间再均衡会重复消费

commitSync()是同步提交偏移量,主程序会一直阻塞,偏移量提交成功后才往下运行,这回限制Kafka的吞吐量。而降低提交频次又会造成重复消费的消息更多。

commitSync只要没有发生不可恢复错误,会进行重试,直到成功

commitAsync是异步提交,不会进行重试,如果重试,那么小偏移量会覆盖大偏移量,造成重复消费。举个例子,假如我们发起了一个异步提交commitA,此时的提交位移为2000,随后又发起了一个异步提交commitB且位移为3000;commitA提交失败但commitB提交成功,此时commitA进行重试并成功的话,会将实际上将已经提交的位移从3000回滚到2000,导致消息重复消费。

正确提交方式:

 	try
    {
        while (true) {
            ConsumerRecords<String, String> records =
                    consumer.poll(Duration.ofSeconds(1));
            process(records); // 处理消息
            commitAysnc(); // 异步提交
        }
    } catch(Exception e)

    {
        handle(e); // 处理异常
    } finally

    {
        try {
            consumer.commitSync(); // 最后一次提交使用同步阻塞式提交
        } finally {
            consumer.close();
        }
    }

采取同步和异步混合的提交方式:

  • 正常消费消息时,采取异步提交,消息处理结束提交offset
  • 程序报错,finally里面采取同步提交,确保成功
  • 再均衡前的回调方法,采取同步提交,确保成功。

这种方法也不能保证消息不会重复,因为如果poll()数据,处理一半再均衡,位移没有提交,重平衡后新的消费者再来消费,消息还是可能会重复

五、消息存储可靠性

Kafka 通过持久化消息到磁盘来保障消息存储的可靠性,但是消息都是先写到操作系统的页缓存中,如果没有fsync到磁盘,存在消息丢失的可能性

Kafka 提供了两个参数来控制 Broker 的刷盘时机:

log.flush.interval.ms
long型,默认值null,单位ms,用于控制日志刷盘的时间间隔,每隔多少时间将消息刷到磁盘上

log.flush.interval.messages
long型,默认值9223372036854775807,用于控制日志刷盘的消息量,即每积累多少条消息将消息刷到磁盘上

建议配置:

#每当producer写入10000条消息时,刷数据到磁盘
log.flush.interval.messages=10000

#每间隔1秒钟时间,刷数据到磁盘
log.flush.interval.ms=1000

频繁调用fsync会影响性能,官方不建议通过上述参数来强制写盘,认为数据的可靠性通过replica来保证,而强制flush数据到磁盘会对整体性能产生影响

如果副本所在broker节点发生宕机,可以从其它副本中进行数据恢复

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值