平时使用消息队列的时候难免会出现消息丢失的情况,而消息丢失的情况有好几种,数据的丢失问题,可能出现在生产者、MQ、消费者中,所以具体情况具体分析,故本文从 RabbitMQ 和 Kafka 分别来分析。

一、RabbitMQ

1、生产者丢失数据

生产者将数据发送到RabbitMQ的时候,可能发送到一半数据就丢失了,可能网络,也可能有其他的问题导致的。所以我们可以选择RabbitMQ提供的事务机制(不推荐)和Comfirm机制进行处理。

  • 事务机制
    选择用 RabbitMQ 提供的事务功能,就是生产者发送数据之前开启 RabbitMQ 事务channel.txSelect,然后发送消息,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重试发送消息;如果收到了消息,那么可以提交事务channel.txCommit。
// 开启事务
	channel.txSelect
	try {
	    // 这里发送消息
	} catch (Exception e) {
	    channel.txRollback
	
	    // 这里再次重发这条消息
	}
	
	// 提交事务
	channel.txCommit

但是使用事务机制,因为是同步的,会导致吞吐量下降,性能消耗太大,故此机制不推荐。

  • Comfirm机制
    在生产者那里设置开启 confirm 模式之后,每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会回传一个 ack 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。因为confirm模式是异步调用的,所以性能各方便都比事务机制好。

所以在生产者端防止数据丢失都选择confirm机制。

2、RabbitMQ丢失数据

在RabbitMQ防止数据丢失就得开启持久化,就是消息写入之后会持久化到磁盘,哪怕是 RabbitMQ 自己挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢。除非极其罕见的是,RabbitMQ 还没持久化,自己就挂了,可能导致少量数据丢失,但是这个概率较小。
设置持久化有两个步骤:

  • 创建queue的时候将它设置为持久化,但是只是持久化queue的元数据,不会持久化queue里面的数据
  • 设置deliveryMode为2,将数据持久化,RabbitMQ会将数据持久化到磁盘中。

所以必须同时设置两个持久化才行。

3、消费端丢失数据

消费端刚消费到,但是还没处理完,进程挂掉或者服务器关机等原因都可能导致数据丢失,这个时候就得用到RabbitMQ提供的ack机制,但是得关闭RabbitMQ自动ack,通过手动调取接口形式,自己处理完在ack。

kafka生产者丢失 kafka生产者消息丢失_数据

二、Kafka

1、消费端丢失数据

Kafka的消费端数据丢失跟RabbitMQ消费端数据丢失模式差不多,都是消费到了数据,但是你还没处理完,结果服务挂掉了,kafka自动提交了offset,导致kafka认为你已经消费掉消息了,所以数据就导致了丢失。那么只要关闭自动提交 offset,在处理完之后自己手动提交 offset,就可以保证数据不会丢。但是此时确实还是可能会有重复消费,比如你刚处理完,还没提交 offset,结果自己挂了,此时肯定会重复消费一次,自己保证幂等性就好了。

2、kafka丢失数据

kafka丢失数据是比较常见的一个场景,就是 Kafka 某个 broker 宕机,然后重新选举 partition 的 leader。此时其他的 follower 刚好还有些数据没有同步,结果此时 leader 挂了,然后选举某个 follower 成 leader 之后,数据就丢失了。所以一般设置如下4个参数:

  • 给 topic 设置 replication.factor 参数:这个值必须大于 1,要求每个 partition 必须有至少 2 个副本。
  • 在 Kafka 服务端设置 min.insync.replicas 参数:这个值必须大于 1,这个是要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader 挂了还有一个 follower 。
  • 在 producer 端设置 acks=all:这个是要求每条数据,必须是写入所有 replica 之后,才能认为是写成功了。
  • 在 producer 端设置 retries=MAX:这个是要求一旦写入失败,就无限重试,卡在这里了。

3、生产者丢失数据

如果设置了 acks=all,一定不会丢,要求是,你的 leader 接收到消息,所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。