RabbitMQ

RabbitMQ简介

RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

1. RabbitMQ系统架构


如上图,红色线包围的有Exchange和Queue,在服务端,称作Broker,是由RabbitMQ实现的。剩下的则是客户端,有Prodcer和Consumer两种类型。

2. RabbitMQ核心概念

RabbitMQ两大核心组件是Exchange和Queue。

Queue 

Queue是一个不重复,唯一,名字随机的的缓冲区,应用程序在其权限之内可以自由地创建、共享使用和消费消息队列。 

(在RabbitMQ中,队列的名字是系统随机创建的,且当Consumer与Queue断开连接的时候,Queue会被自动删除,在下一次连接时又会自动创建。) 


上图,两个队列的名字分别是“amqp.gen-RQ6…” 和 “amqp.gen-AsB..”,是随机产生的。

Exchange 

Exchange称作交换器,它接收消息和路由消息,然后将消息发送给消息队列。每个交换器都有独一无二的名字。

Routing Key 

生产者在将消息发送给Exchange的时候,一般会指定一个routing key,来指定这个消息的路由规则。 

而这个routing key需要与Exchange Type及binding key联合使用才能最终生效。 

在Exchange Type与binding key固定的情况下,生产者就可以在发送消息给Exchange时,通过指定routing key来决定消息流向哪里。 

RabbitMQ为routing key设定的长度限制为255 bytes。

Binding 和 Binding Key 

每个Exchange都和一个特定的Queue绑定(可以是多对多的关系)。绑定的同时会指定一个binding key。 

每个发送给Exchange的消息一般都有一个routing key参数;当队列与Exchange绑定的binding key与该消息的routing key参数相同的时候,该消息才会被Exchange发给特定的队列。 

(这就好比我们上火车,我们就是消息,而手中的火车票就是routing key 。进站的时候,我们需要找到火车列次(binding key)与我们手中火车票信息匹配的车次才可以进站,即routing key = binding key才可以。)

Exchange Type 

AMQP定义了4种不同类型的Exchange,每一种都有不同的路由算法。当消息发送到Exchange时,Exchange 会对比消息的routing key /参数 和 与其绑定的队列的binding key。如果对比结果满足相应的算法,那么消息将会路由到队列上;否则,将不会被路由到队列上。

4种标准的AMQP Exchange 如下所示: 

(1)Direct(直接式交换器):

如果消息的routing key 与 binding key 直接匹配,消息会被路由到该队列上(可以用此构建点对点传输模型)。 

如图: 

生产者P发送消息到交换器X。 

如果消息的routing key 是 “orange”,则会被路由到队列Q1; 

如果消息的routing key 是 “black” 或 “green”,则会被路由到队列Q2。

当然,也可以实现多路绑定,即一个Exchange 和多个Queue绑定时可以有同样的 binding key。 

上图中,当一个消息的routing key 是 “black”,则会被同时路由到队列Q1和队列Q2。

(2)Topic(主题式交换器):

如果消息的routing key 与 binding key 符合通配符匹配的话,消息会路由到该队列上。

匹配规则:

binding key与routing key都是用句点号“. ”分隔的字符串: 

如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”;

支持通配符:其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个) 。

上图中:

routingKey=”quick.orange.rabbit”的消息会同时路由到Q1与Q2;

routingKey=”lazy.orange.fox”的消息会路由到Q1和Q2;

routingKey=”lazy.brown.fox”的消息会路由到Q2;

routingKey=”lazy.pink.rabbit”的消息会路由到Q2(只会投递给Q2一次,虽然这个routingKey与Q2的两个bindingKey都匹配);

routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit”的消息将会被丢弃,因为它们没有匹配任何bindingKey。

(3)Fanout(广播式交换器):

不管消息的routing key是什么,消息都会被路由到所有与该交换器绑定的队列中。 


上图中,生产者(P)发送到Exchange(X)的所有消息都会路由到图中的两个Queue,并最终被两个消费者(C1与C2)消费。

(4)Headers

headers类型的Exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。

在绑定Queue与Exchange时指定一组键值对;当消息发送到Exchange时,RabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue。

常见面试题

1、什么是RabbitMQ?为什么使用RabbitMQ?

答:RabbitMQ是一款开源的,Erlang编写的,基于AMQP协议的,消息中间件;

可以用它来:解耦、异步、削峰。


2、RabbitMQ有什么优缺点?

答:优点:解耦、异步、削峰;

缺点:降低了系统的稳定性:本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低;

增加了系统的复杂性:加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大。


3、如何保证RabbitMQ的高可用?

答:没有哪个项目会只用一搭建一台RabbitMQ服务器提供服务,风险太大;


4、如何保证RabbitMQ不被重复消费?

答:先说为什么会重复消费:正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除;

但是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。

针对以上问题,一个解决思路是:保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;保证消息等幂性;

比如:在写入消息队列的数据做唯一标示,消费消息时,根据唯一标识判断是否消费过;


5、如何保证RabbitMQ消息的可靠传输?

答:消息不可靠的情况可能是消息丢失,劫持等原因;

丢失又分为:生产者丢失消息、消息列表丢失消息、消费者丢失消息;


生产者丢失消息:从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息;

transaction机制就是说:发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()),如果发送成功则提交事务(channel.txCommit())。然而,这种方式有个缺点:吞吐量下降;

confirm模式用的居多:一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后;

rabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了;

如果rabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。


消息队列丢数据:消息持久化。

处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。

这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。

这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

那么如何持久化呢?

这里顺便说一下吧,其实也很容易,就下面两步

将queue的持久化标识durable设置为true,则代表是一个持久的队列

发送消息的时候将deliveryMode=2

这样设置以后,即使rabbitMQ挂了,重启后也能恢复数据


消费者丢失消息:消费者丢数据一般是因为采用了自动确认消息模式,改为手动确认消息即可!

消费者在收到消息之后,处理消息之前,会自动回复RabbitMQ已收到消息;

如果这时处理消息失败,就会丢失该消息;

解决方案:处理消息成功后,手动回复确认消息。


6、如何保证RabbitMQ消息的顺序性?

答:单线程消费保证消息的顺序性;对消息进行编号,消费者处理消息是根据编号处理消息;

你可能感兴趣的:(RabbitMQ)