Java高频面试题- 每日三连问?「Day9」—消息队列篇二

Java高频面试题- 每日三连问?「Day9」—消息队列篇二

问题导读

一、重复消费的情况出现过吗?如何解决的?

二、你是如何防止消息丢失的?

三、如何保证消息队列的高可用?


一、重复消费的情况出现过吗?如何解决的?

Java高频面试题- 每日三连问?「Day9」—消息队列篇二

正经回答:

  在一般网络环境下,都存在一定的网络延迟、网络抖动,网络问题导致消息重复发送的情况是难以避免的,毕竟网络环境无法预知。

因此MQ默认允许消息重复发送。是的,只要通过网络交换数据,就无法避免这个问题。秉承着打不过就加入的原则,解决这个问题的办法就是绕过这个问题。

那么问题就变成了:如果消费端收到两条一样的消息,应该怎样处理?

  RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题。因为这问题通常不是 MQ 自己保证的,而是消费方自己来保证的。

  比如说Kafka, 他实际上有个 offset 的概念(偏移量),就是每个消息写进去,都有一个 offset,代表消息的序号,然后 consumer 消费了数据之后,每隔一段时间(定时定期),会把自己消费过的消息的 offset 提交一下。

代表我已经消费过了,就算消费者重启,Kafka也会让消费者继上次消费到的offset继续消费。

场景示例:

  kafka 中有一条数据:A、B,kafka给这条数据分一个 offset(偏移量),offset为: 1001、1002。消费者从 kafka 去消费的时候,也是按照这个顺序去消费。

当消费者消费到 offset=1002 的这条数据(此时offset=1001还没消费完),刚提交 offset=1002 到 zookeeper,消费者进程就被重启了。

此时消费过的数据 A 的 offset 还没有提交,kafka 也就不知道消费者已经消费了1001这条数据。那么重启之后,消费者会找 Kafka 把上次消费到的那个地方后面的数据继续传递过来。数据 A 再次被消费。

Java高频面试题- 每日三连问?「Day9」—消息队列篇二

 如果消费者是拿到一条数据就往数据库里写一条,就会导致把数据 A 在数据库里插入了 2 次,导致数据不一致。

重复消费其实并不可怕,可怕的是你没考虑到重复消费时,怎么保证幂等性。

Java高频面试题- 每日三连问?「Day9」—消息队列篇二


解决重复消费的方案(保证幂等性)

幂等性,比如一个数据或者一个请求,给后台重复发多次,针对这类情况,你得确保对应的数据结果是不会改变的,不能因为发了多个相同请求导致数据出错。


二、你是如何防止消息丢失的?

正经回答:

Java高频面试题- 每日三连问?「Day9」—消息队列篇二


第一种:生产者弄丢了数据。生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,因为网络问题啥的,都有可能。

第二种:RabbitMQ 弄丢了数据。MQ还没有持久化自己挂了
第三种:消费端弄丢了数据。刚消费到,还没处理,结果进程挂了,比如重启了。

解决方案

Java高频面试题- 每日三连问?「Day9」—消息队列篇二

①:可以选择使用rabbitmq提供是事物功能

就是生产者在发送数据之前开启事物,然后发送消息,如果消息没有成功被rabbitmq接收到,那么生产者会受到异常报错,这时就可以回滚事物,然后尝试重新发送;如果收到了消息,那么就可以提交事物。

channel.txSelect();//开启事物
try{
//发送消息
}catch(Exection e){
channel.txRollback();//回滚事物
//重新提交
}

缺点:rabbitmq事物已开启,就会变为同步阻塞操作,生产者会阻塞等待是否发送成功,太耗性能会造成吞吐量的下降。

Java高频面试题- 每日三连问?「Day9」—消息队列篇二


②:可以开启confirm模式

在生产者哪里设置开启了confirm模式之后,每次写的消息都会分配一个唯一的id,然后如何写入了rabbitmq之中,rabbitmq会给你回传一个ack消息,告诉你这个消息发送OK了;

如果rabbitmq没能处理这个消息,会回调你一个nack接口,告诉你这个消息失败了,你可以进行重试。

而且你可以结合这个机制知道自己在内存里维护每个消息的id,如果超过一定时间还没接收到这个消息的回调,那么你可以进行重发。

//开启confirm
channel.confirm();
//发送成功回调
public void ack(String messageId){
}


// 发送失败回调
public void nack(String messageId){
    //重发该消息
}


三、如何保证消息队列的高可用?

正经回答:

可以考虑镜像模式部署消息队列

RabbitMQ 是基于主从做高可用性的,Rabbitmq有三种模式:单机模式、普通集群模式、镜像集群模式。

单机模式一般在生产环境中很少用,普通集群模式只是提高了系统的吞吐量,让集群中多个节点来服务某个 Queue 的读写操作。

那么真正实现 RabbitMQ 高可用的是镜像集群模式。

镜像集群模式跟普通集群模式不一样的是,创建的 Queue,无论元数据还是Queue 里的消息都会存在于多个实例上,然后每次你写消息到 Queue 的时候,都会自动和多个实例的 Queue 进行消息同步。

这样设计,好处在于:任何一个机器宕机不影响其他机器的使用。

坏处在于:1. 性能开销太大:消息同步所有机器,导致网络带宽压力和消耗很重;2. 扩展性差:如果某个 Queue 负载很重,即便加机器,新增的机器也包含了这个 Queue 的所有数据,并没有办法线性扩展你的 Queue。

Java高频面试题- 每日三连问?「Day9」—消息队列篇二

每日小结

今天我们复习了面试中常考的消息队列三个问题,你做到心中有数了么?

对了,如果你的朋友也在准备面试,请将这个系列扔给他,如果他认真对待,肯定会感谢你的!!

好了,今天就到这里,学废了的同学,记得三连,也会给我继续更新的动力。

展开阅读全文

页面更新:2024-05-04

标签:队列   消息   都会   吞吐量   生产者   集群   这个消息   正经   事物   消费者   机器   模式   情况   数据   科技   网络

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top