Deque达到一定阈值后,就会唤醒sender线程将消息发送到kafka集群, 这个阈值受两个参数影响
因为linger.ms默认为0, 所以来一个消息就会唤醒sender来发送消息, 这样的效率并不高(会频繁开启线程发送消息), 为了提高拉取速度的能力, 我们希望一次能发送很多消息
所以在生产环境中, 我们一般会修改linger.ms的值, 改为5~100ms, 而batch.size使用默认值即可
注意点:不能将batch.size和linger.ms设置的很大, 这样每批次消息的发送时间间隔就会很大(延迟过大)
// batch.size:批次大小,默认 16K
properties.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
// linger.ms:等待时间,默认 0
properties.put(ProducerConfig.LINGER_MS_CONFIG, 1);
// RecordAccumulator:缓冲区大小,默认 32M:buffer.memory
properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG,33554432);
// compression.type:压缩,默认 none,可配置值 gzip、snappy、lz4 和 zstd
properties.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,"snappy");
生产消息可靠 = 消息能成功落盘(落到分区的所有副本中)
kafka为消费者提供了消息确认机制:
一、ack为0(数据会丢失)
生产者只管发送消息, 不用等待任何来自服务器的响应(不关系消息是否最终落盘)
不会重复发送消息
数据丢失场景:如果当中出现问题,导致服务器没有收到消息, 没有落盘到partition,生产者无从得知,会造成消息丢失
二、ack为1(数据会丢失)
生产者发送消息后, 等待分区的leader落盘消息后应答
如果leader没收到应答,或是收到失败应答, 则会重新发送消息
数据丢失场景:如果leader落盘成功了, 向producer也收到了成功响应, 但是还没来得及将消息同步副本(follower), 此时leader挂了, 此时服务器会从follower中推选新的leader, 新的leader并没有同步消息, 而producer也不会再发了, 此时消息就丢失了
三、ack为-1(数据不会丢失)
生产者发送消息后, 等待分区的所有ISR副本(包括leader以及其他存活的副本)落盘消息后应答
producer只有收到分区中所有ISR副本的成功落盘消息后才认为是推送成功, 反之重新发送消息
如果分区的副本设置为1(即只有leader没有follower), 或者ISR中应答的最小副本数量(min.insync.replicas 默认为1)设置为1, 这种情况下就和ack=1时效果是一样的, 存在数据丢失问题(leader:0, isr:0)
min.insync.replicas = n,代表的语义是,如果生产者acks=all,而在发送消息时,Broker的ISR数量没有达到n,Broker不能处理这条消息,需要直接给生产者报错。
消息重复存在几个场景
生产端重复发送消息的场景
Leader收到数据后, 将数据落盘, 并将数据同步到follower, 此时在给Producer应答时Leader宕机了, 此时Producer就会收到服务器传来的响应失败, 重新发送消息, 服务器会重新挑选一个follower成为leader, 而这个新的leader其实已经落盘了消息
数据发送有三种情况
一、至少一次
什么是至少一次:生产者发送到kafka集群, 至少kafka集群能收到一次数据
如何保证至少一次:ACK级别设置为-1或者all + 分区副本大于等于2 + ISR里应答的最小副本数量大于等于2
至少一次存在的问题:kafka集群重复收到数据的问题, 即可以保证数据不丢失,但是不能保证数据不重复
二、最多一次
什么是最多一次:生产者发送到kafka集群, 不论成功与否, 只会发送一次
如何保证最多一次:ACK级别设置为0
最多一次会产生的问题:无法保证数据是否落盘, 即可以保证数据不重复,但是不能保证数据不丢失
三、精确一次
什么是精确一次:数据既不会丢失, 也不会重复发送
如何保证精确一次:Kafka 0.11版本以后,引入了一项重大特性:幂等性
和事务
, 通过这两点来保证严格一次
精确一次 = 幂等性 + 至少一次
幂等性就是指Producer不论向Broker发送多少次重复数据,Broker端都只会持久化一条,保证了不重复。
如何保证幂等性?
一个消息会被封装成TopicPartition, TopicPartition中记录了以下几个信息:PID、Partition、SeqNumber; 重复数据的判断标准:具有PID, Partition, SeqNumber相同主键的消息提交时,Broker只会持久化一条。
幂等性的条件
所以幂等性只能保证的是在单分区单会话内不重复。
如何使用幂等性
enable.idempotence被设置成true后, Producer自动升级成幂等性Producer,其他所有的代码逻辑都不需要改变。(enable.idempotence默认为true, 不需要手动开启)
properties.put(“enable.idempotence”, ture)
properties.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true)。
开启事务, 必须开启幂等性(即enable.idempotence设置为true), 不需要保证精确一次(幂等性 + 至少一次)
一、一对一模式
保证只有一个consumerGroup(消费者组)订阅topic(主题), 即使这个consumerGroup(消费者组)中存在多个consumer(消费者), 但是一个partitioner(分区)只能被一个consumer(消费者)消费, 这样就能保证消息只被一个消费者消费(一对一)
二、发布/订阅模式
多个consumerGroup(消费者组)一起订阅topic(主题), consumerGroup(消费者组)在消费消息时, 都会在partitioner(分区)中创建对应的_consumer_offsets(消费者偏移量), 这个偏移量使用key-value的存储机构, key为consumerGroup.id + topic + 分区号, value是当前offset的值, 每过5毫秒(默认值), consumerGroup都会提交offset, kafka根据最新的offset来更新roup.id+topic+分区号对应的value
当消费者群组里的消费者发生变化,或者主题里的分区发生了变化,都会导致再均衡现象的发生
有4种再平衡策略
形成一个消费者组的条件,是所有消费者的groupid相同。
形成一个消费者组的条件,是所有消费者的groupid相同。
在Kafka集群中,某个Broker将被选举出来担任一种特殊的角色,其用于管理和协调Kafka集群,即管理集群中的所有分区的状态并执行相应的管理操作。每个Kafka集群任意时刻都只能有一个Controller。当集群启动时,所有Broker都参与Controller的竞选,最终有一个胜出,一旦Controller在某个时刻崩溃,集群中的其他的Broker会收到通知,然后开启新一轮的Controller选举,新选举出来的Controller将承担起之前Controller的所有工作。
controller的作用
选举规则:在isr中存活为前提, 按照AR中排在前面的优先, 例如AR[1, 0, 2], ISR[1, 2], 那么leader会按照1,2的顺序轮巡
对于topicA的partition0这个分区,它选举出broker1作为leader, 而broker0、broker2作为follower, controller会把这个信息告诉zookeeper(将节点信息上传到zookeeper),这是为了防止controller挂了后, 新的controller不知道主副本信息
至少一次:
ISR里应答的最小副本数量大于等于2(ISR中的数量至少有两个, 否则broker不处理这条消息, 并直接给生产者报错)
ISR里应答的最小副本数量大于等于2(ISR中的数量至少有两个, 否则broker不处理这条消息, 并直接给生产者报错)
精确一次 = 幂等性 + 至少一次
幂等性默认为开启, 底层使用Producer.id、Partition、SeqNumber作为重复数据的判断标准
消费者为什么会出现消息丢失的情况, 我们得先了解offset提交机制
如果在消息处理完成前就提交了offset,那么就有可能造成数据的丢失。为了避免数据丢失,可以采用手动提交offset
消费者为什么会重复消费消息?
kafka重复消费的根本原因就是“数据消费了,但是offset没更新