【RabbitMQ】基本特性、工作模型、路由方式

1.基本特性

官网:https://www.rabbitmq.com/getstarted.html

  • 高可靠:RabbitMQ 提供了多种多样的特性让你在可靠性和性能之间做出权衡,包括持久化、发送应答、发布确认以及高可用性
  • 灵活的路由:通过交换机(Exchange)实现消息的灵活路由。
  • 支持多客户端:对主流开发语言(Python、Java、Ruby、PHP、C#、JavaScript、Go、Elixir、Objective-C、Swift等)都有客户端实现。
  • 集群与扩展性:多个节点组成一个逻辑的服务器,支持负载。
  • 高可用队列:通过镜像队列实现队列中数据的复制。
  • 权限管理:通过用户与虚拟机实现权限管理。
  • 插件系统:支持各种丰富的插件扩展,同时也支持自定义插件。
  • 与Spring集成:Spring对AMQP进行了封装

2.AMQP协议

AMQP:高级消息队列协议,是一个工作于应用层的协议,最新的版本是1.0版本

【RabbitMQ】基本特性、工作模型、路由方式

  • 除了 RabbitMQ 之外,AMQP 的实现还有 OpenAMQ、Apache Qpid、Redhat Enterprise MRG、AMQP Infrastructure、ØMQ、Zyre
  • 除了 AMQP 之外,RabbitMQ 支持多种协议,STOMP、MQTT、HTTP and WebSockets。

可以使用WireShark等工具对RabbitMQ通信的AMQP协议进行抓包。

3.工作模型

由于RabbitMQ实现了AMQP协议,所以RabbitMQ的工作模型也是基于AMQP的。理解这张图片至关重要。

【RabbitMQ】基本特性、工作模型、路由方式

3.1 Broker

我们要使用RabbitMQ来收发消息,必须要安装一个RabbitMQ的服务,可以安装在Windows上面也可以安装在 Linux上面,默认是 5672 的端口。这台 RabbitMQ的服务器我们把它叫做Broker,中文翻译是代理/中介,因为MQ 服务器帮助我们做的事情就是存储、转发消息。

3.2 Connection

无论是生产者发送消息,还是消费者接收消息,都必须要跟Broker之间建立一个连接,这个连接是一个TCP的长连接。

3.3 Channel

如果所有的生产者发送消息和消费者接收消息,都直接创建和释放TCP长连接的话,对于Broker来说肯定会造成很大的性能损耗,因为TCP连接是非常宝贵的资源,创建和释放也要消耗时间。

所以在AMQP 里面引入了 Channel 的概念,它是一个虚拟的连接。把它翻译成通道,或者消息信道。这样我们就可以在保持的 TCP 长连接里面去创建和释放Channel,大大了减少了资源消耗。

另外一个需要注意的是,Channel是RabbitMQ原生API里面的最重要的编程接口,也就是说我们定义交换机、队列、绑定关系,发送消息消费消息,调用的都是Channel接口上的方法。而不是直接操作TCP连接

参考连接

3.4 Queue

现在我们已经连到 Broker 了,可以收发消息了。在其他一些 MQ 里面,比如ActiveMQ和Kafka,我们的消息都是发送到队列上的。

队列是真正用来存储消息的,是一个独立运行的进程,有自己的数据库(Mnesia)。

消费者从队列中获取消息时,有两种消费模式

  1. Push模式:只要生产者发到服务器,就马上推送给消费者。

    • 推模式接收消息是最有效的一种消息处理方式
      • channel.basicConsume(queneName,consumer)方法将信道(channel)设置成投递模式,直到取消队列订阅为止
      • 在投递模式期间,当消息到达RabbitMQ时,RabbitMQ会自动地、不断地投递消息给匹配的消费者,而不需要消费端手动来拉取,当然投递消息的个数还是会受到channel.basicQos的限制。
    • 推模式将消息提前推送给消费者,消费者必须设置一个缓冲区缓存这些消息
      • 优点是消费者总是有一堆在内存中待处理的消息,所以当真正去消费消息时效率很高。
      • 缺点就是缓冲区可能会溢出
    • 由于推模式是信息到达RabbitMQ后,就会立即被投递给匹配的消费者,所以实时性非常好,消费者能及时得到最新的消息。
  2. Pull模式:消息存放在服务端,只有消费者主动获取才能拿到消息。

    • 适用于从队列中获取单条消息而不是持续订阅
      • 使用channel.basicGet(queueName,autoAck)方法来进行消费消息
      • 消费者需要写一个 while 循环不断从队列获取消息吗?不需要,可以基于事件机制,实现消费者对队列监听
    • 拉模式在消费者需要时才去消息中间件拉取消息,这段网络开销会明显增加消息延迟,降低系统吞吐量
    • 由于拉模式需要消费者手动去RabbitMQ中拉取消息,所以实时性较差;消费者难以获取实时消息,具体什么时候能拿到新消息完全取决于消费者什么时候去拉取消息。

无论是push还是poll,由于队列有FIFO的特性,只有确定前一条消息被消费者接收之后,才会把这条消息从数据库删除,继续投递下条消息(一条一条推/拉的)。

RabbitMQ同时支持这两种模式,具体表现在Consumer获取消息时采用不同的方法。要想实现高吞吐量,消费者需要使用推模式,RabbitMQ一般也使用push模式

3.5 Exchange

在 RabbitMQ 里面永远不会出现消息直接发送到队列的情况。因为在AMQP 里面引入了交换机(Exchange)的概念,用来实现消息的灵活路由。(注:Exchange是作用于生产者发送消息到broker时,决定消息进入哪个Queue存储的)

交换机是一个绑定列表,用来查找匹配的绑定关系。

  • 队列使用绑定键(Binding Key)跟交换机建立绑定关系。
  • 生产者发送的消息需要携带路由键(RoutingKey),交换机收到消息时会根据它保存的绑定列表,决定将消息路由到哪些与它绑定的队列上。

注意:交换机与队列、队列与消费者都是多对多的关系。即一个队列可以绑定多个交换机,一个队列同样的可以被多个消费者消费

3.6 Vhost

我们每个需要实现基于RabbitMQ的异步通信的系统,都需要在服务器上创建自己要用到的交换机、队列和它们的绑定关系。如果某个业务系统不想跟别人混用一个系统,怎么办?

  • 再采购一台硬件服务器单独安装一个RabbitMQ服务?这种方式成本太高了。

  • 在同一个硬件服务器上安装多个RabbitMQ的服务呢?比如再运行一个5673的端口?没有必要,因为RabbitMQ提供了虚拟主机VHOST。

    VHOST 除了可以提高硬件资源的利用率之外,还可以实现资源的隔离和权限的控制。它的作用类似于编程语言中的 namespace 和 package,不同的 VHOST中可以有同名的Exchange和Queue,它们是完全透明的。

这个时候,我们可以为不同的业务系统创建不同的用户(User),然后给这些用户分配VHOST的权限。比如给风控系统的用户分配风控系统的VHOST的权限,这个用户可以访问里面的交换机和队列。给超级管理员分配所有VHOST的权限。

4.路由方式

前面说到RabbitMQ引入Exchange是为了实现消息的灵活路由,到底有哪些路由方式?

4.1 直连(Direct)

队列与直连类型的交换机绑定,需指定一个精确的绑定键。
生产者发送消息时会携带一个路由键。只有当路由键与其中的某个绑定键完全匹配时,这条消息才会从交换机路由到满足路由关系的此队列上。

【RabbitMQ】基本特性、工作模型、路由方式

4.2 主题(Topic)

队列与主题类型的交换机绑定时,可以在绑定键中使用通配符。两个通配符:

  • #:0个或者多个单词
  • *:不多不少一个单词 (注:单词指的是用英文的点“.”隔开的字符,例如abc.def是两个单词)

【RabbitMQ】基本特性、工作模型、路由方式

  • 第一个队列支持路由键以junior 开头的消息路由,后面可以有单词,也可以没有。
  • 第二个队列支持路由键以netty开头,并且后面是一个单词的消息路由。
  • 第三个队列支持路由键以jvm结尾,并且前面是一个单词的消息路由。

4.3 广播(Fanouot)

广播类型的交换机与队列绑定时,不需要指定绑定键。因此生产者发送消息到广播类型的交换机上,也不需要携带路由键。消息达到交换机时,所有与之绑定了的队列,都会收到相同的消息的副本。

【RabbitMQ】基本特性、工作模型、路由方式