RabbitMQ五种工作模式及spirngboot集成

一、文章主旨

第二小节介绍了 RabbitMQ及其控制台的安装,以及常用操作;
第三小节介绍了常见的五种工作模式,并对对其进阶比较做了描述,同时配以代码辅助理解;
第四小节则是对springboot框架引入RabbitMQ作了介绍,并以通配符模式作为示例;

二、RabbitMQ Management控制台

安装Windows RabbitMQ及控制台文档.pdf

2.1 创建用户

这里的Tags代表着用户权限,有以下几种:

1、 超级管理员(administrator)
可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操作。
2、 监控者(monitoring)
可登陆管理控制台,同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)
3、 策略制定者(policymaker)
可登陆管理控制台, 同时可以对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。
4、 普通管理者(management)
仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。
5、 其他
无法登陆管理控制台,通常就是普通的生产者和消费者。
在这里插入图片描述

2.2 创建Virtual Host

这里的Virtual Host就类似于数据库中的database概念,每一个都相当于一个独立的Rabbit MQ服务器,相互隔离不能互相通信,并且vhost的命名一般以/开头;
在这里插入图片描述

2.3 给予用户vhost权限

在这里插入图片描述

三、工作模式

3.0 环境及工具类:

  1. 源码下载(包括3、4小节)

  2. Builder构建器源码博客:用来创建连接工具类;

  3. 父依赖:
    生产者及消费者采用子模块的方式,依赖也只有amqp-client

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.coderwhat</groupId>
    <artifactId>rabbitmqDemo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>producer</module>
        <module>consumer</module>
    </modules>

    <properties>
        <java_version>1.8</java_version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java_version}</source>
                    <target>${java_version}</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
</project>
  1. 连接工厂:
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

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

//RabbitMQ连接工厂
public class RQConnectionFactory {
    public static Connection getConnection() throws IOException, TimeoutException {
        ConnectionFactory factory = Builder.of(ConnectionFactory::new)
                //主机地址
                .with(ConnectionFactory::setHost,"localhost")
                //连接端口,默认为5672
                .with(ConnectionFactory::setPort,5672)
                //vhost名称
                .with(ConnectionFactory::setVirtualHost,"/rqhost")
                //连接用户名密码
                .with(ConnectionFactory::setUsername,"admin")
                .with(ConnectionFactory::setPassword,"admin")
                .build();
        return factory.newConnection();
    }
}

3.1 简单模式

简单模式,就是简单的生产与消费的消息队列:

在这里插入图片描述
P:生产者,发送消息的程序;
C:消费者:消息的接受者,会一直等待消息到来(监听状态)。
queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。

下列为生产者、消费者的步骤及示例代码:

步骤生产者消费者
1获取连接获取连接
2创建并声明频道与消息队列创建并声明频道与消息队列
3定义并发送消息创建消费者,设置消息处理方式(后续监听调用)
4关闭连接、通道资源监听队列
  • 生产者
import com.coderwhat.util.RQConnectionFactory;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * @Description: 简单模式 生产者
 * @Date: 2021/4/7 19:16
 * @Author: Evan
 * @Version: v1.0.0
 */
public class MySimpleProducer {
    
    //定义队列名
    private static final String QUEUE_NAME = "simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接
        Connection connection = RQConnectionFactory.getConnection();
        //2.创建频道
        Channel channel = connection.createChannel();
        /**
         * 3.声明队列:
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         */
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);
        //4.定义要发送的信息
        String message = "简单模式实例";
        /**
         * 5.发送消息
         * 参数1:交换机名称,如果没有指定则使用默认Default Exchage
         * 参数2:路由key,简单模式可以传递队列名称
         * 参数3:消息其它属性
         * 参数4:消息内容
         */
        channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
        System.out.println("已成功发送简单消息:" + message);

        //6.关闭频道、连接资源
        channel.close();
        connection.close();
    }

}
  • 消费者
import com.coderwhat.util.RQConnectionFactory;
import com.rabbitmq.client.*;

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

/**
 * @Description: 简单模式 消费者
 * @Date: 2021/4/7 20:27
 * @Author: Evan
 * @Version: v1.0.0
 */
public class MySimpleConsumer {
    private static final String QUEUE_NAME = "simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接
        Connection connection = RQConnectionFactory.getConnection();
        //2.创建频道
        Channel channel = connection.createChannel();
        //3.声明频道,与生产者保持一致
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);
        //4.创建消费者,设置消息处理方式(待后续监听调用)
        DefaultConsumer consumer = new DefaultConsumer(channel){
            /**
             * 消费消息方法需要重写
             * @param consumerTag 消费者标签,channel.basicConsume时可以指定
             * @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"));
            }
        };

        /**
         * 5.监听队列
         * 参数1:队列名称
         * 参数2:是否自动确认,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消
         息,设置为false则需要手动确认
         * 参数3:消息接收到后回调处理方法(消费)
         */
        channel.basicConsume(QUEUE_NAME,true,consumer);

        //这里无需再关闭资源,持续监听
    }
}

3.2 工作队列模式

相较于普通模式,工作队列模式中可以有一个或多个消费端,共同消费同一队列中的消息,此时多个消费者之间对于同一个消息是竞争关系;对于任务过重的情况可以提高任务处理速度;
在这里插入图片描述
代码基本与简单模式一致,只需多复制一份消费者代码,此时我们可以在消费者服务中设置频道每次最大处理消息数量,并在回调的时手动声明当前这条消息已被处理(简单模式代码中设置的时自动确认);

  • 消费者
import com.coderwhat.util.RQConnectionFactory;
import com.rabbitmq.client.*;

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

/**
 * @Description: 工作队列模式 消费者
 * @Date: 2021/4/8 22:37
 * @Author: Evan
 * @Version: v1.0.0
 */
public class MyWorkConsumer {

    private static final String QUEUE_NAME = "simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接
        Connection connection = RQConnectionFactory.getConnection();
        //2.创建频道
        Channel channel = connection.createChannel();
        //3.声明频道,与生产者保持一致
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);

        //4.设置一次只能接收并处理一个消息
        channel.basicQos(1);

        //5.创建消费者,设置消息处理方式(待后续监听调用)
        DefaultConsumer consumer = new DefaultConsumer(channel){
            /**
             * 消费消息方法需要重写
             * @param consumerTag 消费者标签,channel.basicConsume时可以指定
             * @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"));

                //确认消息
                /**
                 * 参数1:消息id
                 * 参数2:是否确认,false表示只有当前这条被确认
                 */
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        /**
         * 6.监听队列
         * 前面手动确认消息已被处理,这里就只用设置为false了
         */
        channel.basicConsume(QUEUE_NAME,false,consumer);

        //这里无需再关闭资源,持续监听
    }
}

3.3 发布与订阅模式

订阅模式相较于工作队列模式,多了一个 X (交换机),生产者 P 发送的消息不再直接送到队列中而是交换机,根据交换机类型的不同决定如何将消息传递给后续队列中(交换机不存储消息,如果无法传递出去就会丢失),常见有以下三种交换机:

  • Fanout:广播,将消息交给所有绑定的交换机;
  • Direct:定向,将消息交给符合指定 routing key 的队列;
  • Topic:通配符,叫消息符合 routing parttern 的队列;

发布与订阅模式:
在这里插入图片描述

发布与订阅模式就是使用的 Fanout 交换机,消费者监听自己的队列,生产者将消息发送给交换机后,交换机转发至每个绑定的队列中,因此都能接收到消息;

下列为生产者、消费者的步骤及示例代码:
此时我们在代码中可以看到,工作队列模式只是没有设置与手动绑定交换机(底层使用默认交换机);

步骤生产者消费者
1获取连接获取连接
2创建频道创建频道
3声明频道绑定的多个消息队列声明当前消费者绑定的消息队列
4声明交换机名称与类型(Fanout),并绑定绑定队列声明交换机名称与类型(Fanout),并绑定绑定队列
3定义并发送消息创建消费者,设置消息处理方式(后续监听调用)
4关闭连接、通道资源监听队列
  • 生产者:
import com.coderwhat.util.RQConnectionFactory;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * @Description: 发布订阅模式生产者
 * @Date: 2021/4/11 16:07
 * @Author: Evan
 * @Version: v1.0.0
 */
public class MyFanoutProducer {

    //队列名称
    private static final String FANOUT_QUEUE_1 = "fanout_queue_1";

    private static final String FANOUT_QUEUE_2 = "fanout_queue_2";
    //交换机名称
    private static final String EXCHANGE_NAME = "fanout_exchange";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接
        Connection connection = RQConnectionFactory.getConnection();
        //2.创建频道
        Channel channel = connection.createChannel();
        //3.声明两个队列
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         */
        channel.queueDeclare(FANOUT_QUEUE_1,true,false,false,null);
        channel.queueDeclare(FANOUT_QUEUE_2,true,false,false,null);
        //4.声明交换机并将队列绑定到交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        channel.queueBind(FANOUT_QUEUE_1,EXCHANGE_NAME,"");
        channel.queueBind(FANOUT_QUEUE_2,EXCHANGE_NAME,"");
        //5.发布消息
        /**
         * 参数1:交换机名称,如果没有指定则使用默认Default Exchage
         * 参数2:路由key,简单模式可以传递队列名称
         * 参数3:消息其它属性
         * 参数4:消息内容
         */
        String message = "发布订阅模式";
        channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes());
        System.out.println("已成功发送(发布订阅模式消息):" + message);
        //6.关闭资源
        channel.close();
        connection.close();
    }
}
  • 消费者
    每个消费者只用更改其所绑定的队列即可
import com.coderwhat.util.RQConnectionFactory;
import com.rabbitmq.client.*;

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

/**
 * @Description: 发布订阅模式消费者
 * @Date: 2021/4/11 16:08
 * @Author: Evan
 * @Version: v1.0.0
 */
public class MyFanoutConsumer {
    //队列名称
    private static final String FANOUT_QUEUE_1 = "fanout_queue_1";

    //交换机名称
    private static final String EXCHANGE_NAME = "fanout_exchange";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接
        Connection connection = RQConnectionFactory.getConnection();
        //2.创建频道
        Channel channel = connection.createChannel();
        //3.声明当前消费者所绑定的队列
        /**
		 * 参数1:队列名称
		 * 参数2:是否定义持久化队列
		 * 参数3:是否独占本次连接
		 * 参数4:是否在不使用的时候自动删除队列
		 * 参数5:队列其它参数
		 */
        channel.queueDeclare(FANOUT_QUEUE_1,true,false,false,null);
        //4.声明将当前队列绑定到交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        channel.queueBind(FANOUT_QUEUE_1,EXCHANGE_NAME,"");
        //5.创建消费者
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
           /**
             * 消费消息方法需要重写
             * @param consumerTag 消费者标签,channel.basicConsume时可以指定
             * @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志
             *                 (收到消息失败后是否需要重新发送)
             * @param properties 属性信息
             * @param body 消息
             * @throws IOException
             */
            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("消费者1-接收到的消息为:" + new String(body, "UTF-8"));
            }
        };

        //6.监听并回调消费者
        /**
         * 参数1:队列名称
         * 参数2:是否自动确认,设置为true为表示消息接收到自动向mq回复接收到了,
         *       mq接收到回复会删除消息,设置为false则需要手动确认
         * 参数3:消息接收到后回调
         */
        channel.basicConsume(FANOUT_QUEUE_1, true, consumer);
    }
}

3.4 路由模式

路由模式就是在发布订阅模式的基础上,新增了对路由key的限定,在发送消息时指定 routing key,这样交换机只会将消息发送到routing key匹配的队列中;下图所示就代表着第二个队列可以匹配routing keyinfo、error或warning
在这里插入图片描述
下列为生产者、消费者的步骤及示例代码:
routing key一般由一个或多个以.间隔的单词组成;

步骤生产者消费者
1获取连接获取连接
2创建频道创建频道
3声明频道绑定的多个消息队列声明当前消费者绑定的消息队列
4声明交换机名称与类型(Direct),并绑定队列(指定队列routing key)声明交换机名称与类型(Direct),并绑定队列(指定队列routing key)
3定义并发送消息(指定routing key创建消费者,设置消息处理方式(后续监听调用)
4关闭连接、通道资源监听队列
  • 生产者
import com.coderwhat.util.RQConnectionFactory;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * @Description: 路由模式生产者
 * @Date: 2021/4/11 17:02
 * @Author: Evan
 * @Version: v1.0.0
 */
public class MyDirectProducer {

    //交换机名称
    private static final String EXCHANGE_NAME = "direct_exchange";
    //队列名称
    private static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";
    //队列名称
    private static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接
        Connection connection = RQConnectionFactory.getConnection();
        //2.创建频道
        Channel channel = connection.createChannel();
        //3.声明两个队列
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         */
        channel.queueDeclare(DIRECT_QUEUE_INSERT,true,false,false,null);
        channel.queueDeclare(DIRECT_QUEUE_UPDATE,true,false,false,null);
        //4.声明交换机 -> 将队列绑定到交换机并指定队列 routing key
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        channel.queueBind(DIRECT_QUEUE_INSERT,EXCHANGE_NAME,"insert");
        channel.queueBind(DIRECT_QUEUE_UPDATE,EXCHANGE_NAME,"update");
        //5.发布消息并指定消息的 routing key
        /**
         * 参数1:交换机名称,如果没有指定则使用默认Default Exchage
         * 参数2:路由key,简单模式可以传递队列名称
         * 参数3:消息其它属性
         * 参数4:消息内容
         */
        String message = "insert路由模式";
        channel.basicPublish(EXCHANGE_NAME,"insert",null,message.getBytes());
        String message1 = "update路由模式";
        channel.basicPublish(EXCHANGE_NAME,"update",null,message1.getBytes());
        System.out.println("已成功发送(insert路由模式消息):" + message);
        System.out.println("已成功发送(update路由模式消息):" + message1);
        //6.关闭资源
        channel.close();
        connection.close();
    }
}
  • 消费者
import com.coderwhat.util.RQConnectionFactory;
import com.rabbitmq.client.*;

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

/**
 * @Description: 路由模式消费者
 * @Date: 2021/4/11 17:01
 * @Author: Evan
 * @Version: v1.0.0
 */
public class MyDirectConsumer {

    //交换机名称
    private static final String EXCHANGE_NAME = "direct_exchange";
    //队列名称
    private static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接
        Connection connection = RQConnectionFactory.getConnection();
        //2.创建频道
        Channel channel = connection.createChannel();
        //3.声明当前消费者所绑定的队列
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         */
        channel.queueDeclare(DIRECT_QUEUE_INSERT,true,false,false,null);
        //4.声明将当前队列绑定到交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        channel.queueBind(DIRECT_QUEUE_INSERT,EXCHANGE_NAME,"insert");
        //5.创建消费者
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            /**
             * 消费消息方法需要重写
             * @param consumerTag 消费者标签,channel.basicConsume时可以指定
             * @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志
             *                 (收到消息失败后是否需要重新发送)
             * @param properties 属性信息
             * @param body 消息
             * @throws IOException
             */
            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("消费者1-接收到的消息为:" + new String(body, "UTF-8"));
            }
        };

        //6.监听并回调消费者
        /**
         * 参数1:队列名称
         * 参数2:是否自动确认,设置为true为表示消息接收到自动向mq回复接收到了,
         *       mq接收到回复会删除消息,设置为false则需要手动确认
         * 参数3:消息接收到后回调
         */
        channel.basicConsume(DIRECT_QUEUE_INSERT, true, consumer);
    }
}

3.5 通配符模式

通配符模式与路由模式一样,都是通过routing key指定匹配条件,不同的是通配符模式可以使用通配符,并且通配符有以下两种:
* :匹配一个单词,如a.*可以匹配a.b,a.c
# :匹配多个词,如 a.#可以匹配a.b,a.b.c
通配符模式能实现发布订阅模式、路由模式的功能,并且还能使用通配符,显得更灵活一些;
在这里插入图片描述
下列为生产者、消费者的步骤及示例代码:
当我们不再在生产者里声明队列并绑定交换机时,需要先将消费者运行起来才不会出现消息丢失的情况,因为生产者单纯将消息传递给交换机,其不会存储消息;

步骤生产者消费者
1获取连接获取连接
2创建频道创建频道
3声明交换机名称与类型(Topic)声明当前消费者绑定的消息队列
4定义并发送消息(指定routing key声明交换机名称与类型(Topic),并绑定队列(指定队列routing key通配符)
3关闭连接、通道资源创建消费者,设置消息处理方式(后续监听调用)
4监听队列
  • 生产者
import com.coderwhat.util.RQConnectionFactory;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * @Description: 通配符模式生产者
 * @Date: 2021/4/11 17:52
 * @Author: Evan
 * @Version: v1.0.0
 */
public class MyTopicProducer {
    //交换机名称
    private static final String EXCHANGE_NAME = "topic_exchange";
    //队列名称
    private static final String TOPIC_QUEUE_INSERT = "topic_queue_insert";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接
        Connection connection = RQConnectionFactory.getConnection();
        //2.创建频道
        Channel channel = connection.createChannel();
        //3.声明交换机
        // 当我们不再在生产者里声明队列时,需要先将消费者运行起来才不会出现消息丢失的情况,
        // 因为生产者单纯将消息传递给交换机,其不会存储消息
        /*
        *//**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         *//*
        channel.queueDeclare(DIRECT_QUEUE_INSERT,true,false,false,null);
        channel.queueDeclare(DIRECT_QUEUE_UPDATE,true,false,false,null);
        //3声明交换机 -> 将队列绑定到交换机并指定队列 routing key
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        channel.queueBind(TOPIC_QUEUE_INSERT,EXCHANGE_NAME,"item.insert");
        channel.queueBind(TOPIC_QUEUE_INSERT,EXCHANGE_NAME,"item.update");*/
        channel.exchangeDeclare(EXCHANGE_NAME,BuiltinExchangeType.TOPIC);
        //4.发布消息并指定消息的 routing key,后续将通过通配符接受 routing key
        /**
         * 参数1:交换机名称,如果没有指定则使用默认Default Exchage
         * 参数2:路由key,简单模式可以传递队列名称
         * 参数3:消息其它属性
         * 参数4:消息内容
         */
        String message = "insert通配符模式";
        channel.basicPublish(EXCHANGE_NAME,"item.insert",null,message.getBytes());
        String message1 = "update通配符模式";
        channel.basicPublish(EXCHANGE_NAME,"item.update",null,message1.getBytes());
        System.out.println("已成功发送(insert通配符模式消息):" + message);
        System.out.println("已成功发送(update通配符模式消息):" + message1);
        //5.关闭资源
        channel.close();
        connection.close();
    }
}
  • 消费者
import com.coderwhat.util.RQConnectionFactory;
import com.rabbitmq.client.*;

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

/**
 * @Description: 通配符模式消费者
 * @Date: 2021/4/11 17:51
 * @Author: Evan
 * @Version: v1.0.0
 */
public class MyTopicConsumer {
    //交换机名称
    private static final String EXCHANGE_NAME = "topic_exchange";
    //队列名称
    private static final String TOPIC_QUEUE_INSERT = "topic_queue_insert";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接
        Connection connection = RQConnectionFactory.getConnection();
        //2.创建频道
        Channel channel = connection.createChannel();
        //3.声明当前消费者所绑定的队列
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         */
        channel.queueDeclare(TOPIC_QUEUE_INSERT,true,false,false,null);
        //4.声明将当前队列绑定到交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //绑定队列与交换机,并声明 routing key 通配符,替换原来一个个声明路由的方式
        channel.queueBind(TOPIC_QUEUE_INSERT,EXCHANGE_NAME,"item.*");
        //channel.queueBind(TOPIC_QUEUE_INSERT,EXCHANGE_NAME,"item.insert");
        //channel.queueBind(TOPIC_QUEUE_INSERT,EXCHANGE_NAME,"item.update");
        //5.创建消费者
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            /**
             * 消费消息方法需要重写
             * @param consumerTag 消费者标签,channel.basicConsume时可以指定
             * @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志
             *                 (收到消息失败后是否需要重新发送)
             * @param properties 属性信息
             * @param body 消息
             * @throws IOException
             */
            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("消费者1-接收到的消息为:" + new String(body, "UTF-8"));
            }
        };

        //6.监听并回调消费者
        /**
         * 参数1:队列名称
         * 参数2:是否自动确认,设置为true为表示消息接收到自动向mq回复接收到了,
         *       mq接收到回复会删除消息,设置为false则需要手动确认
         * 参数3:消息接收到后回调
         */
        channel.basicConsume(TOPIC_QUEUE_INSERT, true, consumer);
    }
}

三、springboot集成

以通配器模式为示例,同样使用父模块管理依赖版本
todo:这里有一个小问题,就是junit5版本使用碰到一些问题,各种小异常,使用junit4则没问题

下列为生产者和消费者步骤列表:

步骤生产者消费者
1引入依赖,编写启动类引入依赖编写启动类
2编写配置类(声明队列、交换机、二者绑定)使用注解编写监听器
3编写测试类,发送消息并指定routing key

父依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

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

    <groupId>org.coderwhat</groupId>
    <artifactId>rabbitmq-demo-2</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>producer</module>
        <module>consumer</module>
    </modules>

    <properties>
        <java_version>1.8</java_version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-amqp</artifactId>
                <version>2.3.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>2.3.6.RELEASE</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <!-- 插件版本保持与父类一致 -->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java_version}</source>
                    <target>${java_version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.6.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>
                                <!--打包时引入第三方依赖生成 big jar -->
                                repackage
                            </goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

3.1 生产者

依赖套用即可,启动类较简单,省略

  • 生产者配置类
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description: 生产者配置类,用来声明队列、交换机,绑定队列和交换机
 * @Date: 2021/4/12 21:51
 * @Author: Evan
 * @Version: v1.0.0
 */
@Configuration
public class TopicRabbitMQConfig {
    //交换机名
    public static final String EXCHANGE_NAME = "sb_topic_exchange";
    //队列名
    public static final String TOPIC_QUEUE = "sb_topic_queue";

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

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

    //绑定队列与交换机,并声明通配符,无参数
    @Bean
    public Binding bindQueueExchange(
            @Qualifier("topicQueue") Queue queue,
            @Qualifier("topicExchange") Exchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with("item.*").noargs();
    }
}
  • 测试类
import com.coderwhat.rabbit.config.TopicRabbitMQConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @Description: topic生产者测试类
 * @Date: 2021/4/12 23:17
 * @Author: Evan
 * @Version: v1.0.0
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerApplicationTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void test(){
        rabbitTemplate.convertAndSend(
                TopicRabbitMQConfig.EXCHANGE_NAME,
                "item.insert","这个消息是insert");

        rabbitTemplate.convertAndSend(
                TopicRabbitMQConfig.EXCHANGE_NAME,
                "item.update","这个消息是update");

        rabbitTemplate.convertAndSend(
                TopicRabbitMQConfig.EXCHANGE_NAME,
                "item.delete","这个消息是delete");
    }

}

3.2 消费者

  • 监听类:
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @Description: 监听topic queue队列
 * @Date: 2021/4/12 23:09
 * @Author: Evan
 * @Version: v1.0.0
 */
@Component
public class MyListener {
    /**
     * 监听队列消息,队列名保持一致
     * @param message 接收到的消息
     */
    @RabbitListener(queues = "sb_topic_queue")
    public void topicListener(String message){
        System.out.println("接收到的消息为:" + message);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mitays

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值