MQ术语
Broker: 消息队列服务器实体
Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列
Queue: 消息队列载体,每个消息都会被投入到一个或多个队列
Binding: 绑定,它的作用就是把exchange和queue按照路由规则绑定起来
Routing Key: 路由关键字,exchange根据这个关键字进行消息投递
VHost: vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的 queue、exchange 和binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。
Producer: 消息生产者,就是投递消息的程序
Consumer: 消息消费者,就是接受消息的程序
Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务
使用MQ可以做什么?
解耦:系统间通过消息通信,不用关心其它系统的处理。
例如:系统A执行完业务,系统B需要得到系统A的执行后的结果,此时可以在系统A中调用系统B,系统A耦合了系统B的业务。若此时系统C、D都有类似的需求,那就得又更改系统A中的代码,违反设计模式中的开闭原则。此时,就可以利用MQ来解耦,如下图所示:
异步:相比传统的串行、并行方式,提高了系统吞吐量。
例如:一个业务在执行过程中,需要调用其它服务,业务链过长,同步调用时耗时就会很长,如下图所示:
若我们在系统B执行完后,利用MQ通知系统C和系统D去完成,直接返回结果给用户,就可以减少业务耗时。这就是异步调用,如下图所示:
流量削锋:可以通过消息队列长度控制请求量;可以缓存短时间内的高并发请求。减少高峰时期对服务器压力。
延迟队列:当我们在淘宝购物下单但未成功支付时,若用户超时30分钟未支付,订单就会被取消。这些就需要发送延迟消息。当发送消息30分钟后,消费者勇者收到消息,这就是延迟队列。
日志处理:解决大量日志传输。
消息通讯:消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等
使用MQ要解决的问题
如何实现高可用?
将队列及其中的数据同步到镜像节点中,当队列所在节点故障时,镜像队列可以继续提供服务。MQ数据可持久化,当节点恢复时,可以恢复数据。
如何保证RabbitMQ的消息可靠性,防止消息丢失?
-
网络故障导致丢失消息
- 发送者确认模式
-
MQ宕机导致丢失消息
- 消息持久化:MQ会将消息持久化磁盘,宕机重启可以恢复消息。
- 镜像集群:主从备份。
-
消费者丢失消息
- 消费者确认机制:在处理消息结束后,手动Acknowledge回执给MQ;
- MQ未接受到Acknowledge会认为消费失败,消息会保留在MQ;
如何防止MQ消息的重复消费?
原因:网络故障导致生产者确认机制失败,MQ重新投递消息;
解决思路:保证处理消息接口的幂等性(幂等性指的是多次操作,结果是一致的)。
例如:你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下是否已经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确性。
解决消息幂等性::
- 唯一索引:保证插入的数据只存在一条;
- token:每次接口请求前先获取一个token,在下次请求时,在请求头中加上这个token,后台进行校验,检验通过删除token,下次请求再次判断token;
- 先查询后判断:首先查询数据库是否存在数据,若存在则表示数据已请求,直接拒绝该请求,若不存在,说明是第一次请求,直接放行。
如果保证消息的有序性?
假如生产者产生了 2 条消息:M1、M2,假定 M1 发送到 S1,M2 发送到 S2,如果要保证 M1 先于 M2 被消费,怎么做?
解决方法:保证生产者—MQ服务—消费者三者是一对一对一的关系。
缺陷:
(1)并行度不够(吞吐量不够)
(2)更多的异常处理,比如:只要消费端出现问题,就会导致整个处理流程阻塞,我们不得不花费更多的精力来解决阻塞的问题。
如何解决消息堆积问题?
可以给单个消费者接收消息后放入队列,交给线程池去处理。
MQ的工作模式
简单模式:一个生产者,一个消费者;
生产者将消息放入消息队列。消费者监听消息队列,若队列中有消息,就消费掉,消息被拿走后自动从队列中删除。
work模式:一个生产者,多个消费者;每个消费者获取到的消息唯一。
生产者将消息放入消息队列(消费者可以有多个)。C1、C2同时监听同一个队列,竞争消息资源,谁先拿到消息谁就负责消费。
订阅模式:一个生产者发送的消息会被多个消费者获取;
- 每个消费者监听自己的队列;
- 生产者将消息发给broker,由交换机将消息转发给绑定此交换机的每一个队列,每个绑定交换机的队列都将接收到消息。
路由模式:发送消息到交换机并且要指定路由key,消费者指定路由key将队列绑定到交换机上;
生产者将消息发送给交换机按照路由判断当前产生的消息携带的路由字符,交换机根据路由key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息
topic模式:将路由key和某模式进行匹配,此时队列需要绑定在一个模式上;
- *号,#号代表通配符;
- *号代表多个单词;#号代表一个单词;
- 路由功能添加模糊匹配;
- 生产者产生消息,将消息交给交换机;
- 交换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收并消费消息;
Rabbitmq有哪几种交换机?
- direct交换机
- fanout交换机
- topic交换机
消息基于什么传输?
信道。
如何确保消息正确地发送至RabbitMQ? 如何确保消息接收方消费了消息?
发送方确认模式
将信道设置成 confirm 模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的 ID。一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一 ID)。
若MQ发生内部错误从而导致消息丢失,会发送一条未确认(nack)消息。
发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。
接收方确认机制
消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,MQ 才能安全地把消息从队列中删除。
如何利用MQ实现延迟队列?
RabbitMQ中有一个死信队列设定:我们可以给一个队列设置过期时间,或者发送消息时给消息设置过期时间。过期的消息称为死信,队列会把死信转发给提前设置的死信交换机,而与死信交换机绑定的队列就可以拿到这些消息。
因为发送消息超过一定时间(过期)后,才会被队列拿到,这样就实现了延迟队列效果。
实现起来非常简单,不过也有一些缺陷:
- 如果延迟消息过多,可能导致MQ的消息堆积过多。
- MQ消息无法删除,因此不能撤销延迟消息。
如果对上述问题有要求,可以利用Redis来实现延迟队列。
参考文章: