RabbitMQ 整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。可以把消息传递的过程想象成:当你讲一个包裹送到邮局,邮局会暂存并最终将邮件通过邮递员送到收件人的手上,RabbitMQ 就好比由邮局、邮箱和邮递员组成的一个系统。从计算机术语层面来说,RabbitMQ 模型更像是一种交换机模型。

RabbitMQ 的整体模型架构如下图:

生产者和消费者

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.Consume 或者 Basic.Get),经过可能的解包处理得到原始的数据,之后再进行业务处理逻辑。这个业务处理逻辑并不一定需要和接收消息的逻辑使用同一个线程。消费者线程可以使用一个线程去接收消息,存入到内存中,比如使用 Java 中的 BlockingQueue。业务处理逻辑使用另一个线程从内存中读取数据,这样可以将应用进一步解耦,提高整个应用的处理效率。

队列

Queue:队列,是 RabbitMQ 的内部对象,用于存储消息。

  RabbitMQ 中消息只能存储在队列中,这一点和 Kafka 这种消息队列中间件相反。Kafka 将消息存储在 topic(主题)这个逻辑层面,而相对应的队列处理逻辑只是 topic 实际存储文件中的位移标识。RabbitMQ 的生产者生产消息并最终投递到队列中,消费者可以从队列中获取消息并消费。

  多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(Round-Robin,即轮询)给多个消费者进行处理,而不是每个消费者都接收到所有的消息并处理。

  RabbitMQ 不支持队列层面的广播消费,如果需要广播消费,需要在其上进行二次开发,处理逻辑会变得异常复杂,同时也不建议这么做。

交换器、路由键、绑定

Exchange:交换器。在图 2-4 中我们暂时可以理解成生产者将消息投递到队列中,实际上这个在 RabbitMQ 中不会发生。真实情况是,生产者将消息发送到 Exchange(交换器,通常也可以用大写的 "X" 来表示),由交换器将消息路由到一个或者多个队列中。如果路由不到,或许会返回给生产者,或许直接丢弃。这里可以将 RabbitMQ 中的交换器看作一个简单的实体。

  RabbitMQ 中的交换器有四种类型,不同的类型有着不同的路由策略。

RoutingKey:路由键。生产者将消息发给交换器的时候,一般会指定一个 RoutingKey,用来指定这个消息的路由规则,而这个 RoutingKey 需要与交换器类型和 绑定键(BindingKey)联合起来才能最终生效。

  在交换器类型和绑定键(BindingKey)固定的情况下,生产者可以在发送消息给交换器时,通过指定 RoutingKey 来决定消息流向哪里。

Binding:绑定。RabbitMQ 中通过绑定将 交换器与队列关联起来,在绑定的时候一般会指定一个绑定键(BindingKey),这样 RabbitMQ 就指定如何正确地将消息路由到队列了。

  生产者将消息发送给交换器,需要一个 RoutingKey,当 BindingKey 和 RoutingKey 相匹配时,消息会被路由到对应的队列中。在绑定多个队列到同一个交换器的时候,这些绑定允许使用相同的 BindingKey。BindingKey 并不少在所有的情况下都生效,它依赖于交换器类型,比如 fanout 类型的交换器就会无视 BindingKey,而是将消息路由到所有绑定到该交换器的队列中。

  沿用本文开头的比喻,交换器相当于投递包裹的邮箱,RoutingKey 相当于写在包裹上的地址,BindingKey 相当于包裹的目的地,当填写在包裹上的地址和实际想要投递的地址相匹配时,那么这个包裹就会被正确投递到目的地,最后这个目的地的 "主人" -- 队列可以保留这个包裹。如果填写的地址出错,邮递员不能正确投递到目的地,包裹可能会回退给寄件人,也有可能被丢弃。

  在某些情形下,RoutingKey 与 BindingKey 可以看作同一个东西。

channel.exchangeDeclare(EXCHANGE_NAME, "direct", true, false, null);
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);
String message = "Hello World!";
channel.basicPublic(EXCHANGE_NAME, ROUTING_KEY,MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes());

  以上代码声明了一个 direct 类型的交换器,然后将交换器和队列绑定起来。注意这里使用的字样是 "ROUTING_KEY",在本该使用 BindingKey 的 channel.queueBind 方法中却和 channel.basicPublish 方法同样使用了 RoutingKey,这样做的潜台词是:这里的 RoutingKey 和 BindingKey 是同一个东西。在 direct 交换器类型下,RoutingKey 和 BindingKey 需要完全匹配才能使用,所以上面的代码中采用了这种写法会显得方便许多。

  但是在 topic 交换器类型下,RoutingKey 和 BindingKey 之间需要做模糊匹配,两者并不是相同的。

  BingingKey 其实也属于路由键的一种,官方解释为:the routing key to use for the binding。可以翻译为:在绑定的时候使用的路由键。大多数时候,包括官方文档和 RabbitMQ Java API 中都把 BindingKey 和 RoutingKey 看作 RoutingKey,为了避免混淆,可以这么理解:

  在绑定的时候,其中需要的路由键是 BindingKey。涉及的客户端方法如: channel.exchangeBind、channel.queueBind,对应 AMQP 命令为 Exchange.Bind、Queue.Bind。

  发送消息的时候,其中需要的路由键是 RoutingKey。涉及的客户端方法如 channel.basicPublish,对应的 AMQP 命令为 Basic.Publish。

  由于某些历史的原因,包括现存能搜集到的资料显示:大多数情况下习惯性地将 BindingKey 写成 RoutingKey,尤其是在使用 direct 类型的交换器的时候。

交换器类型

RabbitMQ 常用的交换器类型有 fanout、direct、topic、headers 这四种。AMQP 协议里还提到另外两种类型:System 和自定义。

fanout

它会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中。

direct

direct 类型的交换器路由规则也很简单,它会把消息路由到那些 BindingKey 和 RoutingKey 完全匹配的队列中。

以下图为例,交换器的类型为 direct,如果我们发送一条消息,并在发送消息的时候设置路由键为 "warning",消息会路由到 Queue1 和 Queue2,对应的示例代码如下:

channel.basicPublish(EXCHANGE_NAME, "warning",MessageProperties.PERSISTENT_TEXT_PLAIN,message.getbytes());

  如果 在发送消息的时候设置路由键为 "info" 或者 "debug",消息只会路由到 Queue2。如果以其他的路由键发送消息,则消息不会路由到这两个队列中。

topic

前面讲到 direct 类型的交换器路由规则是完全匹配 BindingKey 和 RoutingKey,但是这种严格的匹配方式在很多情况下不能满足实际业务的需求。topic 类型的交换器在匹配规则上进行了扩展,它与 direct 类型的交换器相似,也是将消息路由到 BindingKey 和 RoutingKey 相匹配的队列中,但这里的匹配规则有些不同,它约定:

  RoutingKey 为一个点号 "." 分隔的字符串(被点号 "." 分隔开的每一段独立的字符串成为一个单词),如 "com.rabbitmq.client"、"java.util.concurrent"、"com.hidden.client";

  BindingKey 和 RoutingKey 一样也是点号 ".' 分隔的字符串

  BindingKey 中可以存在两种特殊字符串 "*" 和 "#",用于做模糊匹配,其中 "*" 用于匹配一个单词, "#" 用于匹配多个单词(可以是零个)

  以图 2-8 中的配置为例:

  路由键为 "com.rabbitmq.client" 的消息会同时路由到 Queue1 和 Queue2;

  路由键为 "com.hidden.client" 的消息只会路由到 Queue2;

  路由键为 "com.hidden.demo" 的消息只会路由到 Queue2;

  路由键为 "java.util.concurrent" 的消息将会被丢弃或者返回给生产者(需要设置 mandatory 参数),因为它没有匹配任何路由键。

headers

headers 类型的交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的 headers 属性进行匹配。在绑定队列和交换器时指定一组键值对,当发送消息到交换器时,RabbitMQ 会获取到该消息的 headers(也是一个键值对的形式),对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对,如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers 类型的交换器性能会很差,而且也不实用,基本上不会看到它的存在。

转载于:https://www.cnblogs.com/eleven24/p/10327288.html

RabbitMQ 相关概念相关推荐

  1. RabbitMQ 相关概念和方法详解

    名词解释 ConnectionFactory: 与 RabbitMQ 服务器连接的管理器. Connection: 与 RabbitMQ 服务器的连接. Channel: 与 Exchange 的连接 ...

  2. RabbitMQ入门到精通

    RabbitMQ 1. 消息中间件概述 1.1. 为什么学习消息队列 电子商务应用中,经常需要对庞大的海量数据进行监控,随着网络技术和软件开发技术的不断提高,在实战开发中MQ的使用与日俱增,特别是Ra ...

  3. Python三方库:RabbitMQ基本使用

    Python有多种插件都支持RabbitMQ,本文介绍的是RabbitMQ推荐的Pika插件.使用pip直接安装即可 pip install pika . 一.RabbitMQ简介 1. MQ简介 M ...

  4. 学习 RabbitMQ 这一篇就够了

    文章目录 一.MQ相关概念 二.RabbitMQ相关概念 三.安装 四.HelloWorld 五.工作队列 5.1.轮询分发消息 5.2.消息应答 5.2.1.自动应答 5.2.2.手动应答 5.2. ...

  5. 一、Rabbit的介绍与安装

    文章目录 MQ概述 RabbitMQ介绍 RabbitMQ的安装 MQ概述 MQ全称 Message Queue(消息队列),是在消息的传输过程中保存消息的容器.多用于分布式系统之间进行通信. 传统的 ...

  6. RabbitMQ的相关概念以及RabbitMQ的安装

    文章目录 RabbitMQ的相关概念以及RabbitMQ的安装 MQ的相关概念 什么是MQ 为什么要使用MQ MQ的分类 MQ的选择 RabbitMQ RabbitMQ的概念 四大核心概念 Rabbi ...

  7. 面试官:RabbitMQ本身不支持延迟队列,那你给我实现一个?

    以下文章来源方志朋的博客,回复"666"获面试宝典 RabbitMQ本身没有延迟队列的支持,但是基于其本身的一些特性,可以做到类似延迟队列的效果:基于死信交换器+TTL. 以下介绍 ...

  8. RabbitMQ 七战 Kafka,差异立现!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 译者丨王欢,Golang后端工程师,DockOne社区译者 ...

  9. 选型必看:RabbitMQ 七战 Kafka,差异立现

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作为一个有丰富经验的微服务系统架构师,经常有人问我,"应 ...

最新文章

  1. 【Ubuntu入门到精通系列讲解】系统信息相关命令
  2. MY Fist 51CTO BLOG
  3. linux的ip地址是127,Linux的IP地址显示127.0.0.1的解决办法
  4. oracle索引对模糊查询,Oracle索引
  5. winform在表格中输入一个完整的时间字段_【ArcGIS小技巧视频教程】(5):在ArcGIS中挂接其他数据...
  6. js获取自定义属性的值
  7. AcWing 1054. 股票买卖
  8. bootstrap-table 树形结构
  9. 大数据技术原理与应用学习笔记(九)
  10. 六大手机软件商店分析报告
  11. 61家第三方收单机构、247家商业银行、70家农商银行及119家村镇银行收单机构号
  12. qt 富文本 html,Qt富文本编辑器QTextDocument
  13. Captain Flint and Crew Recruitment
  14. 2020李宏毅学习笔记——14.Convolution Netural Network
  15. 华为云服务器双十一报价表出炉最新内容
  16. 【RK按键】按键切换
  17. 前端编程中,如何消除浏览器缓存
  18. 一千零一夜的观后感(一)
  19. 中国软件缺少核心技术 自主创新是关健
  20. OpenCV人脸识别的原理 .

热门文章

  1. c++ 迭代器支持的运算
  2. C++类中不能定义自身类类型成员变量
  3. redis 源码阅读
  4. 8086CPU常见汇编指令、debug命令
  5. ROS初学笔记 - C++11与PCL库冲突问题
  6. c++-内存管理-debug_allocator
  7. 总有个短信发来一行乱码_个别收到的短信乱码,有什么办法还原么。。。
  8. 如何用二进制表示一个小数
  9. java 调 cmd 没反应
  10. 【题解】luogu P1757 通天之分组背包