RabbitMQ系列
“ RabbitMQ是现在常用的消息中间件,消息中间件是利用可靠的消息传递机制进行系统和系统直接的通讯;以及通过提供消息传递和消息的排队机制,它可以在分布式系统环境下扩展进程间的通讯。其他常见的消息中间件还有有ActiveMQ、Kafka、RocketMQ等。”
目录
前言
MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出,只不过队列中存放的内容是message 而已,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ 是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。使用了 MQ 之后,消息发送上游只需要依赖 MQ,不用依赖其他服务。
一、RabbitMQ的应用场景
一、流量削峰
举个例子,如果订单系统最多能处理一万次订单,这个处理能力应付正常时段的下单时绰绰有余,正常时段我们下单一秒后就能返回结果。但是在高峰期,如果有两万次下单操作系统是处理不了的,只能限制订单超过一万后不允许用户下单。使用消息队列做缓冲,我们可以取消这个限制,把一秒内下的订单分散成一段时间来处理,这时有些用户可能在下单十几秒后才能收到下单成功的操作,但是比不能下单的体验要好。
二、应用解耦
以电商应用为例,应用中有订单系统、库存系统、物流系统、支付系统。用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障,都会造成下单操作异常。当转变成基于消息队列的方式后,系统间调用的问题会减少很多,比如物流系统因为发生故障,需要几分钟来修复。在这几分钟的时间里,物流系统要处理的内存被缓存在消息队列中,用户的下单操作可以正常完成。当物流系统恢复后,继续处理订单信息即可,中单用户感受不到物流系统的故障,提升系统的可用性。
三、异步处理
有些服务间调用是异步的,例如 A 调用 B,B 需要花费很长时间执行,但是 A 需要知道 B 什么时候可以执行完,以前一般有两种方式,A 过一段时间去调用 B 的查询 api 查询。或者 A 提供一个 callback api, B 执行完之后调用 api 通知 A 服务。这两种方式都不是很优雅,使用消息总线,可以很方便解决这个问题,A 调用 B 服务后,只需要监听 B 处理完成的消息,当 B 处理完成后,会发送一条消息给 MQ,MQ 会将此消息转发给 A 服务。这样 A 服务既不用循环调用 B 的查询 api,也不用提供 callback api。同样 B 服务也不用做这些操作。A 服务还能及时地得到异步处理成功的消息。
二、RabbitMQ核心内容
一、RabbitMQ四大核心概念
1、生产者:产生数据发送信息的程序是生产者
2、交换机:接受生产者发送的数据,然后传递到队列中
3、消息缓冲区,消费者从队列中获取数据
4、消费者:从队列中拿数据并进行操作
二、RabbitMQ的工作模式
2.1 simple简单模式
步骤:
1. 消息产生者p将消息放入队列
2. 消息的消费者(consumer) 监听(while) 消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除(隐患 消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失)应用场景:聊天(中间有一个过渡的服务器;p端,c端)
单向传输,类似于A把消息放到队列B里面,C从队列B里面取出然后操作。
2.2 work工作模式(竞争)
步骤:
消息生产者P将消息放入队列,此时有两个或者多个消费者(图中为c1,c2)同时监听队列,c1和c2共同争抢消息队列中的内容,谁先拿到归谁
应用场景:抢红包
2.3 发布订阅模式(共享资源)
X代表交换机rabbitMQ内部组件,erlang 消息产生者是代码完成,代码的执行效率不高,消息产生者将消息放入交换机,交换机发布订阅把消息发送到所有消息队列中,对应消息队列的消费者拿到消息进行消费
应用场景:邮件群发,群聊天,广播(广告)
2.4 路由模式
消息生产者将消息发送给交换机按照路由判断,路由是字符串(info) 当前产生的消息携带路由字符(对象的方法),交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息;
根据业务功能定义路由字符串
从系统的代码逻辑中获取对应的功能字符串,将消息任务扔到对应的队列中业务场景:error 通知;EXCEPTION;错误通知的功能;传统意义的错误通知;客户通知;利用key路由,可以将程序中的错误封装成消息传入到消息队列中,开发者可以自定义消费者,实时接收错误;
2.5 主题模式(路由模式的一种)
三、RabbitMQ核心内容
1.消息应答机制:
保证消息不丢失,确保消费者已经完成消息接收并产生应答后才删除消息
1.1自动应答:
消息发送成功后就认为已经传送成功,mq会把消息删除。不安全,可能会造成消息丢失。
1.2手动应答:
可以批量处理,可以一次性对信道中所有的消息进行应答,但是不建议这样做,因为造成数据丢失的可能性更大。
2.消息重新入队:
若消费者由于某些原因失去连接(信道关闭,TCP连接丢失等等),导致消息未发送ACK确认,RabbitMQ将了解到消息未完全处理,并且对其进行重新排队,如果有其他消费者能够处理,会分发给其他消费者,这样可以保证信息不会丢失。
3.发布确认
生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被投递到所有匹配的队列之后,broker就会发送一个确认给生产者(包含消息的唯一 ID),这就使得生产者知道消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息会在将消息写入磁盘之后发出,broker 回传
给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号,此外 broker 也可以设置basic.ack 的 multiple 域,表示到这个序列号之前的所有消息都已经得到了处理。
confirm 模式最大的好处在于他是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调方法来处理该确认消息,如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消息,生产者应用程序同样可以在回调方法中处理该 nack 消息。
4.交换机
RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上,通常生产者甚至都不知道这些消息传递传递到了哪些队列中。相反,生产者只能将消息发送到交换机(exchange),交换机工作的内容非常简单,一方面它接收来自生产者的消息,另一方面将它们推入队列。交换机必须确切知道如何处理收到的消息。是应该把这些消息放到特定队列还是说把它们放到许多队列中还是说应该丢弃它们。这就得由交换机的类型来决定。
4.1交换机的类型
直接(direct), 主题(topic) ,标题(headers) , 扇出(fanout)
关于交换机的类型,我们首先要了解一个概念“绑定”
4.1.1绑定
什么是 绑定呢,绑定其实是 交换机和队列之间的桥梁,它告诉我们交换机和那个队列进行了绑定关系。比如说下面这张图告诉我们的就是 X 与 Q1 和 Q2 进行了绑定。
4.1.2 fanout交换机
fanout交换机会把消息传播给所有与它绑定的队列。
4.1.3Direct交换机
此为定向传播交换机,首先我们要了解一个概念,交换机与队列的绑定关系是通过一个“routingKey”来进行的,也就是说,交换机可以通过不同的
routingKey来绑定不同的队列,前面提到的fanout交换机为什么没有声明routingKey呢,因为fanout是广播交换机,如果声明了routingKey,他就无法达到广播(发送给全部与交换机绑定的队列)的作用了。
所有在Direct交换机中,如图,x交换机会把消息键为orange的消息发送给Q1,会把消息键为black
4.1.4Tpoic交换机
此为主题交换机发送到类型是 topic 交换机的消息的 routing_key 不能随意写,必须满足一定的要求,它必须是一个单词列表,以点号分隔开。这些单词可以是任意单词
比如说:"stock.usd.nyse","nyse.vmw","quick.orange.rabbit".这种类型的。当然这个单词列表最多不能超过 255 个字节。
在这个规则列表中,其中有两个替换符是大家需要注意的
*(星号)可以代替一个单词
#(井号)可以替代零个或多个单词
当队列绑定关系是下列这种情况时需要引起注意
当一个队列绑定键是#,那么这个队列将接收所有数据,就有点像 fanout 了。
如果队列绑定键当中没有#和*出现,那么该队列绑定类型就是 direct 了。
是不是发现到了这里,上面所说的交换机类型很像第二章节提到的RabbitMQ的工作模式?嘿嘿
emm...确实是这样的,我们上面所说的RabbitMQ的几种工作模式,其实都是根据交换机与队列之间不同的绑定模式来进行的。但是,概念性的东西讲了这么多,我们再用一整章图来全部回顾一下RabbitMQ的工作流程吧
总结
例如我们用一个实际业务流程来举例,左边的小人,就是我们网络商城的用户,他下订单,订单的业务会进入到交换机中,下订单的过程中我们需要完成不同的服务,要提供微信支付,要发邮件,发信息通知用户下订单的结果等等。于是交换机通过不同的routingKey与队列1234分别进行绑定,这样就能实现业务的解耦,不同的业务发送到不同的队列中,然后异步进行,恩。。大体流程就是这样。
好了~今天记录的初始RabbitMQ的过程就到这里了,下一次更新RabbitMQ的高级部分,死信队列,延迟队列以及RabbitMQ集群的部署,下次见