kafka如何保证消息不丢失
消息丢失的场景
-
producer: config.Producer.RequiredAcks + 异步发送模式
- producer把消息发送给broker,因为网络抖动,消息没有到达broker-master
- producer把消息发送给broker-master,master接收到消息,在未将消息同步给follower之前,挂掉了
- 上面2个问题,都可以通过设置ACK来保证消息不丢失,即 config.Producer.RequiredAcks = sarama.WaitForAll
- 采用”异步发送模式“,当发送失败时,重新发送
-
consumer成功拉取到了消息,consumer未处理完业务逻辑,就挂了,导致消息没被正确的处理 ==>(手动提交):offset未提交的消息,被认为未被消费
Consumer.Offsets.AutoCommit.Enable = false // 禁用自动提交,改为手动提交
- 在处理完业务逻辑之后,调用sess.MarkMessage(msg, “”),手动提交
-
broker
-
Kafka 为分区(Partition)引入了多副本(Replica)机制。分区(Partition)中的多个副本之间会有一个叫做 leader 的家伙,其他副本称为 follower。我们发送的消息会被发送到 leader 副本,然后 follower 副本才能从 leader 副本中拉取消息进行同步。生产者和消费者只与 leader 副本交互。可以理解为其他副本只是 leader 副本的拷贝,它们的存在只是为了保证消息存储的安全性
-
试想一种情况:假如 leader 副本所在的 broker 突然挂掉,那么就要从 follower 副本重新选出一个 leader ,但是 leader 的数据还有一些没有被 follower 副本的同步的话,就会造成消息丢失(通过配置可以避免该问题)
-
// 每个分区(partition) 至少有3个副本
replication.factor >= 3
// 代表消息至少要被写入到2个副本才算是被成功发送
min.insync.replicas > 1
// 非ISR中的副本不能够参与选举
unclean.leader.election.enable = false
kafka的3种方式
At-most-once(最多一次):如果在ack超时或返回错误时,生产者没有重试,那么消息可能最终不会被写入Kafka topic,因此不会传递给消费者。(不存在消息重复,但是会丢失消息)
上图这种情况,当Producer第一次发送消息给Broker时,Broker将消息(x2,y2)追加到了消息流中,但是在返回Ack信号给Producer时失败了(比如网络异常) 。此时,Producer端触发重试机制,将消息(x2,y2)重新发送给Broker,Broker接收到消息后,再次将该消息追加到消息流中,然后成功返回Ack信号给Producer。这样下来,消息流中就被重复追加了两条相同的(x2,y2)的消息。
At-least-once(最少一次):
1. 如果生产者从Kafka broker接收到一个确认(ack)并且ack = all,这意味着消息已经被写入Kafka topic一次。
2. 然而,如果生产者ack超时或收到一个错误,它可能会重试发送消息:如果broker在它发送ack之前失败,但消息已经被成功写入Kafka topic后,此重试将导致消息被写入两次,因此将不止一次地传递给最终消费者
Exactly-once(精确一次):消息既不会丢失,也不会重复
如何实现exactly once
kafka通过幂等性和事务,实现exactly once。
1. 幂等producer
1. 当Producer发送消息(x2,y2)给Broker时
2. Broker接收到消息并将其追加到消息流中。
3. 此时,Broker返回Ack信号给Producer时,发生异常导致Producer接收Ack信号失败。
4. 对于Producer来说,会触发重试机制,将消息(x2,y2)再次发送,但是,由于引入了幂等性,在每条消息中附带了PID(ProducerID)和SequenceNumber。相同的PID和SequenceNumber发送给Broker,而之前Broker缓存过之前发送的相同的消息,那么在消息流中的消息就只有一条(x2,y2),不会出现重复发送的情况;
局限性:实现比较简单,同样的限制也比较大,存在下面的弊端(无法保证多分区多会话的幂等性)
1.只能保证 Producer 在单个会话内不丟不重:如果 Producer 出现意外挂掉再重启是无法保证的(幂等性情况下,是无法获取之前的状态信息,因此是无法做到跨会话级别的不丢不重)
2.幂等性不能跨多个 Topic-Partition,只能保证单个 partition 内的幂等性:当涉及多个 Topic-Partition 时,这中间的状态并没有同步
==> 如果需要跨会话、跨多个 topic-partition 的情况,需要使用 Kafka 的事务性来实现
2. 事务性
先看一个2PC用例图
kafka事务消息处理_Ivan-Zhong的博客-CSDN博客_kafka事务消息
Rocketmq事务消息
监听器(在发送方中定义)需要实现TransactionListener,有两个功能:① prepare消息发送成功,则在executeLocalTransaction()中执行本地事务;② 在checkLocalTransaction()中执行本地事务状态回查逻辑。
发送事务消息可能存在3种情况:
1)正常情况:步骤1 -> 步骤2 -> 步骤3 -> 步骤4
发送方先发送prepare消息,发送成功后执行本地事务,本地事务的执行成功通知RocketMQ服务器进行commit投递消息,本地事务执行失败通知RocketMQ服务器rollback删除消息不进行投递。
2)回查情况:步骤1 -> 步骤2 -> 步骤3 -> 步骤5 -> 步骤6 -> 步骤7
发送方发送prepare消息,发送成功后执行本地事务,但由于本地事务可能执行时间较长,如果超过一定时间RocketMQ服务器没有收到处理结果,不知道是commit还是rollback,这时会根据定时任务进行发送方本地事务状态的回查,在监听器中进行事务状态回查,如果查到本地事务处理结果,则返回commit或rollback状态;如果还是未查到本地事务处理结果,返回unknow状态,RocketMQ服务器不会做操作仍会进行定时回查。
3)异常情况:步骤1 -> 步骤2 -> 步骤3 -> 步骤5 -> 步骤6 -> 人工干预队列
基于情况2的情况,如果回查次数达到上限,将消息放入人工干预队列