2
  1. 本文主要整理一下与 RabbitMQ 相关的一些基本概念,了解这些概念,是使用好 RabbitMQ 的基础;
  2. 并使用直连型交换机做了一个demo,通过实例去更好的理解这些概念。文末提供有 demo 下载地址。
  3. 可以访问 这里 查看更多关于RabbitMQ的原创文章。

AMQP

  • 即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计;
  • AMQP 的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
  • RabbitMQ 是一个开源的 AMQP 实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、PHP等。

备注:想对 AMQP 有深入了解可以点这里查看。

RabbitMQ 的一些基本概念

通过下面这张图片,我们可以对 RabbitMQ 的模型有一个大概的了解:
rabbitMQ模型.png

基本概念

需要了解的基本概念主要如下:

  • Producer:消息生产者。
  • Consumer:消息消费者。
  • Connection(连接):Producer 和 Consumer 通过 TCP 连接到 RabbitMQ Server。
  • Channel(信道):基于 Connection 创建,数据流动都是在 Channel 中进行。
  • Broker(消息代理):实际上就是消息服务器实体。
  • Vhost(虚拟主机) : 虚拟主机,一个消息代理(Broker)里可以开设多个虚拟主机(Vhost),用作不同用户的权限分离。
  • Exchange(交换机) : 用来发送消息的 AMQP 实体,它指定消息按什么路由规则,路由到哪个队列。
  • Queue(消息队列) :是 RabbitMQ 的内部对象,用于存储消息。每个消息都会被投入到一个或多个队列。且多个消费者可以订阅同一个 Queue(这时 Queue 中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理)。
  • Binding(绑定) : 它的作用就是把交换机(Exchange)和队列(Queue)按照路由规则绑定起来。
  • Routing Key(路由键) :消息发送给 Exchange(交换机)时,消息将拥有一个路由键(默认为空), Exchange(交换机)根据这个路由键将消息发送到匹配的队列中。
  • Binding Key(绑定键):指定当前 Exchange(交换机)下,什么样的 Routing Key(路由键)会被下派到当前绑定的 Queue 中。

交换机的三种类型

  • Direct:完全匹配,消息路由到那些 Routing Key 与 Binding Key 完全匹配的 Queue 中。比如 Routing Key 为 test1,则只会转发转发 test1,不会转发 test2。
  • Topic:模式匹配,Exchange 会把消息发送到一个或者多个满足通配符规则的 routing-key 的 Queue。

    • *号表示匹配一个 word(比如:满足 a.*.c的 routing-key 有 a.test1.c);
    • #号匹配多个 word 和路径,路径之间通过 . 隔开(比如:满足 a.#.c的 routing-key 有 a.test1.test2.c);
  • Fanout:忽略匹配,把所有发送到该 Exchange 的消息路由到所有与它绑定的 Queue 中。

通过实例去深入理解

一下子看到这么多概念,可能会有点发懵。下面我们通过一个很简单的 demo 去深入理解一下这些概念。
panda.jpeg

SpringBoot 工程添加 RabbitMQ

pom文件添加依赖

创建一个 SpringBoot 项目,在 pom.xml文件里添加依赖:

  <!--mq-->
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-amqp</artifactId>
  </dependency>
设置 RabbitMQ 配置参数
# rabbitmq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

创建 RabbitMQ 配置类

添加一个 RabbitMQ 的配置类 RabbitConfig:

/**
 * RabbitMQ配置
 * @公众号 全栈在路上
 * @GitHub https://github.com/liuyongfei1
 * @author lyf
 * @date 2020-05-17 17:20
 */
Configuration
public class RabbitConfig {

    /**
     * 声明队列
     * 参数说明:
     * durable 是否持久化,默认是false(持久化队列则数据会被存储在磁盘上,当消息代理重启时数据不会丢失;暂存队列只对当前连接有效)
     * exclusive 默认是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
     * autoDelete 默认是false,是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
     * 一般设置一下队列的持久化就好,其余两个就是默认false
     * @return Queue
     */
    @Bean
    Queue myQueue() {
        return new Queue(QueueConstants.QUEUE_NAME, true);
    }

    /**
     * 设置交换机,类型为 direct
     * @return DirectExchange
     */
    @Bean
    DirectExchange myExchange() {
        return new DirectExchange(QueueConstants.QUEUE_EXCHANGE_NAME, true, false);
    }

    /**
     * 绑定:将交换机和队列绑定,并设置路由匹配键
     * @return Binding
     */
    @Bean
    Binding queueBinding() {
        return BindingBuilder.bind(myQueue()).to(myExchange()).with(QueueConstants.QUEUE_ROUTING_KEY_NAME);
    }
}

消息生产端

/**
 * 消息生产端
 * @公众号 全栈在路上
 * @GitHub https://github.com/liuyongfei1
 * @author lyf
 * @date 2020-05-17 18:30
 */
@RestController
public class ProducerController {

    /**
     * RabbitTemplate提供了发送/接收消息的方法
     */
    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 发送消息(交换机类型为 Direct)
     * @return
     */
    @GetMapping("/sendDirectMessage")
    public String sendDirectMessage() {
        // 生成消息的唯一id
        String msgId = UUID.randomUUID().toString();
        String messageData = "hello,this is rabbitmq demo message";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        // 定义要发送的消息对象
        Map<String,Object> messageObj = new HashMap<>();
        messageObj.put("msgId",msgId);
        messageObj.put("messageData",messageData);
        messageObj.put("createTime",createTime);

        rabbitTemplate.convertAndSend(QueueConstants.QUEUE_EXCHANGE_NAME,QueueConstants.QUEUE_ROUTING_KEY_NAME, messageObj);
        return "message send ok";
    }

}

生产消息

代码保存后,启动服务,使用 Postman 请求生产消息接口:
postman.png

查看 RabbitMQ 管理界面

​Postman 请求成功,我们打开 RabbitMQ 的管理界面:
rabbitmq-web1.png

可以看到有一个名为 demo1_queue 的队列,说明我们测试的消息已经推送到RabbitMq 服务器上面了:

  • Total 为 1

    • 因为使用Postman就请求了接口一次,因此生产端总共就生产了 1 条消息。
  • Ready 为 1

    • 因为我们还没有启动消费端,所以没有被消费的消息数量为 1。

消息消费端

/**
 * 消息消费端
 * @公众号 全栈在路上
 * @GitHub https://github.com/liuyongfei1
 * @author lyf
 * @date 2020-05-17 18:00
 */
@Component
@RabbitListener(queues = {QueueConstants.QUEUE_NAME})
public class ConsumerController {

    @RabbitHandler
    public void handler(Map message) throws IOException {
        System.out.println("收到消息:" + message.toString());
    }
}

保存代码,重启服务,在 Idea 的终端窗口,可以看到之前生产的那条消息已经被被消费了:
consumer-console.png
由于当前队列就 1 条消息 且已经被成功消费掉了,再次访问 RabbitMQ 管理界面,会发现 Ready 和 Total 已经更新为 0。

demo下载地址

  1. 文中 demo 代码下载请点击这里
  2. 欢迎大家关注扫描二维码或 添加微信公众号:全栈在路上
  3. 微信公众号二维码.jpg

liuyongfei
445 声望30 粉丝

可以访问 这里 查看更多关于大数据平台建设的原创文章。