1. 为什么你们公司选择RabbitMQ作为消息中间件

在消息队列选型时,我们调研了市场上比较常用ActiveMQ,RabbitMQ,RocketMQ,Kafka。

  1. RabbitMQ相对成熟稳定,这是我们选择它最主要的原因。
  2. 社区比较活跃,有完善的资料可以参考。
  3. Rabbitmq的吞吐量可以达到万级,完全满足我们系统的要求。
  4. RabbitMQ是Erlang语言开发的,性能比较好。
  5. 有完善的可视化界面,方便查看。

2. 消息队列的优点和缺点有哪些

优点有:

  • 异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。
  • 应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。
  • 流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。

缺点有:

  • 系统可用性降低
  • 系统复杂度提高

3. RabbitMQ常用的工作模式有哪些

2.1 简单模型

  • p:生成者
  • C:消费者
  • 红色部分:quene,消息队列

2.2 工作模型

这种模式下一条消息只能由一个消费者进行消费,默认情况下,每个消费者是轮询消费的。

  • p:生成者
  • C1、C2:消费者
  • 红色部分:quene,消息队列

2.3 发布订阅模型(fanout)

这种模型中生产者发送的消息所有消费者都可以消费。

  • p:生成者
  • X:交换机
  • C1、C2:消费者
  • 红色部分:quene,消息队列

2.4 路由模型(routing)

这种模型消费者发送的消息,不同类型的消息可以由不同的消费者去消费。

  • p:生成者
  • X:交换机,接收到生产者的消息后将消息投递给与routing key完全匹配的队列
  • C1、C2:消费者
  • 红色部分:quene,消息队列

2.5 主题模型(topic)

这种模型和direct模型一样,都是可以根据routing key将消息路由到不同的队列,只不过这种模型可以让队列绑定routing key 的时候使用通配符。这种类型的routing key都是由一个或多个单词组成,多个单词之间用.分割。

通配符介绍:

*:只匹配一个单词

#:匹配一个或多个单词

4. 如何保证消息不丢失(如何保证消息的可靠性)

一条消息从生产到消费经历了三个阶段,分别是生产者,MQ和消费者,对于RabbitMQ来说,消息的传递还涉及到交换机。因此RabbitMQ出现消息丢失的情况有四个

分别是

  1. 消息生产者没有成功将消息发送到MQ导致消息丢失
  2. 交换机未路由到消息队列导致消息丢失
  3. 消息在MQ中时,MQ发生宕机导致消息丢失
  4. 消费者消费消息时出现异常导致消息丢失

针对上面提到的四种情况,分别进行处理

  1. amqp协议提供了事务机制,在投递消息时开启事务,如果消息投递失败,则回滚事务,很少有人去使用事务。除了事务之外,RabbitMQ还提供了生产者确认机制(publisher confirm)。生产者将信道设置成confirm(确认)模式,一旦信道进入confirm模式,所有在该信道上面发布的消息都会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,RabbitMQ就会发送一个确认(Basic.Ack)给生产者(包含消息的唯一ID),这就使得生产者知晓消息已经正确到达了目的地了。
# 开启生产者确认机制,
# 注意这里确认的是是否到达交换机
spring.rabbitmq.publisher-confirm-type=correlated
@RestController
public class Producer { @Autowired
private RabbitTemplate rabbitTemplate; @GetMapping("send")
public void sendMessage(){
/**
* 生产者确认消息
*/
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println(correlationData);
System.out.println(ack);
System.out.println(cause);
}
});
rabbitTemplate.convertAndSend("s","error","这是一条错误日志!!!");
}
}
  1. 消息从交换机未能匹配到队列时将此条消息返回给生产者
spring.rabbitmq.publisher-returns=true
@RestController
public class Producer { @Autowired
private RabbitTemplate rabbitTemplate; @GetMapping("send")
public void sendMessage(){
/**
* 消息未达队列时返回该条消息
*/
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println(returnedMessage);
}
});
rabbitTemplate.convertAndSend("s","error","这是一条错误日志!!!");
}
}
  1. 消息在交换机或队列中发生丢失,我们只需要将交换机和队列进行持久化。
/**
* 定义一个持久化的topic交换机
* durable 持久化
* @return
*/
@Bean
public Exchange exchangeJavatrip(){
return ExchangeBuilder.topicExchange(EXCHANGE).durable(true).build();
} /**
* 定义一个持久化的队列
* durable 持久化
* @return
*/
@Bean
public Queue queueJavatrip(){
return QueueBuilder.durable(QUEUE).build();
}
  1. 消费者开启手动签收模式,消费完成后进行ack确认。
spring.rabbitmq.listener.simple.acknowledge-mode=manual
@RabbitListener(queues = MqConfig.QUEUE)
public void receive(String body, Message message, Channel channel) throws Exception{
long deliveryTag = message.getMessageProperties().getDeliveryTag();
System.out.println(deliveryTag);
// 系统业务逻辑判断是否签收
if(deliveryTag % 2 == 0){
channel.basicAck(deliveryTag,false);
}else{
// 第二个参数是否批量确认,第三个参数是否重新回队列
channel.basicNack(deliveryTag,false,true);
}
}

5. 如何保证消息不重复消费(如何保证消息的幂等性)

消息重复的原因有两个:

  1. 生产时消息重复

    由于生产者发送消息给MQ,在MQ确认的时候出现了网络波动,生产者没有收到确认,实际上MQ已经接收到了消息。这时候生产者就会重新发送一遍这条消息。

  2. 消费时消息重复。

    消费者消费成功后,在给MQ确认的时候出现了网络波动,MQ没有接收到确认,为了保证消息被消费,MQ就会继续给消费者投递之前的消息。这时候消费者就接收到了两条一样的消息。

由于消息重复是网络波动等原因造成的,无法避免,我们能做的的就是保证消息的幂等性,以防业务重复处理。具体处理方案为:

让每个消息携带一个全局的唯一ID,即可保证消息的幂等性,具体消费过程为:

  1. 消费者获取到消息后先根据id去查询redis/db是否存在该消息。
  2. 如果不存在,则正常消费,消费完毕后写入redis/db。
  3. 如果存在,则证明消息被消费过,直接丢弃。
@RabbitListener(queues = MqConfig.QUEUE)
public void receive(Message message, Channel channel){ String messageId = message.getMessageProperties().getMessageId();
String body = new String(message.getBody());
String redisId = redisTemplate.opsForValue().get(messageId)+"";
// 如果redis中存有当前消息的消息id
// 则证明消费过
if(messageId.equals(redisId)){
return;
}
redisTemplate.opsForValue().set(messageId, UUID.randomUUID());
}

6. 消息大量堆积应该怎么处理

消息堆积的原因有两个

  1. 网络故障,消费者无法正常消费
  2. 消费方消费后未进行ack确认

解决方案如下:

  1. 检查并修复消费者故障,使其正常消费
  2. 编写临时程序将堆积的消息发送到容量更大的MQ集群,增加消费者快速消费
  3. 堆积消息消费完毕后,停止临时程序,恢复正常消费

7. 死信是什么?死信如何处理

当一条消息在队列中出现以下三种情况的时候,该消息就会变成一条死信。

  • 消息被拒绝(basic.reject / basic.nack),并且requeue = false
  • 消息TTL过期
  • 队列达到最大长度

当消息在一个队列中变成一个死信之后,如果配置了死信队列,它将被重新publish到死信交换机,死信交换机将死信投递到一个队列上,这个队列就是死信队列。

一条消息成为死信后,一般会通过死信队列进行存库,然后定时将库中的死信进行重新投递到消息队列上。

8. 如果我有一笔订单,30分钟未支付则关闭订单,使用RabbitMQ如何来实现

RabbitMQ可以使用死信队列来实现延时消费,用户下单之后,将订单信息投递到消息队列中,并且设置消息过期时常为30分钟。如果用户支付则正常关闭订单,如果用户未支付,消息达到过期时间,消息会进入死信交换,由消费者进行消费死信队列来关闭订单。

9. RabbitMQ如何保证高可用

RabbitMQ有两种集群模式,分别是普通集群和镜像集群,普通模式无法保证RabbitMQ的高可用。

普通集群

假如有三个节点,rabbitmq1、rabbitmq2、rabbitmq3,消息实际上只存在于其中一个节点,三个节点仅有相同的元数据,即队列的结构,当消息进入rabbitmq2节点的queue后,consumer从rabbitmq1的节点进行消费,rabbitmq1和rabbitmq2会进行临时通信,从rabbitmq2中获取消息然后返回给consumer。

这种模式存在以下两个问题:

  1. 当rabbitmq2宕机后,消息无法正常消费,没有做到真正的高可用

  2. 实际数据还是在单个实例上,存在瓶颈问题

镜像集群

假如有三个节点,rabbitmq1、rabbitmq2、rabbitmq3,每个实例之间都可以相互通信,每次生产者写消息到queue的时候,每个rabbitmq节点上都有queue的消息数据和元数据。这种模式使用于可靠性要求较高的场景。

点关注、不迷路

如果觉得文章不错,欢迎关注、点赞、收藏,你们的支持是我创作的动力,感谢大家。

如果文章写的有问题,请不要吝惜文笔,欢迎留言指出,我会及时核查修改。

如果你还想看到更多别的东西,可以微信搜索「Java旅途」进行关注。回复“手册”领取Java面试手册!

『假如我是面试官』RabbitMQ我会这样问的更多相关文章

  1. 面试官:RabbitMQ过期时间设置、死信队列、延时队列怎么设计?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 RabbitMQ我们经常的使用, ...

  2. 我是面试官--"自我介绍"

    工作10余年,经历过很多次面试,也面试了N多人.这些年来,已经有好些位朋友(或同事)与我聊起相关话题,涉及面试,更关乎职业生涯规划.感触颇多,就借助自媒体的浪潮,与更多的程序员一起共谈面试经历,希望可 ...

  3. 阿里面试官用HashMap把我问倒了

    本人是一名大三学生,最近在找暑期实习,其中也面试过两次阿里,一次菜鸟网络部门.一次网商银行部门,当然我都失败了,同时也让我印象很深刻,因此记录了其中一些面试心得,我觉得这个问题很值得分享,因此分享给大 ...

  4. 面试官:RabbitMQ有哪些工作模式?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 今天又.又.又来面试了,还是老规 ...

  5. 面试官:RabbitMQ怎么实现消费端限流

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 RabbitMQ有很多高级特性, ...

  6. 《吊打面试官》系列-Redis基础

    你知道的越多,你不知道的越多 点赞再看,养成习惯 前言 Redis在互联网技术存储方面使用如此广泛,几乎所有的后端技术面试官都要在Redis的使用和原理方面对小伙伴们进行360°的刁难.作为一个在互联 ...

  7. [转帖]《吊打面试官》系列-Redis基础

    <吊打面试官>系列-Redis基础 https://www.cnblogs.com/aobing/archive/2019/11/07/11811194.html   你知道的越多,你不知 ...

  8. 《吊打面试官》系列-Redis基础知识

    前言Redis在互联网技术存储方面使用如此广泛,几乎所有的后端技术面试官都要在Redis的使用和原理方面对小伙伴们进行360°的刁难.作为一个在互联网公司面一次拿一次offer的面霸(请允许我使用一下 ...

  9. 三面面试官:运行 npm run xxx 的时候发生了什么?

    事情是这样的,直接开讲 面试官:npm run xxx的时候,发生了什么?讲的越详细越好. 我(心想,简单啊): 首先,DNS 解析,将域名解析成 IP 地址,然后 TCP 连接,TCP 三次握手.. ...

随机推荐

  1. Sublim text3汉化

    Sublime text3汉化步骤: 一.打开已安装好的Sublime text3 二.打开后点击"Preferences",在弹出的选项中找到package Control. 三 ...

  2. Spring MVC工作原理及源码解析(一) MVC原理介绍、与IOC容器整合原理

    MVC原理介绍 Spring MVC原理图 上图是Spring MVC工作原理图(图片来自网上搜索),根据上图,我们可以得知Spring MVC的工作流程如下: 1.用户(客户端,即浏览器)发送请求至 ...

  3. Kafka万亿级消息实战

    一.Kafka应用 本文主要总结当Kafka集群流量达到 万亿级记录/天或者十万亿级记录/天  甚至更高后,我们需要具备哪些能力才能保障集群高可用.高可靠.高性能.高吞吐.安全的运行. 这里总结内容主 ...

  4. 1. Java概述

    1.1 Java语言背景介绍(了解) 语言:人与人交流沟通的表达方式. 计算机语言:人与计算机之间进行信息交流沟通的一种特殊语言. Java语言是美国Sun公司(Stanford University ...

  5. [Java] javaEE

    定义 面向企业级应用中一些通用模块制定的标准 避免重复开发,解决代码可靠性问题 13种规范 JDBC(JavaDatabase Connectivity):数据库连接 以统一方式访问数据库的API J ...

  6. 与find不同,locate并不是实时查找。你需要更新数据库,以获得最新的文件索引信息。updatedb

    find是实时查找,如果需要更快的查询,可试试locate:locate会为文件系统建立索引数据库,如果有文件更新,需要定期执行更新命令来更新索引库: $locate string 寻找包含有stri ...

  7. Ubuntu编译安装TrinityCore3.3.5

    系统:Ubuntu 14.04.4 LTS (GNU/Linux 3.13.0-32-generic x86_64) 1核2G Notice:内存不可过小,否则会编译失败 #安装一堆东西 4 apt- ...

  8. linux中cat、more、less命令区别详解##less 最合适最好用,和vim一样好用

    linux中cat.more.less命令区别详解 caoxinyiyi关注 0.0362018.07.02 15:46:17字数 641阅读 516 linux中命令cat.more.less均可用 ...

  9. mysql基础之数据库备份和恢复的基础知识

    备份数据的最终目的是为了在出现一些意外情况时,能够通过备份将数据还原,所以单单的备份数据往往是无法满足还原时的需求的,所以在备份数据库时,除了要备份数据本身,还要备份相关的数据库环境,如配置文件,定时 ...

  10. HarmonyOS之DevEco2中真实手机调试应用

    本文将带你了解如何在真机上调试HarmonyOS应用 本人按照官方文档执行,还是遇到几个问题,特此记录下 https://developer.harmonyos.com/cn/docs/documen ...