阅读 66

消息队列经典面试问题(四)

常见面试问题

  • 如何处理消息丢失问题?(消息可靠性传输)

在实际生产中,如果用mq来传递比较重要的消息,那肯定不能把消息搞丢了,那我们来看一下哪些情况会导致数据丢失,这里我们以rabbitMQ和kafka为例:

RabbitMQ

生产者弄丢数据:当生产者将数据发送给rabbitMq时,由于网络原因什么的都有可能导致数据丢失。

解决办法

1、利用RabbitMQ事务的功能,发送之前开启事务,然后发送消息,如果消息没有被mq收到,那么生产者会异常报错,此时可以回滚事务,然后重新发送,如果收到了,那么就可以提交事务了。(缺点:吞吐量下降,性能低

2、开启confirm模式,在生产者写入设置开启confirm模式,每次写入的消息都会分配一个唯一的id,如果写入mq成功,mq会给你回传一个ack消息,如果mq没能收到这个消息,会回调你一个nack接口,告诉你消息接收失败,你可以选择重试。confirm机制和事务最大不同是一个是异步的,一个是同步的,很明显同步会阻塞在那里。(生产使用

MQ弄丢数据:这个必须保证rabbitmq开启持久化,如果mq挂掉了,它恢复之后会自动读取存储倒硬盘上的数据,一般来说数据不会丢失,但是也有小概率mq还没来的及持久化到磁盘就挂掉了,这样会造成数据丢失。

解决办法

设置持久化有两个步骤,第一个是创建queue的时候将其设置为持久化的,这样就可以保证rabbitmq持久化queue的元数据,但是不会持久化queue里的数据;第二个是发送消息的时候将消息的deliveryMode设置为2,就是将消息设置为持久化的,此时rabbitmq就会将消息持久化到磁盘上去。必须要同时设置这两个持久化才行,rabbitmq哪怕是挂了,再次重启,也会从磁盘上重启恢复queue,恢复这个queue里的数据。(可以结合生产者confirm机制一起使用,持久化到磁盘上了,才会发送ack消息给生产者。)

消费者弄丢数据:这种情况一般是消费者消费到了数据,但是还没进行处理,就宕机了,rabbitmq认为你消费了,但是实际你没有处理。

解决办法:其实主要还是利用mq的ack机制,关闭mq的自动ack,程序处理完自己ack,当你没有ack的时候,消息一直处于不可以状态,当当前的consumer 发生异常断开连接 broker 才会将该条消息发送给其他的consumer。

Kafka

消费者弄丢数据:当你消费到了这个消息,然后消费者自动提交了offset,但是你还没处理这个消息时,进程挂了,这样这条消息就丢失了,

解决办法:关闭自动提交offset,处理完自己手动提交offset,这样就能保证数据不丢失。(注意这样虽然能保证数据不丢失,但是有可能重复消费,当你处理完,还没来得及提交offset的时候进程挂了,这样就会造成重复消费,要注意自己保证幂等性。)

kafka弄丢数据:当kafka某个broker宕机,然后重新选举partiton的leader时,此时其他的follower刚好还有些数据没有同步,结果此时leader挂了,然后选举某个follower成leader之后,这样就导致数据丢失了。

解决办法:设置如下4个参数:

1、给topic设置replication.factor参数,这个值必须大于1,要求每个partition必须有至少2个副本。

2、在kafka服务端设置min.insync.replicas参数,这个值必须大于1,要求一个leader至少感知到至少有一个follower还跟自己保持联系,这样才能确保leader挂了至少还有一个follower。

3、在producer端设置acks=all,要求每条数据,必须是写入所有replica之后,才能认为是写成功了。

4、在producer端设置retries=MAX(很大很大很大的一个值,无限次重试的意思),要求一旦写入失败,就无限重试。

生产者弄丢数据:当按照上述设置了acks=all,生产者是不会弄丢数据的,因为只有所有的follower都同步到了消息,才认为写成功了,否则会无限重试。