手把手带你了解消息中间件(3)——RabbitMQ

RabbitMQ官网: http://www.rabbitmq.com/
Erlang官网:https://www.erlang.org
友情提示:RabbitMQ基于Erlang语言开发的,要想使用RabbitMQ的话,须要先安装Erlang环境

1.RabbitMQ是什么

  RabbitMQ是消息代理:它接受并转发消息。能够将它视为邮局:当你投递邮件到一个邮箱,邮递员终究会将邮件递交给你的收件人。以此类推,RabbitMQ能够是一个邮箱,一个邮局和一个邮递员。
  RabbitMQ与邮局之间的主要区别在于,它不处理纸张,而是接收,存储和转发数据消息的二进制数据。java

2.首先先了解几个概念

  2.1 生产者(producer)与消费者(consumer)
  生产者与消费者是RabbitMQ通讯过程当中的两个重要的角色,至关于邮件的发送方与接收方,而RabbitMQ充当的角色就是传递消息的第三方,也就是说它是不能产生数据的。在实际应用中,生产者和消费者也是能够角色互相转换的,因此当咱们应用程序链接到 RabbitMQ 服务器时,必需要明确我是生产者仍是消费者。缓存

  2.2 链接(Connections)
  生产者生产消息以后,发布到RabbitMQ以前,须要先链接RabbitMQ服务器吧,RabbitMQ支持的全部协议都是基于TCP的,而且为了提升效率而采用长期链接(每一个协议操做不会打开新链接)。一个客户端库链接使用单个TCP链接。服务器

  2.3 通道(Channels)
  创建完链接以后,客户端会基于该TCP链接的基础之上,开辟一条AMQP通道,客户端执行的每一个协议操做都在通道上发生,通道存在于链接中,而不是单独存在的。关闭链接后,其上的全部通道也将关闭。
  咱们也能够在一条链接中开辟一条或多条AMQP通道,每条通道都会有一个惟一的ID来确保通道之间互不干预
网络

  2.4 队列(Queues)
  队列是RabbitMQ服务器中消息的终点,相似于一个邮箱,能够缓存消息;生产者向其中投递消息,消费者从其中取出消息。负载均衡

  2.5 交换机(Exchanges)
  交换机用于接收生产者发送的消息,而且能处理消息,例如将消息递交给某个队列、递交给全部队列、或者将消息丢弃,它主要应用的有三种类型:
 a、Fanout:广播(扇形),将消息交给全部绑定到交换机的队列
 b、Direct:定向(直连),把消息交给符合指定routing_key 的队列
 c、Topic:通配符(主题),把消息交给符合routing pattern(路由模式) 的队列3d

补充:交换机只负责转发消息,不具有存储消息的能力,若是没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!

3.RabbitMQ的消息模式

  2.1 simple简单模式

  生产者与消费者为一对一的关系,一条消息只能被一个消费者消费,若是此时没有消费者,那么消息会被暂时缓存在queue中,直至被消费代理

  2.2 work工做模式

  在work模型下,生产者与消费者为一对多的关系,多个消费者能够绑定到一个队列,共同消费队列中的消息code

补充:消息一旦被消费者接收,队列中的消息就会被删除,全部不会存在消息重复消费的问题,即每条消息只能被多个消费者中的其中一个消费者消费
  问题1:在多个消费者的状况下,消息如何分配

  queue能够实现负载均衡,生产者将消息放入queue中后,RabbitMQ会经过轮询的方式,实现消息的分配blog

  问题2:RabbitMQ怎么知道消息被接收了呢

  经过消息确认机制(Acknowlege)实现。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。不过这种回执ACK分两种状况:rabbitmq

  • 自动ACK:消息一旦被接收,消费者自动发送ACK
  • 手动ACK:消息接收后,不会发送ACK,须要手动调用

    问题3:如何使用自动和手动ACK?

      这就要取决于消息的重要性,若是消息不过重要,丢失也没有影响,那么自动ACK会比较方便,反之,若是消息很是重要,不容丢失。那么最好在消费完成后手动ACK,不然接收消息后就自动ACK,RabbitMQ就会把消息从队列中删除,若是此时消费者宕机,那么消息就丢失了。

  2.3 订阅模型-Fanout

  X表示交换机;在广播模式下,消息发送流程是这样的:

  • 1) 能够有多个消费者
  • 2) 每一个消费者有本身的queue(队列)
  • 3) 每一个队列都要绑定到Exchange(交换机)
  • 4) 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪一个队列,生产者没法决定。
  • 5) 交换机把消息发送给绑定过的全部队列
  • 6) 队列的消费者都能拿到消息。实现一条消息被多个消费者消费

  2.4 订阅模型-Direct
在Direct模型下:

  • 1) 队列与交换机的绑定,不能是任意绑定了,而是要指定一个routing_key(路由key)
  • 2) 消息的发送方在 向 Exchange发送消息时,也必须指定消息的 routing_key
  • 3) Exchange再也不把消息交给每个绑定的队列,而是根据消息的routing_key进行判断,只有队列的routing_key与消息的 routing_key彻底一致,才会接收到消息

    图解:
  • P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。
  • X:Exchange(交换机),接收生产者的消息,而后把消息递交给 与routing_key彻底匹配的队列
  • C1:消费者,其所在队列指定了须要routing_key 为 error 的消息
  • C2:消费者,其所在队列指定了须要routing_key 为 info、error、warning 的消息

  2.5 订阅模型-Topic
  Topic类型的Exchange与Direct相比,都是能够根据routing_key把消息路由到不一样的队列。只不过Topic类型Exchange可让队列在绑定routing_key的时候使用通配符!通配符规则:
  #:能够替代零个或多个单词。
  *:能够代替一个单词

解释:routing_key 通常都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert


图解:

  • C1:消费者,其所在队列Q1绑定了*.orange.*的routing_key,它能匹配到以orange为中心,先后各一个单词的路由,例如:item.orange.insert,可是该item.orange.insert.one路由就不能被匹配
  • C2:消费者,其所在队列Q2绑定了lazy.#的routing_key,它能匹配到任何以lazy.#开头的路由

    在上面交换机中,提到了消息丢失的问题,那么哪一种状况下消息会丢失,如何处理
  • 1) 生产者弄丢了数据。生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,好比:由于网络问题。
    解决方案:使用发送方确认机制。发送方确认机制是指生产者将信道设置成confirm(确认)模式,一旦信道进入confirm模式,全部在该信道上面发布的消息都会被指派一个惟一的ID(从1开始),一旦消息被投递到RabbitMQ服务器以后,RabbitMQ就会发送一个确认(Basic.Ack)给生产者(包含消息的惟一ID),这就使得生产者知晓消息已经正确到达了目的地了。
  • 2) RabbitMQ 弄丢了数据。好比:MQ宕机了
    解决方案:RabbitMQ 的消息默认存放在内存上面,若是不特别声明设置,消息不会持久化保存到硬盘上面的,若是MQ宕机,消息就会丢失。
    咱们能够对消息进行持久化处理,要将消息持久化,前提是:队列、Exchange都持久化
//获取链接
Connection connection = ConnectionUtil.getConnection();
//创建通道
Channel channel = connection.createChannel();
//交换机持久化 ,第三个参数表明是否持久化
channel.exchangeDeclare(EXCHANGE_NAME,"EXCHANGE_TYPE",true);
//队列持久化 ,第二个参数表明是否持久化
channel.queueDeclare(QUEUE_NAME,true,false,false,null);
//消息持久化 ,第三个参数表明是否持久化
channel.basicPublish(EXCHANGE_NAME,routing_key,MessageProperties.PERSISTENT_TEXT_PLAIN,MESSAGE_TEXT.getBYtes());
  • 3) 消费端弄丢了数据。刚接收到消息,还没处理,结果进程挂了,好比:重启了。 解决方案:使用消费者的ACK机制。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。若是这个消费者挂掉没有发送应答,RabbitMQ会理解为这个消息没有被处理,而后交给另外一个消费者去从新处理。这样,你就能够确认即便消费者偶尔挂掉也不会丢失任何消息了。
相关文章
相关标签/搜索