1. 什么是消息中间件
消息队列中间件 (MessageQueue Middleware,简称为 MQ) 是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
它一般有两种传递模式:点对点 (P2P,Point-to-Point) 模式和发布/订阅 (Pub/Sub) 模式。点对点模式是基于队列的,消息生产者发送消息到队列,消息消费者从队列中接收消息,队列的存在使得消息的异步传输成为可能。发布订阅模式定义了如何向一个内容节点发布和订阅消息,这个内容节点称为主题 (topic),主题可以认为是消息传递的中介,消息发布者将消息发布到某个主题,而消息订阅者则从主题中订阅消息。主题使得消息的订阅者与消息的发布者互相保持独立,不需要进行接触即可保证消息的传递,发布/订阅模式在消息的一对多广播时采用。
2. 消息中间件的作用
应用解耦:
传统的商城架构中,必须是先支付,然后再减少库存,这两个操作必须是在同一事务中,即操作原子性,这样效率极其低下,如果使用RabbitMQ的话,将消息发送给各自的队列,支付和库存的操作之间没有了关联性,这样支付系统和库存系统之间就进行了解耦。
流量削峰:
使用缓冲队列的方式,在访问量急剧增大的时候,减少并发访问的压力,比较常见的场景就是秒杀和签到系统,一般来说流量的削峰有两个处理方式:上游队列缓冲,限速发送;下游队列缓冲,限速执行。
异步处理:
用户发送消息储存在RabbitMQ中,由RabbitMQ传递给消费者来进行消费,也可以通过死信队列来实现延迟队列的效果,让消息定时被消费等。
3. RabbitMQ中一些概念
broker :是指一个或多个 erlang node 的逻辑分组,且 node 上运行着 RabbitMQ 应用程序。cluster 是在 broker 的基础之上,增加了 node 之间共享元数据的约束
exchange:消息交换机,处理消息和队列之间的关系
queue:队列载体,消息投入队列中。
binding:绑定,把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange根据这个进行消息投递。
vhost:虚拟消息服务器,每个RabbitMQ服务器都能够创建虚拟消息服务器。
vhost之间相互完全隔离,不同vhost之间无法共享Exchange和Queue。
vhost主要是用来划分不同业务模块,不同业务模块之间没有信息交互。
4. RabbtiMQ交换器类型
RabbitMQ 常用的交换器类型有 fanout、 direct、 topic、 headers 这四种。
fanout:它会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中。
direct:它会把消息路由到那些BindingKey 和 RoutingKey完全匹配的队列中。
topic:它与 direct 类型的交换器相似,也是将消息路由到 BindingKey 和 RoutingKey 相匹配的队列中,但topic是模糊匹配。BindingKey 中可以存在两种特殊字符串"*"和"#",其中"*"用于匹配一个单词,"#"用于匹配多个单词(可以是零个)。BindingKey 和 RoutingKey是点号". "分隔的字符串,每个子字符串为一个单词。
headers:不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的 headers 属性进行匹配。在绑定队列和交换器时制定一组键值对,当发送消息到交换器时,RabbitMQ 会获取到该消息的 headers (也是一个键值对的形式),对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对,如果完全匹配则消息会路由到该队列,否则不会路由到该队列。 headers 类型的交换器性能会很差,而且也不实用,基本上不会看到它的存在。
5. RabbitMQ消息基于什么传输
由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ使用信道的方式来传输数据,信道是建立在真实的TCP连接上的虚拟连接,且每条TCP连接上的信道数量没有限制。
6. 如何保证RabbitMQ消息的可靠传输
生产者丢失消息: RabbitMQ提供transaction和confirm模式来确保生产者不丢消息;
transaction机制:发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()),如果发送成功则提交事务(channel.txCommit())。然而,这种方式有个缺点:吞吐量下降;
confirm模式:一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,RabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),生产者知道消息已经正确到达目的队列了。
消息队列丢数据:消息持久化。
将queue的持久化标识durable设置为true,则代表是一个持久的队列。
发送消息的时候将deliveryMode=2,消息持久化。
消费者丢失消息:手动确认消息。
处理消息成功后,手动回复确认消息。
7. 如何避免消息重复消费
要求消息体中必须要有一个bizId(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重的依据,避免同一条消息被重复消费。
8. RabbitMQ的死信队列
消息变成死信的几种情况
1. 消息被拒绝(basic.reject / basic.nack),并且requeue = false。
2. 消息TTL过期。
3. 队列达到最大长度。
死信处理过程
DLX(Dead Letter Exchange)一般的Exchange没有区别,它能在任何的队列上被指定。
1. 给队列添加一个DLX。
2. 当这个队列中有死信时,RabbitMQ自动将消息发给DLX。
3. DLX再将信息发送给与其绑定的队列(死信队列)。
9. 使用消息队列的缺点
1. 系统可用性降低:本来其他系统只要运行正常,那么系统就是正常的。现在加了消息队列进去,必须保证消息队列正常。
2. 系统复杂性增加:要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证消息可靠性。
网友评论