3.kafka如何保证消息的可靠性

129 阅读3分钟

kafka应答原理

  • 0:生产者发送过来的数据,不需要等数据落盘应答

    如下图,生产者发送了信息之后,不需要等待kafka回复,如果kafka上的节点挂了后,这个消息就丢失了。

2.png

  • 1:生产者发送过来的数据,Leader收到数据后应答。

    如下图,可能leader收到消息后,还没同步leader挂了,消息丢失了

3.png

  • -1(all):生产者发送过来的数据,Leader和ISR队列里面的所有节点收齐数据后应答。

    如下图,需要等leader和ISR队列中的所有follower都收到后才应答。

4.png

什么是ISR队列?

​ ISR队列中有Leader保持同步的Follower+Leader集合,如果Follower长时间未向Leader发送通信请求或同步数据,则该Follower将被踢出ISR。该时间阈值由replica.lag.time.max.ms参数设定,默认30s。这样保证了不会因为某个follower没有收到而一直等待。

数据完全可靠条件 = ACK级别设置为-1 + 分区副本大于等于2 + ISR里应答的最小副本数量大于等于2

数据重复分析

  • 生产者发送过来的数据,Leader和ISR队列里面的所有节点收齐数据后准备应答,此时leader挂了,follower节点们重新选举出一个leader,由于刚刚发的Hello消息没收到,所以生产者会重试,但是此时的follower其实已经收到了,这样就会导致重复。如下图:

5.png

代码中配置ack

// 设置 acks
 properties.put(ProducerConfig.ACKS_CONFIG, "all");
 // 重试次数 retries,默认是 int 最大值,2147483647
 properties.put(ProducerConfig.RETRIES_CONFIG, 3);

数据去重

​ 对于一些非常重要的信息,比如和钱相关的数据,要求数据既不能重复也不丢失。Kafka 0.11版本以后,引入了一项重大特性:幂等性和事务

幂等性

​ 幂等性就是指Producer不论向Broker发送多少次重复数据,Broker端都只会持久化一条,保证了不重复。

​ 精确一次(Exactly Once) = 幂等性 + 至少一次( ack=-1 + 分区副本数>=2 + ISR最小副本数量>=2) 。

重复数据的判断标准:

​ 具有<PID, Partition, SeqNumber>相同主键的消息提交时,Broker只会持久化一条。其中PID是Kafka每次重启都会分配一个新的;Partition 表示分区号;Sequence Number是单调自增的。

​ 所以幂等性只能保证的是在单分区单会话内不重复

如何使用幂等性

​ 开启参数 enable.idempotence,默认为 true,false 关闭。

kafka事务

​ 幂等性只能解决单个会话内消息不重复,一旦生产者挂了后,无法保证消息不重复。kafka事务就是为了实现跨分区跨会话的消息不重复。

**原理:**它引入了一个全局唯一的TransactionID,并且将producer获得pid和TransactionID绑定,这样当producer重启后就可以通过正在运行的TransactionID获得原来的pid。

为了管理Transaction,Kafka引入了事务协调器Transaction Coordinator,Producer通过Transaction Coordinator交互得到transactionid对应的任务状态。

Transaction Coordinator还负责将事务所有写入一个kafka内部的topic,这样即使服务重启,运行中的事务也能得到恢复,从而继续运行。

在代码中使用方法

// 设置事务 id(必须),事务 id 任意起名
 properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "transaction_id_0");
 // 3. 创建 kafka 生产者对象
 KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
 // 初始化事务
 kafkaProducer.initTransactions();
 // 开启事务
 kafkaProducer.beginTransaction();
 try {
 	// 4. 调用 send 方法,发送消息
	 for (int i = 0; i < 5; i++) {
 	// 发送消息
 	kafkaProducer.send(new ProducerRecord<>("first", "atguigu " + i));
 }
 // int i = 1 / 0;
 // 提交事务
 kafkaProducer.commitTransaction();
 } catch (Exception e) {
 // 终止事务
 kafkaProducer.abortTransaction();
 } finally {
 // 5. 关闭资源
 kafkaProducer.close();
 }

数据有序

  • kafka在1.x版本之前保证数据单分区有序,条件如下:

    max.in.flight.requests.per.connection=1(不需要考虑是否开启幂等性)。

  • kafka在1.x及以后版本保证数据单分区有序,条件如下:

    • 开启幂等性

    • max.in.flight.requests.per.connection(队列中请求个数)需要设置小于等于5。

      因为在kafka1.x以后,启用幂等后,kafka服务端会缓存producer发来的最近5个request的元数据,

    故无论如何,都可以保证最近5个request的数据都是有序的。