MQ使用场景
MQ的使用场景大概包括解耦,提高峰值处理能力,送达和排序保证,缓冲等。
MQ概述
消息队列技术是分布式应用间交换信息的一种技术。
消息队列可驻留在内存或磁盘上,队列存储消息直到它们被应用程序读走。
通过消息队列,应用程序可独立地执行——它们不需要知道彼此的位置、或在继续执行前不需要等待接收程序接收此消息。
MQ主要作用是接受和转发消息。你可以想想在生活中的一种场景:当你把信件的投进邮筒,邮递员肯定最终会将信件送给收件人。我们可以把MQ比作邮局和邮递员。
MQ和邮局的主要区别是,它不处理消息,但是,它会接受数据、存储消息数据、转发消息。
RabbitMQ术语
虚拟主机(virtual host或vhost)
什么是虚拟主机?一组交换机、队列和绑定器被称为虚拟主机(vhost)。
为什么要用虚拟主机?RabbitMQ server 可以说就是一个消息队列服务器实体(Broker),Broker当中可以有多个用户,而用户只能在虚拟主机的粒度进行权限控制,所以RabbitMQ中需要多个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。
生产者
消息发送者,在MQ中被称为生产者(producer),一个发送消息的应用也被叫做生产者,用P表示。
消费者
生产者“生产”出消息后,最终由谁消费呢?等待接受消息的应用程序,我们称之为消费者(Consuming ),用C表示。
队列
消息只能存储在队列(queue )中。尽管消息在rabbitMQ和应用程序间流通,但是队列却是存在于RabbitMQ内部。
队列(queue)是消息载体,每个消息都会被投入到一个或多个队列。试图创建一个已经存在的队列,RabbitMQ会直接忽略这个请求。(接收消息的实体)。
一个队列不受任何限制,它可以存储你想要存储的消息量,它本质上是一个无限的缓冲区。
多个生产者可以向同一个队列发送消息,多个消费者可以尝试从同一个消息队列中接收数据。
一个队列像下面这样(上面是它的队列名称)。
注意:
生产者、消费者、中间件不必在一台机器上,实际应用中也是绝大多数不在一起的。我们可以用一张图表示RabbitMQ的构造:
交换机
把消息放进队列前,我们还需要使用另一个东西:交换机。
交换机(exchange),它指定消息按什么规则,路由到哪个队列。它可以被理解成具有路由表的路由程序。(发送消息的实体)
交换机可以存在多个,每个交换机在自己独立的进程当中执行,因此增加多个交换机就是增加多个进程,可以充分利用服务器上的CPU核以便达到更高的效率。
交换机如何判断要把消息送到哪个队列?这是我们需要路由规则,也就需要绑定器了。
绑定器
绑定器(bind)它的作用就是把exchange和queue按照路由规则绑定起来。(将交换器和队列连接起来,并且封装消息的路由信息)
路由键
每个消息都有一个称为路由关键字(routingKey)的属性,exchange根据这个关键字进行消息投递,其实就是一个简单的字符串。(绑定操作就可以理解成:将exchange将具有路由关键字 “X” 的消息投递到名为“business”的队列当中去。)
从而一个绑定就可以概括为:一个基于路由键将交换机和队列连接起来的路由规则。
需要注意:由Exchange,Queue,RoutingKey三个,才能决定一个从Exchange到Queue的唯一的线路。
重点介绍交换机
交换机定义参数type: 直接式交换机direct、topic 和 fanout。
auto_delete: 当所有绑定队列都不再使用时,是否自动删除该交换机。
交换机类型:
Fanout(发布订阅模式):不处理路由键,将消息广播给绑定到该交换机的所有队列。 不论消息的路由关键字是什么,这条消息都会被路由到所有与该交换器绑定的队列中。
广播式交换器类型的工作方式如下:
不使用任何参数将消息队列与交换器绑定在一起。
发布者(直接式交换器类型描述中的producer变成了publisher,已经隐含了二种交换器类型的区别)向交换器发送一条消息。 消息被无条件的传递到所有和这个交换器绑定的消息队列中。
Direct(路由模式):处理路由键,对消息路径进行全文匹配。消息路由键 "sunshine" 只能匹配 "sunshine" 绑定,不匹配 "sunshine.warm" 这类绑定。
通过精确匹配消息的路由关键字,将消息路由到零个或者多个队列中,绑定关键字用来将队列和交换器绑定到一起。这让我们可以构建经典的点对点队列消息传输模型,不过和任何已定义的交换器类型一样,当消息的路由关键字与多个绑定关键字匹配时,消息可能会被发送到多个队列中。
在direct模式下还可以实现多路绑定,即一个exchange和多个queue绑定时,具有同样的bindkey,如下图:
Topic(通配符模式): 处理路由键,按模式匹配路由键。模式符号 "#" 表示零个或多个单词,“*****”仅匹配一个单词。如 "wood.#" 可匹配 "wood.palm.redwood",但 "wood.*" 只匹配 "wood.deadwood"。
主题式交换器类型提供了这样的路由机制:通过消息的路由关键字和绑定关键字的模式匹配,将消息路由到被绑定的队列中。这种路由器类型可以被用来支持经典的发布/订阅消息传输模型——使用主题名字空间作为消息寻址模式,将消息传递给那些部分或者全部匹配主题模式的多个消费者。
主题交换器类型的工作方式如下:
绑定关键字用零个或多个标记构成,每一个标记之间用“.”字符分隔。绑定关键字必须用这种形式明确说明,并支持通配符:“*****”匹配一个词组,“#”零个或多个词组。
因此绑定关键字“*.dask.#”匹配路由关键字“class.dask”和“eur.dask.tab”,但是不匹配“dask.rho”。
这种交换器类型是可选的。
主题式的原理图解:
消息持久化
问题和方案描述
-
当有多个消费者同时收取消息,且每个消费者在接收消息的同时,还要处理其它的事情,且会消耗很长的时间。在此过程中可能会出现一些意外,比如消息接收到一半的时候,一个消费者死掉了
这种情况要使用消息接收确认机制,可以执行上次宕机的消费者没有完成的事情。
-
在默认情况下,我们程序创建的消息队列以及存放在队列里面的消息,都是非持久化的。当RabbitMQ死掉了或者重启了,上次创建的队列、消息都不会保存。
这种情况可以使用RabbitMQ提供的消息队列的持久化机制。
相关理论描述
RabbitMQ支持消息的持久化,也就是数据写在磁盘上,为了数据安全考虑,我个人觉得大多数开发人员都会选择持久化。
队列和交换机有一个创建时候指定的标志durable。durable的唯一含义就是具有这个标志的队列和交换机会在重启之后重新建立,它不表示说在队列当中的消息会在重启后恢复。
消息队列持久化包括3个部分:
1、exchange持久化,在声明时指定durable => true
2、queue持久化,在声明时指定durable => true
3、消息持久化,在投递时指定delivery_mode=> 2(1是非持久化)
如果exchange和queue都是持久化的,那么它们之间的binding也是持久化的。如果exchange和queue两者之间有一个持久化,一个非持久化,就不允许建立绑定。
注意:一旦创建了队列和交换机,就不能修改其标志了。例如,如果创建了一个non-durable的队列,然后想把它改变成durable的,唯一的办法就是删除这个队列然后重现创建。