RabbitMQ

简介

安装配置

下文安装是在虚拟机中,操作系统为Centos8

erlang

参考自官网https://www.rabbitmq.com/download.html
注意erlang版本匹配

image-20210114162919190

有多种安装源,这里使用匹配rabbitmq的精简版https://packagecloud.io/rabbitmq/erlang
image-20210114163517687

我的系统版本为8,点击这个链接:
image-20210114163255252

执行右侧的两个命令:

image-20210114163328766

# 安装yum源
curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash

# 安装erlang
yum install erlang

依赖包

rabbitmq还有其他依赖包,直接安装即可:image-20210114163852334

$ yum install socat
$ yum install logrotate

rabbitmq

参考自官网https://www.rabbitmq.com/install-rpm.html
image-20210114224529578

同样地,安装yum源:
image-20210114224629413

curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.rpm.sh | sudo bash

安装server:

$ yum install rabbitmq-server

配置文件

模板文件

自3.7.0版本开始,rabbitmq-server使用新形式的配置文件。
下载模板文件(包含多数的默认配置),参考官网https://www.rabbitmq.com/configure.html:

image-20210115143240563

下载路径https://github.com/rabbitmq/rabbitmq-server/blob/master/deps/rabbit/docs/rabbitmq.conf.example:
image-20210115143358142

创建文件

创建文件/etc/rabbitmq/rabbitmq.conf,内容与上面的模板文件一致

修改文件

为启用控制管理台,以图形化界面查看rabbitmq,需要修改/etc/rabbitmq/rabbitmq.conf:
image-20210115145749501

将注释拿掉,允许使用guest用户远程访问。

启用管理

$ rabbitmq-plugins enable rabbitmq_management

启动测试

运行命令:

$ systemctl start rabbitmq-server

查看日志/var/log/rabbitmq/rabbit@server.log,可以看到配置文件生效:
image-20210115150513604

访问: http://:15672,用户密码guest/guest,至此安装完成:
image-20210115150611233

用户

创建用户

创建一个自己的管理用户。当然使用guest也是可以的。
在Admin页签,新建用户:
image-20210118140529582

最后的Tags,是权限设定,我们就选最大的Admin

创建虚拟主机

可以简单地将虚拟主机视作数据库,比如MySql中,一般会对每个业务模块,单独创建一个数据库,而虚拟主机也可以视作业务分离的设定:
image-20210118140851041

名称一般以"/"开头, 后面接业务模块名。

点击新建好的虚拟主机名:
image-20210118141039866

默认是分配给了guest,我们将其清掉,然后新增分配给新建的用户:
image-20210118141145877

开发使用

简单模式

image-20210119142506019

此模式最为精简,一个生产者,一个消费者,一个队列。
新建一个空的maven项目,然后:

生产者

添加依赖
<dependency>
  <groupId>com.rabbitmq</groupId>
  <artifactId>amqp-client</artifactId>
  <version>5.10.0</version>
</dependency>

与jdk的版本要求,可参考官网https://www.rabbitmq.com/java-versions.html:
image-20210118142609379

创建连接
package org.strive.simple;

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

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

public class ConnectionUtil {
    public static Connection connection;

    public static Connection getConnection() throws IOException, TimeoutException {
        if (connection != null)
            return connection;

        // 1. 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();

        // 配置服务端信息
        // 2.1 设置服务端主机(默认localhost)
        connectionFactory.setHost("server.strive.com");
        // 2.2 设置端口
        connectionFactory.setPort(5672);
        // 2.3 设置用户名(默认guest)
        connectionFactory.setUsername("strive");
        // 2.4 设置密码(默认guest)
        connectionFactory.setPassword("...");
        // 2.5 设置虚拟主机(默认/)
        connectionFactory.setVirtualHost("/strive");

        // 3. 创建连接
        connection = connectionFactory.newConnection();

        return connection;
    }
}

这里的连接信息要正确,例如测试时,端口给错,虚拟主机未设置,就报了EOFException

发布消息
package org.strive.simple;

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

public class Producer {
    static final String QUEUE_NAME = "simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 获取连接对象
        Connection connection = ConnectionUtil.getConnection();

        // 2. 创建频道
        Channel channel = connection.createChannel();

        /**
         * 3. 声明队列
         * 参数1: 队列名
         * 参数2: 是否持久化
         * 参数3: 是否独占本连接
         * 参数4: 是否在不使用时删除队列
         * 参数5: 其他参数
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        // 5. 发布消息
        String message = "Hello,RabbitMQ!测试ing...";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println("published ...");

        // 6. 释放资源
        channel.close();
        connection.close();
    }
}

队列可以简单视作数据库中的表,发布的消息最终都是存到这里的。

信道

生产者发布时,有个频道(信道)的概念。
一个连接(connection),对应一个TCP连接,其创建、销毁是昂贵的开销。
为应对性能需求,RabbitMQ 采用类似 NIO(Non-blocking I/O)的做法,选择 TCP 连接复用,不仅可以减少性能开销,同时也便于管理。

每个线程把持一个信道,所以信道复用了 Connection 的 TCP 连接。
同时 RabbitMQ 可以确保每个线程的私密性,就像拥有独立的连接一样。
当每个信道的流量不是很大时,复用单一的 Connection 可以在产生性能瓶颈的情况下有效地节省 TCP 连接资源。
但是信道本身的流量很大时,这时候多个信道复用一个 Connection 就会产生性能瓶颈,进而使整体的流量被限制了。此时就需要开辟多个 Connection,将这些信道均摊到这些 Connection 中,至于这些相关的调优策略需要根据业务自身的实际情况进行调节

运行测试

在创建connection处打上断点,以debug方式运行主方法,逐步运行,在管理台查看各步对应的变化。

创建连接后:
image-20210118151544832

创建频道后:
image-20210118151608411

创建队列后:

image-20210118151647347

发布消息后:

image-20210118151736162

点击上图中的队列名"simple_queue",查看消息内容:
image-20210118151902006

消费者

添加依赖

​ 与生产者相同

创建连接

​ 与生产者相同

消费消息
package org.strive.simple;

import com.rabbitmq.client.*;

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

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 获取连接
        Connection connection = ConnectionUtil.getConnection();

        // 2. 创建频道
        Channel channel = connection.createChannel();

        // 3. 声明队列
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);

        // 4.1 创建消息处理对象
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * 处理消息
             * @param consumerTag   消息者标签,在消费时可指定
             * @param envelope      消息包内容,可从中获取消息ID/RoutingKey、交换机、消息、重转标记(收到消息失败时是否重发)
             * @param properties    消息属性
             * @param body          消息体
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("路由key:" + envelope.getRoutingKey());
                System.out.println("交换机:" + envelope.getExchange());
                System.out.println("消息ID:" + envelope.getDeliveryTag());
                System.out.println("消息:" + new String(body, "UTF-8"));
            }
        };
        // 4.2 消费消息
        channel.basicConsume(Producer.QUEUE_NAME, true, consumer);

        // 正常使用时,不会释放资源,而是一直监听
        //channel.close();
        //connection.close();
    }
}

注意这里的队列声明,要与消费者一致,如生产者会持久化消息,而消费者不持久化,则运行就会报错。

运行测试

控制台输出:

image-20210118154238778

服务管理台查看队列,消息数量将为0了

工作模式

image-20210119142527237

此模式下,有一个生产者,多个消费者,以提高消费效率。

生产者

与简单模式一致,只是修改了队列名,提高了发出的消息数量

public class Producer {
    static final String QUEUE_NAME = "work_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 获取连接对象
        Connection connection = ConnectionUtil.getConnection();

        // 2. 创建频道
        Channel channel = connection.createChannel();

        /**
         * 3. 声明队列
         * 参数1: 队列名
         * 参数2: 是否持久化
         * 参数3: 是否独占本连接
         * 参数4: 是否在不使用时删除队列
         * 参数5: 其他参数
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        // 5. 发布消息
        for (int i = 0; i < 20; i++) {
            String message = "RabbitMQ in work mode with seq " + i;
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        }

        // 6. 释放资源
        channel.close();
        connection.close();
    }
}

消费者

与简单模式一致

public class ConsumerOne {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 获取连接
        Connection connection = ConnectionUtil.getConnection();

        // 2. 创建频道
        Channel channel = connection.createChannel();

        // 3. 声明队列
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);

        // 4.1 创建消息处理对象
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("消息ID:" + envelope.getDeliveryTag());
                System.out.println("消息:" + new String(body, "UTF-8"));
            }
        };
        // 4.2 消费消息
        channel.basicConsume(Producer.QUEUE_NAME, true, consumer);

        // 正常使用时,不会释放资源,而是一直监听
        //channel.close();
        //connection.close();
    }
}

在复制一个ConsumerTwo出来

运行测试

先运行两个消费者,再运行生产者,控制台输出:
image-20210119105921824image-20210119105940848

可以看出,两个消费者是轮询访问消息队列的。

发布订阅模式

image-20210119142608248

此模式下,引入新的概念:交换机。
生产者将消息发布至交换机,而交换机绑定了多个队列,会自动将消息发到每个队列上。
注意:若交换机无对应队列,则消息就丢失了!

这种适用于生产者的同一份消息,被多个外围系统所需求。每个外围系统(消费者)对应一个队列,消费自己队列的消息即可。

生产者

public class Producer {
    static final String EXCHANGE_NAME = "fanout_exchange";
    static final String QUEUE_NAME1 = "fanout_queue1";
    static final String QUEUE_NAME2 = "fanout_queue2";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 获取连接对象
        Connection connection = ConnectionUtil.getConnection();

        // 2. 创建频道
        Channel channel = connection.createChannel();

        /**
         * 3.1 声明交换机
         * 参数1:交换机名称
         * 参数2:交换机类型,包括 direct, fanout, topic, headers
         */
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        // 3.2 声明队列
        channel.queueDeclare(QUEUE_NAME1, true, false, false, null);
        channel.queueDeclare(QUEUE_NAME2, true, false, false, null);

        /**
         * 4. 绑定队列
         * 参数1: 队列名
         * 参数2: 交换机名
         * 参数3: 路由key,不能为null
         */
        channel.queueBind(QUEUE_NAME1, EXCHANGE_NAME, "");
        channel.queueBind(QUEUE_NAME2, EXCHANGE_NAME, "");

        // 5. 发布消息
        for (int i = 0; i < 10; i++) {
            String message = "RabbitMQ in fanout mode with seq " + i;
            channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
        }

        // 6. 释放资源
        channel.close();
        connection.close();
    }
}

需要声明交换机、队列名,然后绑定,再发布消息到交换机

消费者

public class ConsumerOne {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 获取连接
        Connection connection = ConnectionUtil.getConnection();
        // 2. 创建频道
        Channel channel = connection.createChannel();

        // 3.1 声明交换机
        channel.exchangeDeclare(Producer.EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        // 3.2 声明队列
        channel.queueDeclare(Producer.QUEUE_NAME1, true, false, false, null);
        // 3.3 绑定交换机与队列
        channel.queueBind(Producer.QUEUE_NAME1, Producer.EXCHANGE_NAME, "");

        // 4.1 创建消息处理对象
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("路由key:" + envelope.getRoutingKey());
                System.out.println("交换机:" + envelope.getExchange());
                System.out.println("消息ID:" + envelope.getDeliveryTag());
                System.out.println("消息:" + new String(body, "UTF-8"));
                System.out.println("-----------------");
            }
        };
        // 4.2 消费消息
        channel.basicConsume(Producer.QUEUE_NAME1, true, consumer);
    }
}

需要声明交换机、自己的队列,并绑定,然后消费自己的队列。
再复制一个ConsumeTwo出来,消费队列2

运行测试

运行生产者,可以看到多了个交换机:
image-20210119134856790

点击名称,可以看到绑定了2个队列:
image-20210119135858383

运行两个消费者,可以看到二者得到的内容是一致的:
image-20210119140026451image-20210119140051248

比较

相对于工作模式来说,发布订阅模式多了个交换机,生产者负责将消息发布至交换机,交换机负责将消息发布至队列。

其实工作模式也使用了交换机,不过是底层使用了默认的交换机,不需在开发时,显式指明。
而发布消息到队列,其实底层也是先转到交换机的。

路由模式

image-20210119142720354

发布订阅模式下,交换机是将每个消息都发送到了每个队列上。
而路由模式下,是可以选择性发布消息到队列的,比如队列1只有错误消息,队列2只有通知消息。

为实现此功能,引入路由key。

生产者

与发布订阅模式基本一致,主要的变化为:

  • 交换机、队列名称变更

  • 交换机的类型为DIRECT

  • 在绑定队列、发布消息时,要显式指定Routing Key

public class Producer {
    static final String EXCHANGE_NAME = "direct_exchange";
    static final String QUEUE_INFO = "direct_queue_info";
    static final String QUEUE_ERR = "direct_queue_err";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 获取连接对象
        Connection connection = ConnectionUtil.getConnection();

        // 2. 创建频道
        Channel channel = connection.createChannel();

        /**
         * 3.1 声明交换机
         * 参数1:交换机名称
         * 参数2:交换机类型,包括direct, fanout, topic, headers
         */
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        // 3.2 声明队列
        channel.queueDeclare(QUEUE_INFO, true, false, false, null);
        channel.queueDeclare(QUEUE_ERR, true, false, false, null);

        // 3.3 绑定队列
        channel.queueBind(QUEUE_INFO, EXCHANGE_NAME, "info");
        channel.queueBind(QUEUE_ERR, EXCHANGE_NAME, "err");

        // 4. 发布消息
        String message = "路由KEY: info";
        channel.basicPublish(EXCHANGE_NAME, "info", null, message.getBytes());
        message = "路由KEY: err";
        channel.basicPublish(EXCHANGE_NAME, "err", null, message.getBytes());

        // 5. 释放资源
        channel.close();
        connection.close();
    }
}

消费者

同样地,在绑定队列时,要指定Routing Key

package org.strive.direct;

import com.rabbitmq.client.*;
import org.strive.util.ConnectionUtil;

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

public class ConsumerOne {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 获取连接
        Connection connection = ConnectionUtil.getConnection();
        // 2. 创建频道
        Channel channel = connection.createChannel();

        // 3.1 声明交换机
        channel.exchangeDeclare(Producer.EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        // 3.2 声明队列
        channel.queueDeclare(Producer.QUEUE_INFO, true, false, false, null);
        // 3.3 绑定交换机与队列
        channel.queueBind(Producer.QUEUE_INFO, Producer.EXCHANGE_NAME, "info");

        // 4.1 创建消息处理对象
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("路由key:" + envelope.getRoutingKey());
                System.out.println("交换机:" + envelope.getExchange());
                System.out.println("消息ID:" + envelope.getDeliveryTag());
                System.out.println("消息:" + new String(body, "UTF-8"));
                System.out.println("-----------------");
            }
        };
        // 4.2 消费消息
        channel.basicConsume(Producer.QUEUE_INFO, true, consumer);
    }
}        

运行测试

运行生产者,可以在监控平台看到多了个交换机,其绑定了2个队列:
image-20210119150302607

运行两个消费者,查看控制台:
image-20210119150344137image-20210119150358376

通配符模式

image-20210119153940934

与路由模式类似,不过路由模式的Routing Key为全词匹配,而通配符模式是可以使用通配符的。

在通配符模式中,"."之间的字符串为一个词,共有两个通配符:

  • : 匹配任意多个词

  • *: 匹配0或1个词

相较于路由模式的改动:

  • 交换机、队列名称变更
  • 交换机类型为TOPIC
  • 路由Key使用通配符

生产者

public class Producer {
    static final String EXCHANGE_NAME = "topic_exchange";
    static final String QUEUE_INFO = "topic_queue_info";
    static final String QUEUE_ERR = "topic_queue_err";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.1 获取连接对象
        Connection connection = ConnectionUtil.getConnection();
        // 1.2 创建信道
        Channel channel = connection.createChannel();

        // 2.1 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        // 2.2 声明队列
        channel.queueDeclare(QUEUE_INFO, true, false, false, null);
        channel.queueDeclare(QUEUE_ERR, true, false, false, null);
        // 2.3 绑定队列
        channel.queueBind(QUEUE_INFO, EXCHANGE_NAME, "info.#");
        channel.queueBind(QUEUE_ERR, EXCHANGE_NAME, "*.err");

        // 5. 发布消息
        String message = "路由KEY: info.rabbitmq.topic";
        channel.basicPublish(EXCHANGE_NAME, "info.rabbitmq.topic", null, message.getBytes());
        message = "路由KEY: info.";
        channel.basicPublish(EXCHANGE_NAME, "info.", null, message.getBytes());
        message = "路由KEY: info.rabbit.err";
        channel.basicPublish(EXCHANGE_NAME, "info.rabbit.err", null, message.getBytes());
        message = "路由KEY: rabbitmq.err";
        channel.basicPublish(EXCHANGE_NAME, "rabbitmq.err", null, message.getBytes());
        message = "路由KEY: rabbitmq.err";
        channel.basicPublish(EXCHANGE_NAME, "rabbitmq.err", null, message.getBytes());
        message = "路由KEY: .err";
        channel.basicPublish(EXCHANGE_NAME, ".err", null, message.getBytes());

        // 6. 释放资源
        channel.close();
        connection.close();
    }
}

这里发布了6条消息:info开头,并后缀2/0/2个词;err结尾,并前缀1/1/0个词

消费者

同样地,变更交换机类型、Routing Key

public class ConsumerOne {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.1 获取连接
        Connection connection = ConnectionUtil.getConnection();
        // 1.2 创建信道
        Channel channel = connection.createChannel();

        // 2.1 声明交换机
        channel.exchangeDeclare(Producer.EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        // 2.2 声明队列
        channel.queueDeclare(Producer.QUEUE_INFO, true, false, false, null);
        // 2.3 绑定交换机与队列
        channel.queueBind(Producer.QUEUE_INFO, Producer.EXCHANGE_NAME, "info.#");

        // 3.1 创建消息处理对象
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("路由key:" + envelope.getRoutingKey());
                System.out.println("交换机:" + envelope.getExchange());
                System.out.println("消息ID:" + envelope.getDeliveryTag());
                System.out.println("消息:" + new String(body, "UTF-8"));
                System.out.println("-----------------");
            }
        };
        // 3.2 消费消息
        channel.basicConsume(Producer.QUEUE_INFO, true, consumer);
    }
}

在复制一个ConsumerTwo出来,消费队列QUEUE_ERR

运行测试

消费者的控制台输出:
image-20210119155012592image-20210119155058964

可以看到,info开头的都被ConsumerOne消费掉,而err结尾且前缀只有一个词的被ConsumerTwo消费掉。
还可以看到,Routing Key是允许重复的

SpringBoot集成

本节介绍RabbitMQ与SpringBoot集成的方法。

生产者

大致步骤包括:

  • 引用相关依赖
  • 设置rabbitmq的配置文件(创建连接的相关信息)
  • 创建配置类(声明交换机、队列等)

创建项目

创建一个空的maven项目,引入相关依赖

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.7.RELEASE</version>
	</parent>

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

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

创建一空的module,亦是maven类型,不需引入其他依赖。

项目结构:
image-20210120161828830

启动类

package org.strive;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProducerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class, args);
    }
}

配置文件

在resources文件夹下创建application.yml:

# 访问端口
server:
  port: 8001

# rabbitmq
spring:
  rabbitmq:
    host: server.strive.com
    port: 5672
    virtual-host: /strive
    username: strive
    password: ...

这里rabbitmq的相关设置,全是创建连接所需的基本信息

配置类

package org.strive.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 ProducerConfig {
    public static final String EXCHANGE_NAME = "spring-topic-exchange";
    public static final String QUEUE_NAME = "spring-topic-queue";

    // 声明交换机
    @Bean("topicExchange")
    public Exchange getExchange() {
        return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
    }

    // 声明队列
    @Bean("topicQueue")
    public Queue getQueue() {
        return QueueBuilder.durable(QUEUE_NAME).build();
    }

    // 绑定交换机与队列
    @Bean
    public Binding getBinding(@Qualifier("topicExchange") Exchange exchange,
                              @Qualifier("topicQueue") Queue queue) {
        return BindingBuilder.bind(queue).to(exchange).with("info.#").noargs();
    }
}

控制器

package org.strive.controller;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.strive.config.ProducerConfig;

@RestController
public class PublishMsg {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/publish")
    public String publishGetMsg(@RequestParam String key,
                                @RequestParam String message) {
        rabbitTemplate.convertAndSend(ProducerConfig.EXCHANGE_NAME, key, message);
        return "发布成功!";
    }
}

这里发布了一个Get方法,通过RabbitTemplate模板对象,完成消息发布。

运行测试

通过启动类启动,然后通过浏览器访问:
image-20210120162406791

然后查看控制台,可以看到发布成功:
image-20210120162455277

消费者

与生产者类似,不过:

  • 不需创建配置类
  • 不需创建控制器,而是创建监听器

创建项目

与生产者同一项目下,创建新的module

启动类

package org.strive;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}

配置文件

# 访问端口
server:
  port: 9001

# rabbitmq
spring:
  rabbitmq:
    host: server.strive.com
    port: 5672
    virtual-host: /strive
    username: strive
    password: xin773717

与生产者一致,只需改个端口

监听器

package org.strive.listener;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener {
    @RabbitListener(queues = {"spring-topic-queue"})
    public void getMessage(String message) {
        System.out.println("消费的消息:" + message);
    }
}

使用@RabbitListener注解,监听指定名称的队列

运行测试

启动消费者,可以在控制台查看到前面发布的消息:
image-20210120165120176

posted @ 2022-12-29 21:55  水木夏  阅读(61)  评论(1编辑  收藏  举报