RabbitMQ系列(二)RabbitMQ架构原理及消息分发机制

RabbitMQ简介

RabbitMQ支持多种协议,包括:AMQP、STOMP、MQTT、HTTP、WebSockets。

RabbitMQ架构原理

Producer生产者

向RabbitMQ发送消息的一端。

Consumer消费者

从RabbitMQ接收消息的一端。

Broker中介

可以理解为安装了RabbitMQ服务的服务器。默认是5672端口。

Connection连接

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

根据消息到底是 Broker 推送给消费者的?还是消费者主动获取的?消费者消费消息可以分为两种模式。

  • Pull模式:对应的方法是 basicGet。消息存放在服务端,只有消费者主动获取才能拿到消息。消息的实时性会降低。但是好处是可以根据自己的消费能力决定获取消息的频率。

  • Push模式:对应的方法是 basicConsume。只要生产者发消息到服务器,就马上推送给消费者,消息保存在客户端。消息的实时性很高。但是如果消费不过来有可能会造成消息积压。

  • Spring AMQP 是 push 方式,通过事件机制对队列进行监听,只要有消息到达队列,就会触发消费消息的方法。

  • RabbitMQ 中 pull 和 push 都有实现。而 kafka 和 RocketMQ 只有 pull。

Channel通道

为什么要有Channel?

生产者发送消息、消费者接收消息都是在TCP的长连接上进行,如果每个生产者、消费者使用时创建TCP长连接,结束时释放TCP长连接。频繁创建和释放 TCP 长连接会对 Broker 造成很大的性能损耗,也会浪费时间。

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

所以,一个Connection中可以有多个Channel

Channel 仅存在于连接的上下文中,不能独立存在。当连接关闭时,其上的所有通道也会关闭。不同的 Channel 之间是相互隔离的,每个 Channel 都有自己的编号(通道号,是一个整数)。对于每个客户端线程来说,Channel 不共享,各自用自己的 Channel。

Channel 是 RabbitMQ 原生 API 里面的最重要的编程接口。我们定义交换机、队列、绑定关系,发送消息,消费消息,调用的都是Channel 接口上的方法。

Queue队列

队列是生产者和消费者之间的纽带。生产者发送的消息到达队列,在队列中存储。消费者从队列消费消息。

**一个消费者是可以监听多个队列的,一个队列也可以被多个消费者监听。**但是在生产环境中,**建议一个消费者只处理一个队列的消息。**如果需要提升处理消息的能力,可以增加多个消费者。这个时候消息会在多个消费者之间轮询。

Queue的可设置字段

在这里插入图片描述

Exchange交换机

概念

用于把一条消息分发给多个队列的场景。

生产者把消息发送给Exchange交换机,Exchange交换机根据规则分发消息到不同的队列中。

所以,Exchange交换机必须要和接收消息的队列建立一个绑定关系,并且为每个队列指定一个特殊的标识。

  1. Exchange 和队列是多对多的绑定关系,也就说,一个交换机的消息一个路由给多个队列,一个队列也可以接收来自多个交换机的消息。
  2. 绑定关系建立好之后,生产者发送消息到 Exchange,也会携带一个特殊的标识。当这个标识跟绑定的队列标识匹配的时候,消息就会发给一个或者多个符合规则的队列。

Exchange的可设置字段

在这里插入图片描述

Vhost虚拟机

Vhost虚拟机就是一个mini版的RabbitMQ服务器。Vhost虚拟机之于RabbitMQ就像虚拟机之于物理机。Vhost虚拟机提供如下功能:

  1. 拥有自己独立的Broker、交换机、队列、绑定关系。
  2. 不同Vhost虚拟机的队列、交换机的命名可以相同。
  3. 不同Vhost虚拟机间的资源是相互隔离的。
  4. 不同Vhost虚拟机提供的权限控制功能。可以创建专属的用户,给用户分配对应的 VHOST 的权限。
  5. RabbitMQ提供了开箱即用的默认的虚拟主机“/”,如果不需要多个vhost可以直接使用这个默认的vhost,通过使用缺省的guest用户名和guest密码来访问默认的vhost。
  6. 实际开发中,我们可以为不同的业务系统创建专属于他们自己的 VHOST,然后再为他们创建专属的用户,给用户分配对应的 VHOST 的权限。

RabbitMQ架构原理图

在这里插入图片描述

RabbitMQ基本配置

配置文件

RabbitMQ自定义配置文件名:

  • 配置文件:rabbitmq.conf

它的路径:

  • 二进制安装(源码安装)的路径:
安装目录下的/etc/rabbitmq/
  • rpm安装的路径:
/etc/rabbitmq/

rabbitmq.conf文件是可能没有的,此时,需要我们自己创建

touch /etc/rabbitmq/rabbitmq.conf

rabbitmq.conf配置文件示例

https://github.com/rabbitmq/rabbitmq-server/blob/main/deps/rabbit/docs/rabbitmq.conf.example

rabbitmq.conf配置文件的配置项说明

https://www.rabbitmq.com/configure.html#config-items

RabbitMQ端口

RabbitMQ应用了很多端口,最常用的是5672和15672

5672:AMQP客户端端口,用于应用程序访问。

15672:HTTP_API端口,管理员用户才能访问,用于管理RbbitMQ,需要启用management插件。

RabbitMQ管理界面

开启RabbitMQ管理界面

  • 启用Web管理插件
# 启用Web管理插件
rabbitmq-plugins enable rabbitmq_management
  • RabbitMQ 有一个默认的用户“ guest” ,但这个用户默认只能通过本机访问,要让其它机器可以 访问,需要创建一个新用户,为其分配权限
#添加用户,用户名/密码:admin/admin
rabbitmqctl add_user admin admin
#为用户分配权限
rabbitmqctl set_user_tags admin administrator
#为用户分配资源权限
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

RabbitMQ角色

RabbitMQ 的用户角色分类:none、management、policymaker、monitoring、administrator

none,无法登录控制台

不能访问 management plugin,通常就是普通的生产者和消费者。

management,普通管理者

  • 用户可以通过AMQP做的任何事外加:
  • 列出自己可以通过AMQP登入的virtual hosts
  • 查看自己的virtual hosts中的queues, exchanges 和 bindings
  • 查看和关闭自己的channels 和 connections
  • 查看有关自己的virtual hosts的“全局”的统计信息,包含其他用户在这些virtual hosts中的活动。

policymaker,策略制定者

  • 可以做management可以做的任何事,外加:
  • 查看、创建和删除自己的virtual hosts所属的policies和parameters

monitoring,监控者

  • 可以做management可以做的任何事,外加:
  • 列出所有virtual hosts,包括他们不能登录的virtual hosts
  • 查看其他用户的connections和channels
  • 查看节点级别的数据如clustering和memory使用情况
  • 查看所有virtual hosts的全局的统计信息
  • 查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)

administrator,超级管理员

默认的用户guest就是administrator角色

  • 可以做policymaker和monitoring可以做的任何事,外加:
  • 创建和删除virtual hosts
  • 查看、创建和删除users
  • 查看创建和删除permissions
  • 关闭其他用户的connections

RabbitMQ消息分发机制

根据前文可知,Exchange交换机有4种类型:Direct、Topic、Fanout、Headers。其中Headers一般不用。

Direct

生产者发送消息时会携带一个路由键(routing key)。当消息的路由键与某个队列的绑定键完全匹配时,这条消息才会从交换机路由到这个队列上。多个队列也可以使用相同的绑定键

类似于sql中的=精确查找。

直连类型的交换机,适用于一些业务用途明确的消息。比如 HR 系统跟销售系统之间通信,传输的是销售系统专用的消息,就可以建一个直连类型的交换机,使用明确的绑定键。

Topic

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

  • #:代表匹配0个或者多个单词
  • *:代表匹配有且只有一个单词

单词:rabbitmq中,每个单词用符号.隔开。如xuyang.com.haha,就是3个单词。

如下图,

channel.basicPublish("MY_TOPIC_EXCHANGE","junior.abc.jvm","msg 2");:只能被第1个队列接收。

channel.basicPublish("MY_TOPIC_EXCHANGE","python.java.v1","msg 2");:能被第2,3个队列接收。

在这里插入图片描述

Fanout

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

Headers

Headers类型一般不用。

RabbitMQ持久化与内存管理

持久化机制

RabbitMQ的持久化分为:交换机持久化、队列持久化、消息持久化

交换机持久化

交换机默认不是持久化的,在服务器重启之后,交换机会消失。

我们可以在创建交换机的时候,修改属性:
在这里插入图片描述

或者

channel.exchangeDeclare(EXCHANGE_NAME, "fanout", true);

队列持久化

与交换机的持久化相同,队列的持久化也是通过durable参数实现的。
在这里插入图片描述

或者用api

Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments) throws IOException;

第二个参数跟交换机方法的参数一样,true表示做持久化,当RabbitMQ服务重启时,队列依然存在。

这里说一下另外两个参数:

  • exclusive:排他队列。如果一个队列被声明为排他队列,那么这个队列只能被第一次声明他的连接所见,并在连接断开的时候自动删除。这里有三点需要说明:

    • 排他队列是基于连接可见的,同一连接的不同信道是可以同时访问同一连接创建的排他队列
    • 如果一个连接已经声明了一个排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同。
  • 即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除的,这种队列适用于一个客户端发送读取消息的应用场景

  • autoDelete:自动删除,如果该队列没有任何订阅的消费者的话,该队列会被自动删除。这种队列适用于临时队列。

消息持久化

消息的持久化是指当消息从交换机发送到队列之后,被消费者消费之前,服务器突然宕机重启,消息仍然存在。消息持久化的前提是队列持久化,假如队列不是持久化,那么消息的持久化毫无意义。

通过代码设置消息持久化

void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;

其中BasicProperties的类型如下,其中deliveryMode是设置消息持久化的参数。等于1:非持久化,等于2:持久化。

public BasicProperties(
    String contentType,
    String contentEncoding,
    Map<String,Object> headers,
    Integer deliveryMode,
    Integer priority,
    String correlationId,
    String replyTo,
    String expiration,
    String messageId,
    Date timestamp,
    String type,
    String userId,
    String appId,
    String clusterId)
  • 当 RabbitMQ 收到消息时,如果是持久化消息,则会储存在内存中,同时也会写入磁盘;如果是非持久化消息,则只会存在内存中。
  • 当内存快不够用的时候,在内存使用达到 RabbitMQ 的临界值时,内存中的部分数据会被转移到磁盘,持久化消息由于本就存在于磁盘中,不会被重复写入。所以此时,会有部分非持久化的消息写入磁盘。
  • 虽然非持久化的消息可能会被写入磁盘,但是非持久化的消息、队列、交换器在服务重启后仍然会消失。包括已经写入磁盘的非持久化的消息。

内存控制

内存控制:是RabbitMQ控制内存使用量的方法。

  • 通过内存阈值参数控制内存的使用量,当内存使用超过配置的阈值时,RabbitMQ 会阻塞客户端的连接并停止接收从客户端发来的消息,以免服务崩溃。同时,会发出内存告警,此时客户端于与服务端的心跳检测也会失效。
  • 当出现内存告警时,可以通过管理命令临时调整内存的使用量。
rabbitmqctl set_vm_memory_high_watermark <fraction>

其中,fraction 为内存阈值,默认是 0.4,表示 RabbitMQ 使用的内存超过系统内存的40%时,会产生内存告警,通过此命令修改的阈值在重启后会失效。

  • 可以通过修改配置文件的方式,使之永久生效,但是需要重启服务。
# rabbitmq.conf
vm_memory_high_watermark.relative=0.4
#vm_memory_high_watermark.absolute=1GB

配置方式

RabbitMQ 提供 relative 与 absolute 两种配置方式。

  • relative:相对值,也就是前面的 fraction 参数,建议 0.4~0.66,不能太大
rabbitmqctl set_vm_memory_high_watermark <fraction>

#rabbitmqctl set_vm_memory_high_watermark 0.4
  • absolute:绝对值,固定大小,单位为 KB、MB、GB
rabbitmqctl set_vm_memory_high_watermark absolute <value>

#rabbitmqctl set_vm_memory_high_watermark absolute 1GB

内存换页

在 RabbitMQ 达到内存阈值并阻塞生产者之前,RabbitMQ会把部分数据转移到磁盘,以释放内存空间,这就是内存换页。那么RabbitMQ 什么时候触发数据转移呢?

有个换页参数,默认为 0.5,表示当内存使用量达到内存阈值的 50%时会进行换页,也就是当内存使用量达到 0.4*0.5=0.2时,触发内存换页。

vm_memory_high_watermark_paging_ratio=0.5

磁盘控制

除了内存的使用量需要控制外,磁盘的使用量也需要控制。RabbitMQ 通过磁盘阈值参数控制磁盘的使用量,当磁盘剩余空间小于磁盘阈值时,RabbitMQ 同样会阻塞生产者,避免磁盘空间耗尽。

rabbitmqctl set_disk_free_limit <limit>
rabbitmqctl set_disk_free_limit mem_relative <fraction>
# limit 为绝对值, KB、 MB、 GB
# fraction 为相对值,建议 1.0~2.0 之间,这里的值是相对机器内存空间设置的值。1.0表示磁盘空闲空间限制设置为内存空间 * 1的大小。

配置文件

# rabbitmq.conf
disk_free_limit.relative=1.5
# disk_free_limit.absolute=50MB
  • 磁盘阈值默认 50M。
  • 但是,由于是定时检测磁盘空间,不能完全消除因磁盘耗尽而导致崩溃的可能性,比如在两次检测之间,磁盘空间从大于 50M 变为 0M。
  • 一种相对谨慎的做法是将磁盘阈值大小设置与内存相等。

RabbitMQ插件管理

RabbitMQ插件管理的相关命令

  • 查询插件列表

插件前面[ ] 为空说明,没有安装。有 e*说明插件是安装了的。

rabbitmq-plugins list
  • 安装插件
rabbitmq-plugins enable rabbitmq_management
  • 卸载插件
rabbitmq-plugins disable rabbitmq_management

通过Java API使用RabbitMQ

  • 工程名:p003001-rabbitmq-javaapi

引入依赖

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.6.0</version>
</dependency>

消费者代码

通过Java API使用RabbitMQ的方式与JDBC类似,定义消费者有以下步骤:

  1. 创建连接工厂
  2. 设置连接IP
  3. 设置监听端口
  4. 设置vhost虚拟机
  5. 设置访问的用户名和密码
  6. 创建连接对象
  7. 创建消息通道
  8. 声明交换机
  9. 声明队列
  10. 绑定交换机和队列
  11. 创建消费者对象
  12. 获取消息
public class XYConsumer {

    private final String EXCHANGE_NAME = "XY_EXCHANGE";
    private final String QUEUE_NAME = "XY_QUEUE";

    public Channel create() throws IOException, TimeoutException {
        // 1. 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 2. 设置连接IP
        factory.setHost("192.168.200.16");
        // 3. 设置监听端口
        factory.setPort(5672);
        // 4. 设置vhost虚拟机
        factory.setVirtualHost("/");
        // 5. 设置访问的用户名和密码
        factory.setUsername("admin");
        factory.setPassword("admin");
        // 6. 创建连接对象
        Connection conn = factory.newConnection();
        // 7. 创建消息通道
        Channel channel = conn.createChannel();
        // 8. 声明交换机
        // DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete, Map<String, Object> arguments)
        channel.exchangeDeclare(EXCHANGE_NAME, "direct", false, false, null);
        // 9. 声明队列
        // queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 10. 绑定交换机和队列
        // BindOk queueBind(String queue, String exchange, String routingKey)
        String routingKey = "xyRoutingKey";
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
        // 11. 创建消费者对象,并定义接收到消息的回调
        Consumer consumer = new DefaultConsumer(channel) {
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("Received message : '" + msg + "'");
                System.out.println("consumerTag : " + consumerTag);
                System.out.println("deliveryTag : " + envelope.getDeliveryTag());
            }
        };
        // 12. 获取消息
        // String queue, boolean autoAck, Consumer callback
        channel.basicConsume(QUEUE_NAME, true, consumer);
        return null;
    }

    public static void main(String[] args) throws IOException, TimeoutException {
        XYConsumer xyConsumer = new XYConsumer();
        xyConsumer.create();
    }
}

生产者代码

定义生产者有以下步骤:

  1. 创建连接工厂
  2. 设置连接IP
  3. 设置监听端口
  4. 设置vhost虚拟机
  5. 设置访问的用户名和密码
  6. 创建连接对象
  7. 创建消息通道
  8. 发送消息
package com.learn;


import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 1. 创建连接工厂
 * 2. 设置连接IP
 * 3. 设置监听端口
 * 4. 设置vhost虚拟机
 * 5. 设置访问的用户名和密码
 * 6. 创建连接对象
 * 7. 创建消息通道
 * 8. 发送消息
 */
public class XYProducer {
    private final String EXCHANGE_NAME = "XY_EXCHANGE";
    private final String QUEUE_NAME = "XY_QUEUE";
    private final String routingKey = "xyRoutingKey";

    public void create() {
        // 1. 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 2. 设置连接IP
        factory.setHost("192.168.200.16");
        // 3. 设置监听端口
        factory.setPort(5672);
        // 4. 设置vhost虚拟机
        factory.setVirtualHost("/");
        // 5. 设置访问的用户名和密码
        factory.setUsername("admin");
        factory.setPassword("admin");
        // 6. 创建连接对象
        Connection conn = null;
        Channel channel = null;
        try {
            conn = factory.newConnection();
            // 7. 创建消息通道
            channel = conn.createChannel();

            // 8. 发送消息
            String msg = "hello world";
            channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }

            if (null != conn && conn.isOpen()) {
                try {
                    conn.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        XYProducer xyProducer = new XYProducer();
        xyProducer.create();
    }
}

SpringBoot集成RabbitMQ

工程名:p003002-rabbitmq-springboot

创建SpringBoot工程

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

yml配置

spring:
  rabbitmq:
  	# mq的地址
    host: 192.168.200.16
    port: 5672
    username: admin
    password: admin
    # vhost虚拟主机
    virtual-host: /

配置代码

  • RabbitmqConfig主要做了3件事:
    • 定义exchange;
    • 定义queue;
    • 绑定exchange和queue并指定routingKey
package com.learn.rabbitmqapp.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitmqConfig {

    public final static String EXCHANGE_DIRECT_QQ = "exchange_direct_qq";
    public final static String EXCHANGE_TOPIC_WECHAT = "exchange_topic_wechat";
    public final static String EXCHANGE_FANOUT = "exchange_fanout";

    public final static String QUEUE_QQ = "queue_qq";
    public final static String QUEUE_WECHAT = "queue_wechat";

    public final static String ROUTINGKEY_QQ_DIRECT = "routingkey_qq_direct";
    public final static String ROUTINGKEY_WECHAT_TOPIC = "routingkey.*.wechat.#.topic";

    /**
     * exchange,交换机
     */
    @Bean("directExchange")
    public Exchange directExchange() {
        return ExchangeBuilder.directExchange(EXCHANGE_DIRECT_QQ).durable(true).build();
    }

    @Bean("topicExchange")
    public Exchange topicExchange() {
        return ExchangeBuilder.topicExchange(EXCHANGE_TOPIC_WECHAT).durable(true).build();
    }

    @Bean("fanoutExchange")
    public Exchange fanoutExchange() {
        return ExchangeBuilder.fanoutExchange(EXCHANGE_FANOUT).durable(true).build();
    }

    /**
     * queue,队列
     */
    @Bean("qqQueue")
    public Queue qqQueue() {
        return new Queue(QUEUE_QQ, true, false, false, null);
    }

    @Bean("wechatQueue")
    public Queue wechatQueue() {
        return new Queue(QUEUE_WECHAT, true, false, false, null);
    }

    /**
     * binding,绑定交换机和队列
     */
    @Bean("bindingQQQueueToDirectExchange")
    public Binding bindingQQQueueToDirectExchange(@Qualifier("directExchange") Exchange exchange, @Qualifier("qqQueue") Queue queue) {
        return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_QQ_DIRECT).noargs();
    }

    @Bean("bindingWechatQueueToTopicExchange")
    public Binding bindingWechatQueueToTopicExchange(@Qualifier("topicExchange") Exchange exchange, @Qualifier("wechatQueue") Queue queue) {
        return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_WECHAT_TOPIC).noargs();
    }

    @Bean("bindingQQQueueToFanoutExchange")
    public Binding bindingQQQueueToFanoutExchange(@Qualifier("fanoutExchange") Exchange exchange, @Qualifier("qqQueue") Queue queue) {
        return BindingBuilder.bind(queue).to(exchange).with(EXCHANGE_FANOUT).noargs();
    }

    @Bean("bindingWechatQueueToFanoutExchange")
    public Binding bindingWechatQueueToFanoutExchange(@Qualifier("fanoutExchange") Exchange exchange, @Qualifier("wechatQueue") Queue queue) {
        return BindingBuilder.bind(queue).to(exchange).with(EXCHANGE_FANOUT).noargs();
    }
}

消费者代码

package com.learn.rabbitmqapp.consumer;

import com.learn.rabbitmqapp.config.RabbitmqConfig;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class MessageConsumer {

    @RabbitListener(queues = RabbitmqConfig.QUEUE_QQ)
    public void qqMsgHandler(Object obj, Message message, Channel channel) {
        String msg = new String(message.getBody());
        System.out.println("RabbitmqConfig.QUEUE_QQ msg: " + msg);
    }

    @RabbitListener(queues = RabbitmqConfig.QUEUE_WECHAT)
    public void wechatMsgHandler(Object obj, Message message, Channel channel) {
        String msg = new String(message.getBody());
        System.out.println("RabbitmqConfig.QUEUE_WECHAT msg: " + msg);
    }
}

生产者代码

package com.learn.rabbitmqapp.producer;

import com.learn.rabbitmqapp.config.RabbitmqConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MessageProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendDirect() {
        /**
         * 参数:
         * 1、交换机名称
         * 2、routingKey
         * 3、消息内容
         */
        rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE_DIRECT_QQ, RabbitmqConfig.ROUTINGKEY_QQ_DIRECT, "xy msg");
    }

    public void sendTopic() {
        rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE_TOPIC_WECHAT, "routingkey.xy.wechat.info.topic", "wechat msg");
    }

    public void sendFanout() {
        //fanout,不用写routingkey
        rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE_FANOUT, "", "fanout msg");
    }
}

查看结果

  • 可以进RabbitMQ的web管理页查看exchange和queue
    在这里插入图片描述

参考资料

  • 《咕泡云课堂》

  • 官网

https://www.rabbitmq.com/#getstarted

  • 持久化

https://www.cnblogs.com/x-poior/p/6380064.html

https://www.jianshu.com/p/84b3e5d9f8f8#:~:text=RabbitMQ%E6%9C%AC%E8%BA%AB%E5%B8%A6%E6%9C%89%E6%8C%81%E4%B9%85%E5%8C%96%E6%9C%BA%E5%88%B6%EF%BC%8C%E5%8C%85%E6%8B%AC%E4%BA%A4%E6%8D%A2%E6%9C%BA%E3%80%81%E9%98%9F%E5%88%97%E4%BB%A5%E5%8F%8A%E6%B6%88%E6%81%AF%E7%9A%84%E6%8C%81%E4%B9%85%E5%8C%96%E3%80%82,%E6%8C%81%E4%B9%85%E5%8C%96%E7%9A%84%E4%B8%BB%E8%A6%81%E6%9C%BA%E5%88%B6%E5%B0%B1%E6%98%AF%E5%B0%86%E4%BF%A1%E6%81%AF%E5%86%99%E5%85%A5%E7%A3%81%E7%9B%98%EF%BC%8C%E5%BD%93RabbtiMQ%E6%9C%8D%E5%8A%A1%E5%AE%95%E6%9C%BA%E9%87%8D%E5%90%AF%E5%90%8E%EF%BC%8C%E4%BB%8E%E7%A3%81%E7%9B%98%E4%B8%AD%E8%AF%BB%E5%8F%96%E5%AD%98%E5%85%A5%E7%9A%84%E6%8C%81%E4%B9%85%E5%8C%96%E4%BF%A1%E6%81%AF%EF%BC%8C%E6%81%A2%E5%A4%8D%E6%95%B0%E6%8D%AE%E3%80%82%20%E9%BB%98%E8%AE%A4%E4%B8%8D%E6%98%AF%E6%8C%81%E4%B9%85%E5%8C%96%E7%9A%84%EF%BC%8C%E5%9C%A8%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%87%8D%E5%90%AF%E4%B9%8B%E5%90%8E%EF%BC%8C%E4%BA%A4%E6%8D%A2%E6%9C%BA%E4%BC%9A%E6%B6%88%E5%A4%B1%E3%80%82

  • SpringBoot整合RabbitMQ

https://blog.csdn.net/huzecom/article/details/103578310

  • Rabbitmq用户角色

https://blog.csdn.net/zyz511919766/article/details/42292655

  • rabbitmq.conf配置文件示例

https://github.com/rabbitmq/rabbitmq-server/blob/main/deps/rabbit/docs/rabbitmq.conf.example

  • rabbitmq.conf配置文件的配置项说明

https://www.rabbitmq.com/configure.html#config-items

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值