文章目录
- Kafka和RabbitMQ什么区别
- 惰性队列(Lazy Queues)是怎么实现的?
- RabbitMQ工作模式有哪些 ?
- 你们项目中哪里用到了RabbitMQ ?
- 为什么会选择使用RabbitMQ ? 有什么好处 ?
- 使用RabbitMQ如何保证消息不丢失 ?
- 消息的重复消费问题如何解决的 ?
- 如果有100万消息堆积在MQ , 如何解决 ?
- RabbitMQ如何保证消费的顺序性 ?
- RabbitMQ的延迟队列有了解过嘛 ?
- RabbitMQ如何设置消息过期 ?
- 什么情况下消息会成为死信 ?
- 什么是死信交换机 ? 如何为队列绑定死信交换机 ?
- RabbitMQ的高可用机制有了解过嘛 ?
更多相关内容可查看
Kafka和RabbitMQ什么区别
RabbitMQ | RocketMQ | Kafka | |
---|---|---|---|
公司/社区 | Rabbit | 阿里 | Apache |
开发语言 | Erlang | Java | Scala&Java |
协议支持 | AMQP,XMPP,SMTP,STOMP | 自定义协议 | 自定义协议 |
可用性 | 高 | 高 | 高 |
单机吞吐量 | 一般 | 高 | 非常高 |
消息延迟 | 微秒级 | 毫秒级 | 毫秒以内 |
消息可靠性 | 高 | 高 | 一般 |
- 底层开发语言不同 : RabbitMQ底层采用Erlang语言开发 , Kafka是基于Java和Scala语言的
- 协议支持不一样 : RabbitMQ支持AMQP和SMTP通过插件还能支持MQTT等协议 , Kafka是自定义的协议
- Kafka的吞吐量比RabbitMQ更高 , 性能比RabbitMQ更好
- RabbitMQ功能非常丰富 , 支持各种消息模式, 支持延迟消息 , 支持惰性队列 , 镜像队列等 , 还能通过插件扩展功能
- RabbitMQ对于消息可靠性保证的机制比较多, 例如 : 生产者确认机制 , 消费者签收机制等
- RabbitMQ基于队列存储消息和消费消息 , Kafka基于主题分区存储消息和消费消息
你们公司为什么选择RabbitMQ ?
我们选择RabbitMQ主要基于一下考虑 :
- RabbitMQ从可用性, 吞吐量, 消息延迟, 消息可靠性方面来说比较均衡
- RabbitMQ中支持丰富的消息收发类型
- 点对点消息 : 简单队列模式/工作队列模式
- 发布订阅消息 : 广播模式/ 路由模式/ 主题模式
- RabbitMQ支持丰富丰富的插件扩展, 通过插件可以提供更加丰富的特性和功能
- RabbitMQ社区非常活跃, 有大量的文档和案例供开发者学习
- 我们团队中有其他小伙伴之前用过RabbitMQ , 我们现在使用RabbitMQ成本比较低, 也可以规避一些使用过程中的问题
惰性队列(Lazy Queues)是怎么实现的?
惰性队列是指一种特殊类型的队列,它的主要特点是在消息持久化到磁盘时,采用了一种懒加载(Lazy Loading)的方式。当消息被发送到惰性队列时,并不立即将消息写入磁盘,而是先将消息缓存在内存中,然后根据一定的条件(如内存使用情况)将消息异步地写入磁盘。这样可以提高性能
RabbitMQ工作模式有哪些 ?
Rabbitmq的工作模式有六种:
- simple简单模式
生产者直接发送消息给RabbitMQ队列,消费者从队列消费。未定义和指定Exchange的情况下,使用的是内置的AMQP default这个Exchange
- work工作模式
生产者发送消息到队列中,多个消费者消费同一个队列的数据,可以达到负载均衡的效. 消费者之间对于同一个消息的关系是竞争的关系。
对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。例如:短信服务部署多个,只需要有一个节点成功发送即可
- publish/subscribe订阅模式
发布订阅模式主要是使用fanout类型交换器,routingKey忽略。每个消费者定义生成一个队列并绑定到同一个Exchange,每个消费者都可以消费到完整的消息。消息广播给所有订阅该消息的消费者。
- routing路由模式
路由模式主要使用direct类型的Exchange,发N条消费并使用不同的routingKey,消费者定义队列并将队列、routingKey、Exchange绑定。使用direct模式Exchagne必须要routingKey完全匹配的情况下消息才会转发到对应的队列中被消费
- topic 通配符模式
Topics 通配符模式主要是使用topic类型的交换器,队列绑定到交换器、bindingKey时使用通配符,交换器将消息路由转发到具体队列时会根据消息routingKey模糊匹配,比较灵活
topic类型的交换器背后原理跟direct类型的类似:只要队列的bindingKey的值与消息的routingKey匹配,队列就可以收到该消息。
通配符规则:#匹配一个或多个词,*匹配不多不少恰好1个词
- RPC模式
RPC即客户端远程调用服务端的方法 ,使用MQ可以实现RPC的异步调用,基于Direct交换机实现,流程如下:
1、客户端即是生产者也是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC响应队列。
2、服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果。
3、服务端将RPC方法 的结果发送到RPC响应队列。
4、客户端(RPC调用方)监听RPC响应队列,接收到RPC调用结果。
你们项目中哪里用到了RabbitMQ ?
订单处理: 当用户提交采购订单时,ERP 系统可以将订单信息发布到 RabbitMQ 的消息队列中。然后,采购模块的消费者从消息队列中获取订单信息,进行处理,如验证订单、生成采购单等。
库存更新: 在采购订单被处理后,采购模块需要更新库存信息。ERP 系统可以将库存更新的消息发布到 RabbitMQ 中,由库存管理模块的消费者接收并更新库存数据。
RabbitMQ通过异步的方式来读取大数据量的数据
为什么会选择使用RabbitMQ ? 有什么好处 ?
选择使用RabbitMQ是因为RabbitMQ的功能比较丰富 , 支持各种消息收发模式(简单队列模式, 工作队列模式 , 路由模式 , 直接模式 , 主题模式等) , 支持延迟队列 , 惰性队列而且天然支持集群, 保证服务的高可用, 同时性能非常不错 , 社区也比较活跃, 文档资料非常丰富
使用MQ有很多好处:
- 吞吐量提升:无需等待消费者处理完成,响应更快速
- 故障隔离:服务没有直接调用,不存在级联失败问题
- 调用间没有阻塞,不会造成无效的资源占用
- 耦合度极低,每个服务都可以灵活插拔,可替换
- 流量削峰:不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件
使用MQ也有很多缺点:
- 架构复杂了,业务没有明显的流程线,不好管理
- 需要依赖于Broker的可靠、安全、性能
因为我们公司中之前有其他小伙伴/之前有用过RabbitMQ作为消息中间件, 有一定的技术储备, 所以我们会选择使用RabbitMQ作为消息中间件, 这样学习成本和使用成本都很低, 而且能够避免在使用过程中的一些问题
使用RabbitMQ如何保证消息不丢失 ?
消息从发送,到消费者接收,会经理多个过程 , 其中的每一步都可能导致消息丢失
针对这些问题,RabbitMQ分别给出了解决方案:
- 消息发送到交换机丢失 : 发布者确认机制
publisher-confirm
消息发送到交换机失败会向生产者返回ACK , 生产者通过回调接收发送结果 , 如果发送失败, 重新发送, 或者记录日志人工介入
- 消息从交换机路由到队列丢失 : 发布者回执机制
publisher-return
消息从交换机路由到队列失败会向生产者返回失败原因 , 生产者通过回调接收回调结果 , 如果发送失败, 重新发送, 或者记录日志人工介入
-
消息保存到队列中丢失 : MQ持久化(交换机持久化, 队列持久化 , 消息持久化)
-
消费者消费消息丢失 : 消费者确认机制 , 消费者失败重试机制
通过RabbitMQ本身所提供的机制基本上已经可以保证消息不丢失 , 但是因为一些特殊的原因还是会发送消息丢失问题 , 例如 : 回调丢失 , 系统宕机, 磁盘损坏等 , 这种概率很小 , 但是如果想规避这些问题 , 进一步提高消息发送的成功率, 也可以通过程序自己进行控制
设计一个消息状态表 , 主要包含 : 消息id , 消息内容 , 交换机 , 消息路由key , 发送时间, 签收状态等字段 , 发送方业务执行完毕之后 , 向消息状态表保存一条消息记录, 消息状态为未签收 , 之后再向MQ发送消息 , 消费方接收消息消费完毕之后 , 向发送方发送一条签收消息 , 发送方接收到签收消息之后 , 修改消息状态表中的消息状态为已签收 ! 之后通过定时任务扫描消息状态表中这些未签收的消息 , 重新发送消息, 直到成功为止 , 对于已经完成消费的消息定时清理即可 !
消息的重复消费问题如何解决的 ?
- 消息去重表: 维护一个消息去重表,记录已经处理过的消息的唯一标识符。当消费者接收到消息时,首先检查该消息的唯一标识符是否已经存在于消息去重表中,如果存在则认为该消息已经被处理过,直接丢弃;如果不存在,则进行消息处理,并将该消息的唯一标识符记录到消息去重表中。
- 消息确认机制: 在消费者成功处理完消息后,向消息队列发送确认消息。消息队列收到确认消息后将该消息标记为已消费,下次不会再次发送给该消费者。RabbitMQ 中使用的是 AMQP 协议,其中包含了消息确认机制。
如果有100万消息堆积在MQ , 如何解决 ?
解决消息堆积有二种思路:
思路一 : 提高消费者的消费能力 , 可以开启多线程消费 , 条件允许的情况下可以增加消费者的数量 , 提高消费速度
思路二:扩大队列容积,提高堆积上限 , 将来不及消费的消息保存在磁盘上 , 高峰期过了之后慢慢消费 , 可以使用RabbitMQ惰性队列
RabbitMQ如何保证消费的顺序性 ?
所谓顺序消费,即保证消息的发送顺序要与消息的消费顺序保持一致 , RabbitMQ消息发送到队列中 , 队列本身就是有序的 , 所以RabbitMQ保证消费的顺序性 , 只用考虑消费者即可 , 出现消息乱序的场景主要有二种情况 :
- 一个queue,有多个consumer去消费,这样就会造成顺序的错误,consumer从MQ里面读取数据是有序的,但是每个consumer的执行时间是不固定的,无法保证先读到消息的consumer一定先完成操作,这样就会出现消息并没有按照顺序执行
- 一个queue对应一个consumer,但是consumer里面进行了多线程消费,这样也会造成消息消费顺序错误。
解决方案 :
- 队列与消费者一对一 , 一个队列设置一个消费者
将原来的一个queue拆分成多个queue,需要保证顺序性的一批消息发送到同一个队列 , 然后保证每个queue对应一个consumer
- 使用内存队列 , 一个queue只对应一个consumer,然后这个consumer内部用内存队列做排队,然后分发给底层不同的worker来处理
消费者不消费数据, 而是将需要保证顺序性的一批次消息保存到内存队列 , 开启工作线程从队列中获取消息消费
RabbitMQ的延迟队列有了解过嘛 ?
RabbitMQ的延迟队列有二种实现方案 :
一种常见的实现延迟队列的方式是使用 RabbitMQ 插件:RabbitMQ Delayed Message Plugin。该插件允许你在消息发送时指定延迟时间,消息将在指定的延迟时间之后才会被投递到队列中。安装和配置该插件后,你可以通过设置消息的 TTL(Time-To-Live)和消息的特定属性来实现延迟队列的功能。
另一种方式是自定义实现延迟队列,主要思路是使用两个队列:一个用于存放需要延迟处理的消息,另一个用于存放已经到达延迟时间的消息。当消息到达延迟时间后,将其从延迟队列移动到实际的队列中进行消费。这种方式需要在消息生产者和消费者中实现额外的逻辑来管理延迟消息的处理。
RabbitMQ如何设置消息过期 ?
RabbitMQ置消息过期的方式有二种 :
- 为队列设置过期时间, 所有进到这个队列的消息就会具有统一的过期时间
@Bean
public Queue ttlQueue(){
return QueueBuilder.durable("ttl.queue") // 指定队列名称,并持久化
.ttl(10000) // 设置队列的超时时间,10秒
.deadLetterExchange("dl.ttl.direct") // 指定死信交换机
.build();
}
- 为消息单独设置过期时间
@Test
public void testTTLQueue() {
// 创建消息
String message = "hello, ttl queue";
// 消息ID,需要封装到CorrelationData中
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
// 发送消息
rabbitTemplate.convertAndSend("ttl.direct", "ttl", message, correlationData);
// 记录日志
log.debug("发送消息成功");
}
注意 :
- 队列过期和消息过期同时存在 , 会以时间短的时间为准
- RabbitMQ队列消息过期的机制是判断队列头部元素是否过期 , 如果队里头部消息没有到过期时间 , 中间消息到了过期时间, 这个消息也不会被自动剔除
什么情况下消息会成为死信 ?
当一个队列中的消息满足下列情况之一时,就会成为死信(dead letter):
- 消费者使用basic.reject或 basic.nack声明消费失败,并且消息的requeue参数设置为false
- 消息是一个过期消息,超时无人消费
- 要投递的队列消息满了,无法投递
什么是死信交换机 ? 如何为队列绑定死信交换机 ?
死 信交换机和正常的交换机没有什么不同 , 如果一个包含死信的队列配置了dead-letter-exchange
属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机称为死信交换机
为队列绑定死信交换机 , 只需要设置队列属性 dead-letter-exchange
即可
RabbitMQ的高可用机制有了解过嘛 ?
RabbitMQ的是基于Erlang语言编写,而Erlang又是一个面向并发的语言,天然支持集群模式。RabbitMQ的集群有两种模式:
普通集群:是一种分布式集群,将队列分散到集群的各个节点,从而提高整个集群的并发能力。
- 会在集群的各个节点间共享部分数据,包括:交换机、队列元信息。不包含队列中的消息。
- 当访问集群某节点时,如果队列不在该节点,会从数据所在节点传递到当前节点并返回
- 队列所在节点宕机,队列中的消息就会丢失
镜像集群:是一种主从集群,普通集群的基础上,添加了主从备份功能,提高集群的数据可用性。
- 交换机、队列、队列中的消息会在各个MQ的镜像节点之间同步备份。
- 所有操作都是主节点完成,然后同步给镜像节点
- 主宕机后,镜像节点会替代成新的主