RabbitMQ快速入门并实现与Spring Boot的整合

本文介绍了RabbitMQ的基本概念,包括消息生产和消费流程,以及各种工作模式(最简、工作、广播、点对点和主题)的示例。同时展示了如何在SpringBoot项目中整合RabbitMQ,包括配置、生产者和消费者的代码实现。
摘要由CSDN通过智能技术生成

RabbitMQ是什么

RabbitMQ是一个开源的、可靠的消息代理。它应用非常广泛,不管是小型企业还是大型公司,RabbitMQ都能胜任

RabbitMQ相关的一些基本概念

  • 生产者
    生产者是发送消息给RabbitMQ的应用程序,它不属于RabbitMQ
  • 消费者
    消费者是监听并消费RabbitMQ消息的应用程序,它不属于RabbitMQ
  • 虚拟主机
    一个RabbitMQ可以创建多个虚拟主机,一个虚拟主机可以创建多个交换机和队列。虚拟主机起到权限隔离、数据隔离的作用
  • 交换机
    交换机负责接收生产者发送的消息,并将消息根据路由key转发到绑定的队列中,常见的交换机类型有fanout(广播)、direct(点对点)、topic(主题)
  • 队列
    队列是一个消息缓冲区,负责接收并存储消息
  • 绑定关系
    队列会与交换机进行绑定
  • 路由key
    路由key决定了交换机与队列的绑定关系,也决定了交换机最终会把消息转发到哪个队列中
  • 消息
    消息是一个二进制数据,最终会存储到队列中

消息的生产消费的流程

生产者生产并发送消息,交换机接收到消息后转发到指定的队列中,然后消费者监听队列,如果队列中有消息,则消费者会消费消息。我们可以将这些角色与邮局进行类比,生产者就相当于发件人,消息就相当于邮件,交换机就相当于发件箱与邮递员,队列就相当于收件箱,消费者就相当于收件人,这样理解就清晰多了。

RabbitMQ的下载、安装、运行

Docker一键运行RabbitMQ容器

docker run -d -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.13-management

其中 5672 是客户端与RabbitMQ的通信端口,15672 是RabbitMQ控制台访问端口
最后打印出了容器Id,则代表RabbitMQ已启动成功了
在这里插入图片描述

RabbitMQ控制台使用

  • 登录RabbitMQ控制台
    • 控制台访问链接:http://{your ip}:15672
    • 账号/密码:guest/guest

进入控制台后可以看到以下界面,我这里使用的RabbitMQ版本是3.13.1,Erlang是26.2.4

在这里插入图片描述

  • 创建一个direct类型的交换机
    在这里插入图片描述

  • 创建一个队列
    在这里插入图片描述

  • 交换机绑定队列
    在这里插入图片描述
    在这里插入图片描述

  • 发送一条消息
    在这里插入图片描述
    在这里插入图片描述

  • 消费一条消息
    在这里插入图片描述
    在这里插入图片描述

如上图,我们在控制台简单演示了如何创建交换机、队列,如何绑定交换机和队列以及如何发送和消费消息,接下来我们用Java代码来实现类似的效果

使用Java客户端生产与消费消息

最简模式

最简模式的消息结构如下图,一个生产者负责发送消息给队列,然后一个消费者负责监听队列并消费消息。其实这里没有画出交换机,因为这里使用的是RabbitMQ的默认交换机,默认交换机是direct类型。一个队列被创建出来后就会自动绑定默认交换机,绑定的路由key是队列名
在这里插入图片描述

  • 创建一个连接工厂工具类
/**
     * 获取连接
     * @return
     */
    public static Connection getConnection() {
        try {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("localhost");  // RabbitMQ主机IP
            factory.setPort(5672);  // RabbitMQ通信端口
            factory.setVirtualHost("/");  // 设置虚拟主机,每个虚拟主机就相当于一个消息代理
            factory.setUsername("guest");
            factory.setPassword("guest");
            return factory.newConnection();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        return null;
    }
  • 编写生产者程序
public static void main(String[] args) throws IOException, TimeoutException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、生产消息
            // 3.1 声明队列
            String queueName = "basic_q";
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.2 发送消息
            String msg = "hello basic";
            channel.basicPublish("", queueName, null, msg.getBytes());
            System.out.println("发送了一条消息:" + msg);
            // 4、关闭连接
            channel.close();
            connection.close();
        }
    }
  • 编写消费者程序
public static void main(String[] args) throws IOException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、消费消息
            // 3.1 声明队列
            String queueName = "basic_q";
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.2 监听队列
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                // 3.3 消费消息
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println("我消费了消息:" + new String(body));
                }
            };
            channel.basicConsume(queueName, true, consumer);
        }
    }
  • 启动消费者

  • 启动生产者

  • 测试结果
    在这里插入图片描述
    在这里插入图片描述

  • 说明

    • 生产者与消费者程序声明的队列名要一致
    • 启动生产者或消费者程序时,如果声明的队列不存在,则会创建队列,否则不会重复创建
    • 消费者程序启动后会阻塞并监听队列,如果队列有消息则会进行消费

工作模式

工作模式的消息结构如下图,一个生产者负责发送消息给队列,然后多个消费者负责监听队列并消费消息。这里使用的交换机依然是默认交换机。一个队列中的消息只能被消费一次,默认情况下,监听队列的多个消费者会轮询消费消息
在这里插入图片描述

  • 编写生产者程序
 public static void main(String[] args) throws IOException, TimeoutException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、生产消息
            // 3.1 声明队列
            String queueName = "work_q";
            // durable 队列的持久化,防止消息代理挂了导致队列和消息丢失
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.2 发送多条消息
            for (int i = 0; i < 12; i++) {
                String msg = "hello work" + i;
                channel.basicPublish("", queueName, null, msg.getBytes());
                System.out.println("发送了一条消息:" + msg);
            }
            // 4、关闭连接
            channel.close();
            connection.close();
        }
    }
  • 编写消费者程序
public static void main(String[] args) throws IOException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、消费消息
            // 3.1 声明队列
            String queueName = "work_q";
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.2 监听队列
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                // 3.3 消费消息
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println("我消费了消息:" + new String(body));
                }
            };
            channel.basicConsume(queueName, true, consumer);
        }
    }
  • 启动消费者
    • 可以自行复制一份消费者代码,然后启动两个消费者去监听队列
  • 启动生产者
  • 测试结果
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 说明
    可以看到两个消费者是轮询消费消息的

广播模式

广播模式的消息结构如下图,一个生产者发送消息给交换机,交换机将消息转发到所有与它绑定的队列中。广播模式的交换机类型是fanout
在这里插入图片描述

  • 编写生产者程序
public static void main(String[] args) throws IOException, TimeoutException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、生产消息
            // 3.1 声明交换机
            String exchangeName = "fanout_e";
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT);
            // 3.2 发送消息
            String msg = "hello fanout";
            channel.basicPublish(exchangeName, "", null, msg.getBytes());
            System.out.println("发送了一条消息:" + msg);
            // 4、关闭连接
            channel.close();
            connection.close();
        }
    }
  • 编写消费者程序
   public static void main(String[] args) throws IOException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、消费消息
            // 3.1 声明交换机
            String exchangeName = "fanout_e";
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT);
            // 3.2 声明队列
            String queueName = "fanout_q1";
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.3 绑定交换机
            channel.queueBind(queueName, exchangeName, "");
            // 3.4 监听队列
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                // 3.5 消费消息
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println("我消费了消息:" + new String(body) + ";将要发送短信");
                }
            };
            channel.basicConsume(queueName, true, consumer);
        }
    }
 public static void main(String[] args) throws IOException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、消费消息
            // 3.1 声明交换机
            String exchangeName = "fanout_e";
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT);
            // 3.2 声明队列
            String queueName = "fanout_q2";
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.3 绑定交换机
            channel.queueBind(queueName, exchangeName, "");
            // 3.4 监听队列
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                // 3.5 消费消息
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println("我消费了消息:" + new String(body) + ";将要发送邮件");
                }
            };
            channel.basicConsume(queueName, true, consumer);
        }
    }
  • 启动消费者

  • 启动生产者

  • 测试结果
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 说明

    • 启动生产者或消费者程序时,如果声明的交换机不存在,则会创建交换机,否则不会重复创建
    • 生产者与消费者声明的交换机名和交换机类型要一致

点对点模式

点对点模式的消息结构如下图,一个生产者发送消息给交换机,然后交换机根据消息携带的路由key转发到精确匹配的队列中。点对点模式的交换机类型是direct
在这里插入图片描述

  • 编写生产者程序
public static void main(String[] args) throws IOException, TimeoutException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、生产消息
            // 3.1 声明交换机
            String exchangeName = "direct_e";
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT);
            // 3.2 发送消息
            String msg = "hello direct";
            channel.basicPublish(exchangeName, "email", null, msg.getBytes());
            System.out.println("发送了一条消息:" + msg);
            // 4、关闭连接
            channel.close();
            connection.close();
        }

    }
  • 编写消费者程序
public static void main(String[] args) throws IOException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、消费消息
            // 3.1 声明交换机
            String exchangeName = "direct_e";
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT);
            // 3.2 声明队列
            String queueName = "direct_q1";
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.3 绑定交换机
            channel.queueBind(queueName, exchangeName, "sms");
            // 3.4 监听队列
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                // 3.5 消费消息
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println("我消费了消息:" + new String(body)+";将要发送短信");
                }
            };
            channel.basicConsume(queueName, true, consumer);
        }
    }
 public static void main(String[] args) throws IOException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、消费消息
            // 3.1 声明交换机
            String exchangeName = "direct_e";
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT);
            // 3.2 声明队列
            String queueName = "direct_q2";
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.3 绑定交换机
            channel.queueBind(queueName, exchangeName, "email");
            // 3.4 监听队列
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                // 3.5 消费消息
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println("我消费了消息:" + new String(body) + ";将要发送邮件");
                }
            };
            channel.basicConsume(queueName, true, consumer);
        }
    }
  • 启动消费者

  • 启动生产者

  • 测试结果
    在这里插入图片描述
    在这里插入图片描述

  • 说明

    • 只有路由key是email的消费者消费了消息

主题模式

主题模式的消息结构如下图,一个生产者发送消息给交换机,然后交换机根据消息携带的路由key转发到匹配的队列中。主题模式的路由key是可以有通配符的,其中#代表一个任意单词,*代表0个或多个任意单词,单词之间用.隔开。主题模式的交换机类型是topic。主题模式与点对点模式最大的不同是主题模式的路由key是有通配符的,可以模糊匹配队列
在这里插入图片描述

  • 编写生产者程序
public static void main(String[] args) throws IOException, TimeoutException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、生产消息
            // 3.1 声明交换机
            String exchangeName = "topic_e";
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC);
            // 3.2 发送消息
            String msg = "hello topic";
            channel.basicPublish(exchangeName, "sms.we", null, msg.getBytes());
            System.out.println("发送了一条消息:" + msg);
            // 4、关闭连接
            channel.close();
            connection.close();
        }
    }
  • 编写消费者程序
  public static void main(String[] args) throws IOException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、消费消息
            // 3.1 声明交换机
            String exchangeName = "topic_e";
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC);
            // 3.2 声明队列
            String queueName = "topic_q1";
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.3 绑定交换机
            channel.queueBind(queueName, exchangeName, "email.*");
            // 3.4 监听队列
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                // 3.5 消费消息
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println("我消费了消息:" + new String(body)+";将要发送邮件");
                }
            };
            channel.basicConsume(queueName, true, consumer);
        }
    }
public static void main(String[] args) throws IOException {
        // 1、获取连接
        Connection connection = ConnectionUtils.getConnection();
        if (Objects.nonNull(connection)) {
            // 2、建立通道
            Channel channel = connection.createChannel();
            // 3、消费消息
            // 3.1 声明交换机
            String exchangeName = "topic_e";
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC);
            // 3.2 声明队列
            String queueName = "topic_q2";
            channel.queueDeclare(queueName, true, false, false, null);
            // 3.3 绑定交换机
            channel.queueBind(queueName, exchangeName, "sms.#");
            // 3.4 监听队列
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                // 3.5 消费消息
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println("我消费了消息:" + new String(body)+";将要发送短信");
                }
            };
            channel.basicConsume(queueName, true, consumer);
        }
    }
  • 启动消费者

  • 启动生产者

  • 测试结果
    在这里插入图片描述
    在这里插入图片描述

  • 说明

    • 只有路由key是sms.#的消费者消费了消息

Spring Boot整合RabbitMQ

本次整合示例使用topic类型的交换机

  • 创建一个Spring Boot项目

我使用的Spring Boot版本是2.3.6.RELEASE,您可以自行选择版本
在这里插入图片描述

  • 配置maven依赖
<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>
  • 配置RabbitMQ连接信息
spring:
  rabbitmq:
    host: localhost
    port: 5672
    virtual-host: /
    username: guest
    password: guest
  • 编写生产者程序
@RestController
@RequestMapping("/topic")
public class TopicController {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public static final String TOPIC_EX_NAME = "sb_topic_e";

    /**
     * @Description: 只发送短信
     * @Date: 2023/6/8
     */
    @GetMapping("/sendSms")
    public String sendSms() {
        rabbitTemplate.convertAndSend(TOPIC_EX_NAME, "sms", "发送短信");
        return "success";
    }

    /**
     * @Description: 发送短信和邮件
     * @Date: 2023/6/8
     */
    @GetMapping("/sendSmsAndEmail")
    public String sendSmsAndEmail() {
        rabbitTemplate.convertAndSend(TOPIC_EX_NAME, "sms.email", "发送短信和邮件");
        return "success";
    }
}

  • 编写消费者程序
@Component
public class TopicConsume {
    public static final String TOPIC_EX_NAME = "sb_topic_e";
    public static final String QUEUE_EX_NAME_ONE = "sb_topic_que_one";
    public static final String QUEUE_EX_NAME_TWO = "sb_topic_que_two";
    public static final String TOPIC_KEY1 = "sms";
    public static final String TOPIC_KEY2 = "sms.*";

    /**
     * @Description: 监听短信发送
     * @Date: 2023/6/8
     */
    @RabbitListener(bindings = @QueueBinding(value = @Queue(QUEUE_EX_NAME_ONE), exchange = @Exchange(name = TOPIC_EX_NAME, type = "topic"), key = {TOPIC_KEY1}))
    public void receiveSms(String msg) {
        System.out.println("短信消费方收到了消息:" + msg);
    }

    /**
     * @Description: 监听短信和邮件发送
     * @Date: 2023/6/8
     */
    @RabbitListener(bindings = @QueueBinding(value = @Queue(QUEUE_EX_NAME_TWO), exchange = @Exchange(name = TOPIC_EX_NAME, type = "topic"), key = {TOPIC_KEY2}))
    public void receiveSmsAndEmail(String msg) {
        System.out.println("短信和邮件消费方收到了消息:" + msg);
    }
}
  • 启动项目访问测试

参考来源

RabbitMQ官网

  • 52
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值