RabbitMQ高性能原因Erlang语言特性AMQP协议整体模型架构生产者和消费者队列交换器、路由键、绑定ExchangeDirect ExchangeFanout Exchange
Topic Exchange
RoutingKey
RabbitMQ高性能原因
Erlang语言特性
由爱立信公司开发(Ericsson Language) 一门为交换机软件开发诞生的编程语言
- 通用的面向并发的编程语言,适用于分布式系统
- 解释型语言,基于Erlang虚拟机解释运行,跨平台部署
- 进程间上下文切换效率远高于C语言
- 有着和原生Socket一样的延迟,网络IO效率高
AMQP协议
AMQP协议作为RabbitMQ的规范,直接决定了RabbitMQ的内部结构和外部行为
- Broker:接收和分发消息的应用,RabbitMQ就是 Message Broker实例
- Virtual Host:虚拟Broker,将多个单元隔离开
- Connection:publisher,consumer和broker之间的TCP连接
- Channel:connection内部建立的逻辑连接,通常每个线程创建单独的channel
- Routing key:路由键,用来指示消息的路由转发,相当于快递的地址
- Exchange:交换机,相当于快递的分拨中心
- Queue:消息队列,消息最终被送到这里等待consumer取走
- Binding:exchange和queue之间的虚拟连接,用于message的分发依据
整体模型架构
生产者和消费者
- Producer
生产者,就是投递消息的一方。
生产者创建消息,然后发布到 RabbitMQ 中 。消息一般可以包含 2 个部分:消息体和标签 (Label)。
消息体也可以称之为 payload,在实际应用中,消息体一般是一个带有业务逻辑结构 的数据,比如一个 JSON 字符串。当然可以进一步对这个消息体进行序列化操作。
消息的标签
用来表述这条消息,比如 一个交换器的名称和一个路由键。生产者把消息交由 RabbitMQ,RabbitMQ 之后会根据标签把消息发送给感兴趣的消费者( Consumer)。
- Consumer
消费者,就是接收消息的 一方。消费者连接到 RabbitMQ 服务器,并订阅到队列上。当消费者消费一条消息时,只是消费 消息的消息体( payload)。在消息路由的过程中,消息的标签会丢弃,存入到队列中的消息只 有消息体,消费者也只会消费到消息体,也就不知道消息的生产者是谁
- Broker
消息中间件的服务节点 。对于 RabbitMQ 来说, 一个 RabbitMQ Broker 可以简单地看作 一个 RabbitMQ 服务节点, 或者 RabbitMQ服务实例。大多数情况下也可以将一个 RabbitMQ Broker看作一台 RabbitMQ 服务器 。
下图展示了生产者将消息存入 RabbitMQ Broker,以及消费者从 Broker 中消费数据的整 个流程。
首先生产者将业务方数据进行可能的包装,之后封装成消息,发送 ( AMQP 协议里这个动 作对应的命令为 Basic.Publish ) 到 Broker 中。
消费者订阅并接收消息 ( AMQP 协议里这个动作对应的命令为 Basic .Consurne或者 Basic.Get),经过可能的解包处理得到原始的数据, 之后再进行业务处理逻辑。这个业务处理逻辑并不一定需要和接收消息的逻辑使用同一个线程。
消费者进程可以使用一个线程去接收消息,存入到内存中,比如使用 Java 中的 BlockingQueue。 业务处理逻辑使用另 一个线程从内存中读取数据,这样可以将应用进一步解耦,提高整个应用 的处理效率。
队列
Queue:队列,是 RabbitMQ 的内部对象,用于存储消息
RabbitMQ 中消息都只能存储在队列中,这一点和 Kafka这种消息中间件相反。
Kafka将消息存储在 topic (主题)这个逻辑层面,而相对应的队列逻辑只是 topic 实际存储文件中的位移 标识。
RabbitMQ 的生产者生产消息井最终投递到队列中,消费者可以从队列中获取消息并消费 。
多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊( Round-Robin,即轮询) 给多个消费者进行处理,而不是每个消费者都收到所有的消息井处理,如下图所示。
RabbitMQ 不支持队列层面的广播消费,如果需要广播消费,需要在其上进行二次开发,处理逻辑会变得异常复杂,同时也不建议这么做。
交换器、路由键、绑定
Exchange
Exchange的功能是根据绑定关系和路由键为消息提供路由,将消息转发至相应的队列
Exchange有4种类型:Direct / Topic / Fanout / Headers,其中Headers使用很少,以前三种为主
Direct Exchange
Message中的Routing Key如果和Binding Key一致, Direct Exchange则将message发到对应的queue中
Fanout Exchange
每个发到Fanout Exchange的message都会分发到所有绑定的queue上去
Topic Exchange
根据Routing Key及通配规则,Topic Exchange将消息分发到目标Queue中
- 全匹配:与direct类似
- Binding Key中的#: 匹配任意个数的word
- Binding Key中的*: 匹配任意1个word
案例
咖啡,奶茶,果汁三种饮料
- 无咖啡因,热饮,甜味
- 有咖啡因,冷饮,苦味
- 无咖啡因,冷饮,甜味
RoutingKey
路由键。生产者将消息发给交换器的时候, 一般会指定一个 RoutingKey,用来指定这个消息的路由规则,而这个 Routing Key 需要与交换器类型和绑定键( BindingKey)联合使用才能最终生效。
在交换器类型和绑定键( BindingKey)固定的情况下,生产者可以在发送消息给交换器时, 通过指定 RoutingKey来决定消息流向哪里。
Binding:绑定 。 RabbitMQ 中通过绑定将交换器与队列关联起来,在绑定的时候 一般会指定一个绑定键(BindingKey),这样RabbitMQ就知道如何正确地将消息路由到队列了。
生产者将消息发送给交换器时, 需要一个 RoutingKey, 当 BindingKey和 RoutingKey相匹 配时,消息会被路由到对应的队列中。
在绑定多个队列到同一个交换器的时候, 这些绑定允许使用相同的 BindingKey. BindingKey 并不是在所有的情况下都生效,它依赖于交换器类型,比如fanout类型的交换器就会无视 BindingKey,而是将消息路由 到所有绑定到该交换器的队列中
交换器相当于投递包裹的邮箱, RoutingKey相当于填写在包裹上的 地址, BindingKey相当于包裹的目的地, 当填写在包裹上的地址和实际想要投递的地址相匹配时,那么这个包裹就会被正确投递到目的地, 最后这个目的地的“主人”一一队列可以保留这个包裹。
如果填写的地址出错,邮递员不能正确投递到目的地, 包裹可能会回退给寄件人,也有可能被丢弃。