对线面试官 - MQ数据丢失问题的解决方案

2023-09-08 17:18:27 浏览数 (2)

书接上文。这次继续聊一聊MQ,往期文章参考:

对线面试官-为什么要使用MQ

对线面试官 - MQ经典面试题之高可用性及幂等性

面试官:继上次你对MQ的回答,我还有一些疑问想要针对MQ。我们继续聊一聊?

派大星:好的,当然可以。

面试官:OK,那我们继续上次的话题,就是MQ如何保证消息的可靠性,或者说如何保证消息不丢失呢?

派大星:这种情况需要就不同情况进行分析。主要是有三张场景会导致消息丢失的问题。

  1. 生产者丢失了消息
  2. MQ丢失了消息
  3. 消费的时候丢失了消息

面试官:嗯,不错,那你能就每种情况简单聊一聊吗?

派大星:可以,首先我先简单说一下RabbitMQ丢失消息如何解决。每种消息丢失的情况的解决方案大致如下图所示:

  • 首先来说一说生产者丢失了消息:

主要场景是:写消息等过程中消息还没有到达MQ的时候,在网络传输的过程中就将消息丢失了;或者消息到了RabbitMQ但是MQ内部错乱没有存储消息导致消息丢失。

解决方案1:可以使用RabbitMQ事务机制,具体配置如下:

代码语言:javascript复制
channel.txSelect();
try{
    // 发送消息
}catch{
    channel.txRollback();
}
channel.txCommit();

但是该种方案也有弊端:因为是事务机制,所以是同步阻塞的,这样就会导致生产者发送消息的吞吐量大大下降解决方案2:把channel设置成confirm模式,发送一个消息就不用管了,RabbitMQ如果接收到了这个消息就会回调生产者本地的一个接口,通知你说这条消息已经发送成功并且接收成功,反之也会通知。这种方式的吞吐量也会高一些。

  • 其次说一些RabbitMQ自己弄丢了消息

这种情况的解决方案可以将RabbitMQ设置为持久化。除非有及其罕见的情况RabbitMQ还没来得及持久化自己就挂了,可能会导致少量的数据丢失,当然这种概率是很小的。

  • 最后便是第三种情况:消费者丢失了消息

只有当你打开了消费者的autoAck的这样一个机制:你消费到了数据之后消费者会自动通知RabbitMQ说我已经消费到了这条数据;这样会出现一种情况:假设你消费到了一条数据但是还没有处理完,此时消费者就自动autoAck了。此时恰巧消费者系统服务挂了,消息还没来得及处理而且RabbitMQ以为该消息已经处理掉了。解决方案便是关掉RabbitMQ的自动ACK机制

面试官:不错,刚刚你有提到RabbitMQ设置持久化。你知道它怎么配置持久化吗:

派大星:直到的。具体步骤如下(注意两者缺一不可,需同时设置):

  • 创建queue的时候将其设置为持久化的,这样保证RabbitMQ持久化queue的元数据,但是不会持久化queue里的数据
  • 另外发送消息的时候将消息的deliveryMode设置为2,就是将消息设置为持久化。此时RabbitMQ就会将消息持久化到磁盘上去。

面试官:不错,但是我们这边实际工作中用的MQ是Kafka居多,关于Kafka消息丢失就以上情况你了解具体的解决方案吗?

派大星:这个也了解一些。

  • 首先说一下。Kafka中消费者弄丢了消息的场景:

具体过程为消费者自动提交了offset,其实消息还没有处理完。和RabbitMQ情况差不多。解决方案为:就是关闭自动提交offset,手动提交offset

  • 其次说一下Kafka弄丢了消息

主要表现形式为:Kafka的leader接收到了消息但是还没来得及同步给follwer就挂了,此时follwer变成了leader。导致数据丢失。解决方案为:需要设置4个参数:

  1. 给topic设置replication.replicas参数,这个值必须要大于1,也就是要求每个parttion只要有两个副本。
  2. 在Kafka服务端设置min.insync.replicas参数:这个值必须要大于1,这个是要求一个leader只要感知到最少有一个follwer还跟自己保持联系,这样才能确保leader还有一个follwer。
  3. 在producer段设置ack=all:这个要求是每条数据必须是写入所有的replica之后,才能认为是成功了。
  4. 在producer端设置retries=MAX:这个要求一旦写入失败,就无限重试,卡在这里。

按照上述配置后至少保证了在Kafka段在leader所在的broker发生故障进行leader切换时,数据不会丢失。

  • 最后聊一下生产者丢失数据的情况

如果是按照上述方式配置了ack=all则一定不会丢,要求是:你的leader接收到消息,所有的follwer都同步到了消息之后,才认为本次消息发送成功,否则生产者会重试无限次。


0 人点赞