- 什么是消息队列?
- MQ全称为Message Queue,消息队列是应用程序之间的通信方法。
- 为什么使用消息队列?
-
在项目中,无需即时返回的且耗时的操作,进行异步处理从而提高系统的吞吐量;可以实现程序之间的解耦合。
1、任务异步处理
将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时
间。
2、应用程序解耦合
MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。
- 常见产品有哪些?
- 实现方式:AMQP,JMS
- 常见产品:activeMQ,zeroMQ,RabbitMQ,RocketMQ,kafka
- rabbitmq 有哪些重要的角色?
- 生产者:消息的创建者,负责创建和推送数据到消息服务器;
- 消费者:消息的接收方,用于处理数据和确认消息;
- 代理:就是 RabbitMQ 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
- rabbitmq 有哪些重要的组件?
-
ConnectionFactory(连接管理器):(可以设置用户,ip,端口,密码.)应用程序与Rabbit之间建立连接的管理器,程序代码中使用。
-
Connection: (连接):可以通过连接管理器获得,Producer和Consumer都是通过TCP连接到RabbitMQ Server的。以后我们可以看到,程序的起始处就是建立这个TCP连接。
-
Channel(信道):消息推送使用的通道。可以声明队列,传输信息
-
Exchange(交换器):用于接受、分配消息。
-
Queue(队列):用于存储生产者的消息。信道声明队列
-
RoutingKey(路由键):用于把生成者的数据分配到交换器上。
-
BindingKey(绑定键):用于把交换器的消息绑定到队列上。
5.1 流程
生产者发送消息,通过信道传输信息到交换器,在到队列,消费者接受消息
- RabbitMQ的5种模式特征**
-
不直接Exchange交换机(默认交换机)
- simple简单模式:一个生产者生产一个消息到一个队列被一个消费者接收
- work工作队列模式:生产者发送消息到一个队列中,然后可以被多个消费者监听该队列;一个消息只能被一个消费者接收,消费者之间是竞争关系(默认平局分配的原则,轮训方案)
-
使用Exchange交换机;订阅模式(交换机:广播fanout、定向direct、通配符topic)
- 发布与订阅模式:使用了fanout广播类型的交换机,可以将一个消息发送到所有绑定了该交换机的队列
- 路由模式:使用了direct定向类型的交换机,消费会携带路由key,交换机根据消息的路由key与队列的路由key进行对比,一致的话那么该队列可以接收到消息
- 通配符模式:使用了topic通配符类型的交换机,消费会携带路由key(*一个, #一个或者多个),交换机根据消息的路由key与队列的路由key进行对比,匹配的话那么该队列可以接收到消息
-
rabbitmq消息丢失有几种情况,该如何解决?
消息丢失有三个方面可能出现数据丢失:
-
第一个是消费者,默认情况下,ack是自动开启的,当mq将消息发送给消费者,消费者会自动发送ack去相应mq,mq一旦收到ack,就会删除数据。如果此时消费者端宕机或者出现异常,这个消息就没有成功消费,有就是丢失了。
处理:打开手动ack,此时只有当消息成功执行完毕后,才会手动向mq返回ack,如果mq没有收到ack,此时mq会将这条消息自动发送给其他消费者执行。springboot —> 可以自动重试
-
第二个是mq服务器,当mq服务器宕机时,数据会丢失。
处理:通过mq的持久化将数据进行持久化保存,.信道声明队列的时候即channel.deliverMode设置成2,持久化队列,然后持久化消息
一般有交换器,队列,消息持久化
-
第三个是生产者,可能由于网络波动,产生的数据并没有成功发送到mq中。
处理:
方案一:使用mq的事务机制
channel.txSelect(); //开启事务 channel.txCommit(); //提交事务 channel.txRollback(); //回滚事务
方案二:confirm mq的确认机制,返回一个成功或者失败信息,类似ack
同步:
异步:
-
-
怎样避免mq出现消息重复消费?
保证消息不被重复消费的关键是保证消息队列的幂等性
- 方案一:拿到这个消息做数据库的insert操作,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
- 方案二:拿到这个消息做redis的set的操作,你无论set几次结果都是一样的,set操作本来就算幂等操作。
- 方案三:准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。
- 为什么要设置成联合主键的形式呢? 因为消息在发送时,可能会因为失败而重复发送,
而重复发送可能造成回滚数据的重复。订单编号+sku编号组合起来如果相同一定是同一
笔数据 ,所以我们将其设置成联合主键,这样在插入重复数据时就会因为主键唯一冲突
而无法插入。
-
rabbitmq如何实现高可用?
RabbitMQ一共具有三种模式:单机、普通集群、镜像集群
- 镜像集群模式(三大模式之一):每次生产者写消息到Queue时,都会把消息同步到多个Queue中,即使一台机器宕机了,对系统也没有影响。
-
rabbitmq 中 vhost 的作用是什么?
- vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。
- rabbitmq 的消息是怎么发送的?
- 首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 tcp 连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和 RabbitMQ 就创建了一条 amqp 信道(channel),信道是创建在“真实” tcp 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。
- 要保证消息持久化成功的条件有哪些?
-
声明队列必须设置持久化 durable 设置为 true.
-
消息推送投递模式必须设置持久化,deliveryMode 设置为 2(持久)。
-
消息已经到达持久化交换器
-
消息已经到达持久化队列
以上四个条件都满足才能保证消息持久化成功。
- rabbitmq 持久化有什么缺点?
- 持久化的缺地就是降低了服务器的吞吐量,因为使用的是磁盘而非内存存储,从而降低了吞吐量。可尽量使用 ssd 硬盘来缓解吞吐量的问题。
- rabbitmq 怎么实现延迟消息队列?
- 通过消息过期后进入死信交换器,再由交换器转发到延迟消费队列,实现延迟功能;
- 用 RabbitMQ-delayed-message-exchange 插件实现延迟功能。
- rabbitmq 集群有什么用?
- 高可用:某个服务器出现问题,整个 RabbitMQ 还可以继续使用;
- 高容量:集群可以承载更多的消息量。
- rabbitmq 节点的类型有哪些?
- 磁盘节点:消息会存储到磁盘
- 内存节点:消息都存储在内存中,重启服务器消息丢失,性能高于磁盘类型。
- rabbitmq 集群搭建需要注意哪些问题?
- 各节点之间使用“–link”连接,此属性不能忽略。
- 各节点使用的 erlang cookie 值必须相同,此值相当于“秘钥”的功能,用于各节点的认证。
- 整个集群中必须包含一个磁盘节点。
-
rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?
不是,原因有以下两个:
- 存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据;
- 性能的考虑:如果每条消息都需要完整拷贝到每一个集群节点,那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟。
-
rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?
如果唯一磁盘的磁盘节点崩溃了,不能进行以下操作:
-
不能创建队列
-
不能创建交换器
-
不能创建绑定
-
不能添加用户
-
不能更改权限
-
不能添加和删除集群节点
唯一磁盘节点崩溃了,集群是可以保持运行的,但你不能更改任何东西。
- rabbitmq 对集群节点停止顺序有要求吗?
- RabbitMQ 对集群的停止的顺序是有要求的,应该先关闭内存节点,最后再关闭磁盘节点。如果顺序恰好相反的话,可能会造成消息的丢失。
- rabbitmq两种分配数据的模式:
-
轮询的方式(默认)
-
能者多劳(开启手动ack)
22.rabbit的竞争方式
1 默认RabbitMQ采用的是(轮询)平均分配原则,(通过设置线程休眠的方式,设置睡的时间不同),
2 修改分配原则,能者多劳的模式(应用场景:效率高的消费者消费消息多,可以用来进行负载均衡.)
ack:消息自动应答,ack=true,消息接受到自动回复mq消息收到了,mq就会删除消息,ack=false,**手动ack,
2.1 ,消费者告诉rabbitMQ每次住推送一条消息,不在按照以前的方式来分配(通过信道的api),告诉rabbit收到ack消息后,才推送下一条数据 channel.basicQos(1)
2.2设置手动channel.basicConsume(false)
2.3 消费者收到消息后,回复ack,告诉rabbit 你消费了那条消息ack, channel.basicAck(false)