大家好,我是大尧。

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

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

  1. RabbitMQ相对成熟稳定,这是我们选择它最主要的原因。

  2. 社区比较活跃,有完善的资料可以参考。

  3. Rabbitmq的吞吐量可以达到万级,完全满足我们系统的要求。

  4. RabbitMQ是Erlang语言开发的,性能比较好。

  5. 有完善的可视化界面,方便查看。

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

优点有:

  • 异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。

  • 应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。

  • 流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。

缺点有:

  • 系统可用性降低

  • 系统复杂度提高

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

3.1 简单模型

  • p:生成者

  • C:消费者

  • 红色部分:quene,消息队列

3.2 工作模型

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

  • p:生成者

  • C1、C2:消费者

  • 红色部分:quene,消息队列

3.3 发布订阅模型(fanout)

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

  • p:生成者

  • X:交换机

  • C1、C2:消费者

  • 红色部分:quene,消息队列

3.4 路由模型(routing)

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

  • p:生成者

  • X:交换机,接收到生产者的消息后将消息投递给与routing key完全匹配的队列

  • C1、C2:消费者

  • 红色部分:quene,消息队列

3.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 {@Autowiredprivate RabbitTemplate rabbitTemplate;@GetMapping("send")public void sendMessage(){/*** 生产者确认消息*/rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {@Overridepublic 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 {@Autowiredprivate RabbitTemplate rabbitTemplate;@GetMapping("send")public void sendMessage(){/*** 消息未达队列时返回该条消息*/rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {@Overridepublic 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的消息数据和元数据。这种模式适用于可靠性要求较高的场景。

『假如我是面试官』RabbitMQ我会这样问!相关推荐

  1. 假如我是面试官,我会怎么考究你?软件测试面试篇

    我见过很多拼命的同事,这个也学,那个也看,学习资料收集了一大堆,到头来不过是在"用战术上的勤奋,掩盖战略上的懒惰",结果依然无法在测试项目中一显身手.在竞争力如此之大的时代,等待的 ...

  2. [面试] 面试官: 你还有什么想问我的?

    面试官: 你还有什么想问我的? 面试官不想听到的问题 1. 没有 没有问题面试官可能觉得: (1) 你并没有关心这个岗位或者公司 (2) 不会发现问题不善于思考 2. 薪资待遇 这个应该留到给 HR ...

  3. 面试字节跳动,被面试官的源码问题问到怀疑人生

    面试字节跳动,被面试官的源码问题问到怀疑人生 最近,我的一位朋友在找工作,已经拿到了美团.快手等公司的Offer,准备选择其中一家入职了. 后来他又接到了字节跳动的电话,通知他去参加三面.从二面到三面 ...

  4. 假如我是面试官,我会这样虐你

    本文转载自微信公众号 zhisheng 又是金三银四的时候,我希望这份面试题能够祝你一臂之力! 自我和项目相关 1.自我介绍 2.你觉得自己的优点是?你觉得自己有啥缺点? 3.你有哪些 offer? ...

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

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新,可以微信搜索[小奇JAVA面试]第一时间阅 ...

  6. 面试官,求求你不要问我这么简单但又刁难的算法题了

    有时候面试官往往会问我们一些简单,但又刁难的问题,主要是看看你对问题的处理思路.如果你没接触过这些问题,可能一时之间还真不知道怎么处理才比较好,这种题更重要的是一种思维的散发吧,今天就来分享几道题面试 ...

  7. 面试官,你为什么老是问我”闭包“

    前言 写这边博文的背景是前段时间在参加深圳鹏城实验室后台研发工程师一职时被问及闭包是什么,之前对闭包的理解只是停留在使用层面,并未做深层次的了解.我的回答是闭包可以让内部函数访问其所在函数的局部变量. ...

  8. 单片机如何在一数据包前加标识呢_单挑Google面试官,一上来就问我Chrome底层原理和HTTP协议(万字长文)...

    前言 有人说,如果你懂得浏览器的工作原理,你就能解决80%的前端难题. 是的,了解浏览器的工作原理,有助于你的工作:而了解TCP/IP .HTTP等网络协议,更是对你未来的职业发展大有裨益. 下面,我 ...

  9. 如果我是面试官,我会问你 Spring 那些问题?

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"1024"获取公众号专属1024GB资料 来源:rrd.me/f77Hj 1.一般问题 ...

最新文章

  1. 手把手教你JavaEE的分页查询、分页展示,有了这个,你的项目又多了一个谈资
  2. 没有JS的前端:体积更小、速度更快!
  3. 航天智慧物流!智能汽车竞赛—航天赛道开始报名啦!
  4. php大并发 大流量 大存储解决方案
  5. Java毕业设计--健康推广管理系统项目实战【Springboot+mybatis+layui等实现】
  6. 蓝桥杯 ALGO-69 算法训练 字符串逆序
  7. BigQuant*中金财富“启明星”创新量化交易大赛开启,月月都拿奖
  8. 视觉SLAM十四讲 罗德里格斯公式推导 全解
  9. 如果IE浏览器是IE11以下版本跳转到升级页面
  10. skynet.pack序列化学习
  11. Windows10创建系统还原点
  12. python实现火车票查询
  13. 解析机器人视觉系统的神奇之处
  14. 用PS设计制作一款玉石图案
  15. c语言灵异事件之“字符串被吞”
  16. mysql 1593_MySQL数据库经典错误三 Last_IO_Errno: 1593(server-id冲突)
  17. gif图片的体积怎么缩小?如何压缩动图大小?
  18. Nginx限流和黑名单配置
  19. JAVA SE知识整合(暂时完结 五万七字)后续分点详解
  20. 【数据分析】全球医疗卫生开放数据概览

热门文章

  1. android webview mailto,Webview email link (mailto)
  2. mysql $lt_MongoDB与Mysql常用命令解释
  3. CSS中的emmet语法(使用缩写的方式提高书写html编写速度)
  4. [NC15034]德玛西亚万岁
  5. Codeforces Round #599A~D题解
  6. jsapi设计_一个简单API设计
  7. 第二节认识计算机教案,第二章 第二节 局域网的构建 教学设计_博客
  8. python怎样判断字符串可以反序列化_从字符串值issu反序列化的Python AWS Lambda Stringargument构造函数/工厂方法...
  9. Finding iPhone Memory Leaks: A “Leaks” Tool Tutorial[转]
  10. 浏览器保存密码后自动填充问题