RabbitMQ

MQ官网速递:https://www.rabbitmq.com/getstarted.html

MQ的优势

  1. 应用解耦:提高系统的容错性和可维护性;
  2. 异步提速:提升用户体验和系统吞吐量;
  3. 削峰填谷:提高系统稳定性。

MQ的劣势:

  1. 系统可用性降低:如果MQ服务器挂了,则关联业务都会受影响,需要保证MQ的高可用;
  2. 系统复杂度提高:各服务之间需要通过MQ进行异步调用,因此需要保证消息不丢失等情况。

常见的MQ产品

 

 AMQP:即Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端、中间件不同产品,不同的开发语言等条件的限制。2006年AMQP规范发布,类比HTTP。

RabbitMQ的基础架构图

 Broker:接收和分发消息的应用,RabbitMQ Server就是Message Broker。

Virtual Host:出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ Server提供的服务时,可以划分出多个vHost,每个用户在自己的vHost创建exchange/queue等。

Connection:publisher(producer)/consumer和broker之间的TCP连接。

Channel:如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection开销会很大,效率也很低。Channel是在Connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯。AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection极大地减少了操作系统建立TCP Connection的开销。

Exchange:message到达broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue中。常用的类型有direct(point-to-point)、topic(publish-subscribe)和fanout(multicast)。

Queue:消息最终被送到队列等待consumer消费。

Binding:exchange和queue之间的虚拟连接,binding中可以包含routing key,binding信息被保存到exchange中的查询表里,用于管理message分发。

 

RabbitMQ的消息确认机制

confirm:表示生产者把消息发送到broker时的状态,后续会出现两种情况:ack(表示broker已经接收数据)和nack(表示broker拒收消息,原因可能是队列已满、限流、IO异常等……)。

return:表示消息被broker正常接收(ack)后,但broker没有对应的队列,消息被退回给生产者。

这两种状态只代表生产者和broker之间的消息发送情况,与消费者是否接收、确认消息无关。

 

RabbitMQ的高级特性

  1. 消息端限流:消费端限制每次读取消息的数量。
  2. TTL:Time To Live,表示存活时间/过期时间。当消息到了过期时间后还没有被消费,消息就会被自动清除。RabbitMQ可以设置消息的过期时间,也可以给整个队列设置过期时间。
  3. 死信队列:DLX(Dead Letter Exchange),也就是死信交换机。死信就是存活时间到期还没有被消费、要被清除的消息,这类消息可以被重新发送到另一个交换机,这个交换机就是DLX。消息成为死信的几种情况:队列消息长度上限、消费者拒绝消费消息并且不重回队列(重发)、原队列存在消息过期设置且消息超时未被消费。
  4. 延时队列:消息进入队列后不会被立即消费,只有到达指定时间后才会被消费,比如需求下单后15分钟未支付就取消订单回滚库存或新用户注册7天后发信息问候就可以通过延时队列来实现。RabbitMQ没有提供延时队列的功能,但是可以通过TTL+死信队列组合使用实现延时队列的效果。
  5. 消息可靠性投递
  6. Consumer ACK

消息可靠性的要求

  1. 持久化
    1. exchange持久化
    2. queue持久化
    3. message持久化
  2. 生产者确认confirm
  3. 消费者确认ack
  4. broker高可用

消息积压的可能原因

  1. 消费者宕机积压
  2. 消费者消费能力不足积压
  3. 发送者发送流量太大

消息积压的解决方案

  1. 上线更多的消费者,提升消费能力;
  2. 上线专门的队列,把消息记录进数据库,再慢慢处理。

消息幂等性保障:幂等性指一次或多次请求某一个资源,对资源本身应具有同样的结果。在MQ中指消费多条相同的消息,结果都应该一致。可以通过乐观锁来保障幂等性。 

RabbitMQ提供了6种工作模式:简单模式、work queues(队列模式)、publish/subscribe(发布与订阅模式)、routing(路由模式)、topics(主题模式)、RPC(远程调用模式,远程调用不太算MQ)。

代码实现简单模式通信

引入依赖包

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

生产者

package com.example.rabbitmq.helloworld;

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

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

/**
 * 简单模式 发布者
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-09  14:49
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置主机信息
        connectionFactory.setHost("192.168.0.42");
        /*
            端口
            5672:RabbitMQ的通讯端口
            25672:RabbitMQ的节点间的CLI通讯端口
            15672:RabbitMQ HTTP_API的端口,管理员用户才能访问,用于管理RabbitMQ,需要启动Management插件。
            1883、8883:MQTT插件启动时的端口。
            61613、61614:STOMP客户端插件启用的时候的端口。
            15674、15675:基于webscoket的STOMP端口和MOTT端口。
         */
        connectionFactory.setPort(5672);
        // 这里最好使用建VirtualHost的用户登陆 否则可能找不到这个VirtualHost
        connectionFactory.setUsername("ywy");
        connectionFactory.setPassword("ywy");
        connectionFactory.setVirtualHost("/myHost");
        // 获取TCP长连接
        Connection connection = connectionFactory.newConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        Channel channel = connection.createChannel();
        /*
            如果队列名不存在 会自动创建
            P1:队列名称ID
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        final String QUEUE_NAME = "helloworld";
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 定义需要发送的消息
        String msg = "超多钱!";
        /*
            发送消息
            P1:交换机 简单模式不需要指定交换机
            P2:队列名称
            P3:其他参数
            P4:要发送的消息
         */
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes(StandardCharsets.UTF_8));
        // 关闭连接
        channel.close();
        connection.close();
        System.out.println("恭喜!消息发送成功!");
    }
}

消费者

package com.example.rabbitmq.helloworld;

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

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

/**
 * 简单模式 消费者
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-09  15:11
 */
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置主机信息
        connectionFactory.setHost("192.168.0.42");
        connectionFactory.setPort(5672);
        // 这里最好使用建VirtualHost的用户登陆 否则可能找不到这个VirtualHost
        connectionFactory.setUsername("ywy");
        connectionFactory.setPassword("ywy");
        connectionFactory.setVirtualHost("/myHost");
        // 获取TCP长连接
        Connection connection = connectionFactory.newConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        Channel channel = connection.createChannel();
        /*
            如果队列名不存在 会自动创建
            P1:队列名称ID
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        final String QUEUE_NAME = "helloworld";
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /*
            从MQ服务器中获取数据
            P1:队列名
            P2:是否自动确认收到消息,false表示需要手动编码确认消息(MQ推荐手动)。
            P3:DefaultConsumer的实现类对象 做消息处理
         */
        channel.basicConsume(QUEUE_NAME, false, new Receiver(channel));
    }
}

消息处理类

package com.example.rabbitmq.helloworld;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * 消费对象实现类 做消息处理
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-09  15:16
 */
public class Receiver extends DefaultConsumer {

    private Channel channel;

    /**
     * 重写构造函数 Channel通道对象需要从外层传入,在handleDelivery中需要用到
     *
     * @param channel
     */
    public Recevier(Channel channel) {
        super(channel);
        this.channel = channel;
    }

    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        String msg = new String(body);
        System.out.println("消费者接收到的消息:" + msg);
        System.out.println("消费者接收到的消息ID:" + envelope.getDeliveryTag());
        /*
            签收消息
            P1:消息ID
            P2:false表示只确认签收当前的消息 true表示签收该消费者所有未签收的消息
         */
        channel.basicAck(envelope.getDeliveryTag(), false);
    }
}

 代码实现队列模式通信

Rabbit工具类

 

package com.example.rabbitmq.helloworld;

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

/**
 * Rabbit工具类
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-09  16:41
 */
public class RabbitUtil {

    private static ConnectionFactory connectionFactory = new ConnectionFactory();

    static {
        // 设置主机信息
        connectionFactory.setHost("192.168.0.42");
        /*
            端口
            5672:RabbitMQ的通讯端口
            25672:RabbitMQ的节点间的CLI通讯端口
            15672:RabbitMQ HTTP_API的端口,管理员用户才能访问,用于管理RabbitMQ,需要启动Management插件。
            1883、8883:MQTT插件启动时的端口。
            61613、61614:STOMP客户端插件启用的时候的端口。
            15674、15675:基于webscoket的STOMP端口和MOTT端口。
         */
        connectionFactory.setPort(5672);
        // 这里最好使用建VirtualHost的用户登陆 否则可能找不到这个VirtualHost
        connectionFactory.setUsername("ywy");
        connectionFactory.setPassword("ywy");
        connectionFactory.setVirtualHost("myHost");
    }

    /**
     * 获取TCP连接
     *
     * @return com.rabbitmq.client.Connection
     * @author YangWanYi
     * @date 2022/12/9 16:33
     */
    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = connectionFactory.newConnection();
            return connection;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

RabbitMQ 常量类 

package com.example.rabbitmq.helloworld;

/**
 * Rabbit常量类
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-09  16:41
 */
public class RabbitConstant {
    public static final String QUEUE_HELLO_WORLD = "helloworld";
    public static final String EXCHANGE_NEWS = "news";
    public static final String EXCHANGE_DIRECT = "routingDirect";
    public static final String EXCHANGE_TOPIC = "topicExchange";
    public static final String QUEUE_PRO_SUB_1 = "proSub1";
    public static final String QUEUE_PRO_SUB_2 = "proSub2";
    public static final String QUEUE_ROUTING_1 = "routing1";
    public static final String QUEUE_ROUTING_2 = "routing2";
}

 

队列模式 生产者

package com.example.rabbitmq.workqueue;

import com.alibaba.fastjson.JSONObject;
import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * 队列模式 生产者
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-09  16:41
 */
public class Producer {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        Channel channel = connection.createChannel();
        /*
            如果队列名不存在 会自动创建
            P1:队列名称ID
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        channel.queueDeclare(RabbitConstant.QUEUE_HELLO_WORLD, false, false, false, null);
        int times = 100;
        for (int i = 1; i < times; i++) {
            // 封装要发送的消息
            MessageVo messageVo = new MessageVo("骑手您好!", "平安路" + i + "号", "您有一个新的订单待配送!");
            String msg = JSONObject.toJSONString(messageVo);
            /*
                发送消息
                P1:交换机 队列模式不需要指定交换机
                P2:队列名称
                P3:其他参数
                P4:要发送的消息
             */
            channel.basicPublish("", RabbitConstant.QUEUE_HELLO_WORLD, null, msg.getBytes(StandardCharsets.UTF_8));
        }
        System.out.println("队列模式,消息发送成功!");
        channel.close();
        connection.close();
    }
}

队列模式 -消费者 消费者代码一样 可以多写几个一起消费来模拟队列消费

package com.example.rabbitmq.workqueue;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * 队列模式 消费者1号
 * 队列模式的多个消费者都是共同消费的同一个队列的消息
 * @author YangWanYi
 * @version 1.0
 * @date 2022/12/10 12:46
 */
public class Consumer1 {

    public static void main(String[] args) throws IOException {
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
          /*
            如果队列名不存在 会自动创建
            P1:队列名称ID
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        channel.queueDeclare(RabbitConstant.QUEUE_HELLO_WORLD, false, false, false, null);
        /*
            如果不写basicQos(1),MQ会自动把所有的消息平均发给所有的消费者。
            写了,MQ不再对消费者一次性发送多个消息,而是消费者处理完一个消息后(确认后),再从队列里获取一个新的。
         */
        channel.basicQos(1);
         /*
            从MQ服务器中获取数据
            P1:队列名
            P2:是否自动确认收到消息,false表示需要手动编码确认消息(MQ推荐手动)。
            P3:DefaultConsumer的实现类对象 做消息处理
         */
        channel.basicConsume(RabbitConstant.QUEUE_HELLO_WORLD, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 处理消息
                String msg = new String(body);
                System.out.println("消费者1号收到消息:" + msg);
                 /*
                    签收消息
                    P1:消息ID
                    P2:false表示只确认签收当前的消息 true表示签收该消费者所有未签收的消息
                 */
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }

}

 代码实现发布订阅模式通信

发布订阅模式 生产者

package com.example.rabbitmq.pubsub;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * 发布订阅模式 生产者
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-10  13:28
 */
public class Producer {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 获取输入信息
        String next = new Scanner(System.in).next();
        // 创建通信通道 相当于TCP中的虚拟连接
        Channel channel = connection.createChannel();
        /*
            发送消息
            P1:交换机名称
            P2:队列名称 发布订阅模式在发布消息时不需要指定队列名,在消费端指定队列名即可
            P3:其他参数
            P4:要发送的消息
         */
        channel.basicPublish(RabbitConstant.EXCHANGE_NEWS, "", null, next.getBytes(StandardCharsets.UTF_8));
        // 关闭连接
        channel.close();
        connection.close();
    }
}

发布订阅模式 消费者1号 不同的消费者同一个交换机绑定不同的队列名,生产者会通过交换机给已绑定的不同的队列发送同样的消息。

package com.example.rabbitmq.pubsub;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * 发布订阅模式 消费者1号
 * 发布订阅模式的多个消费者都分别消费自己绑定队列的消息
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-10  13:37
 */
public class Consumer1 {

    public static void main(String[] args) throws IOException {
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        /*
            声明队列
            P1:队列名称 如果队列名不存在 会自动创建
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        channel.queueDeclare(RabbitConstant.QUEUE_PRO_SUB_1, false, false, false, null);
        /*
            绑定交换机
            P1:队列名
            P2:交换机名 必须先创建好,否则会报错
            P3:路由key 发布订阅模式还用不到这个参数
         */
        channel.queueBind(RabbitConstant.QUEUE_PRO_SUB_1, RabbitConstant.EXCHANGE_NEWS, "");
         /*
            如果不写basicQos(1),MQ会自动把所有的消息平均发给所有的消费者。
            写了,MQ不再对消费者一次性发送多个消息,而是消费者处理完一个消息后(确认后),再从队列里获取一个新的。
         */
        channel.basicQos(1);
         /*
            从MQ服务器中获取数据
            P1:队列名
            P2:是否自动确认收到消息,false表示需要手动编码确认消息(MQ推荐手动)。
            P3:DefaultConsumer的实现类对象 做消息处理
         */
        channel.basicConsume(RabbitConstant.QUEUE_PRO_SUB_1, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 处理消息
                String msg = new String(body);
                System.out.println("发布订阅模式消费者1号收到消息:" + msg);
                 /*
                    签收消息
                    P1:消息ID
                    P2:false表示只确认签收当前的消息 true表示签收该消费者所有未签收的消息
                 */
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

发布订阅模式 消费者2号 不同的消费者同一个交换机绑定不同的队列名,生产者会通过交换机给已绑定的不同的队列发送同样的消息。

package com.example.rabbitmq.pubsub;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * 发布订阅模式 消费者2号
 * 发布订阅模式的多个消费者都分别消费自己绑定队列的消息
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-10  13:37
 */
public class Consumer2 {

    public static void main(String[] args) throws IOException {
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        /*
            声明队列
            P1:队列名称 如果队列名不存在 会自动创建
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        channel.queueDeclare(RabbitConstant.QUEUE_PRO_SUB_2, false, false, false, null);
        /*
            绑定交换机
            P1:队列名
            P2:交换机名 必须先创建好,否则会报错。发布订阅模式的交换机类型是fanout(广播)。
            P3:路由key 发布订阅模式还用不到这个参数
         */
        channel.queueBind(RabbitConstant.QUEUE_PRO_SUB_2, RabbitConstant.EXCHANGE_NEWS, "");
         /*
            如果不写basicQos(1),MQ会自动把所有的消息平均发给所有的消费者。
            写了,MQ不再对消费者一次性发送多个消息,而是消费者处理完一个消息后(确认后),再从队列里获取一个新的。
         */
        channel.basicQos(1);
         /*
            从MQ服务器中获取数据
            P1:队列名
            P2:是否自动确认收到消息,false表示需要手动编码确认消息(MQ推荐手动)。
            P3:DefaultConsumer的实现类对象 做消息处理
         */
        channel.basicConsume(RabbitConstant.QUEUE_PRO_SUB_2, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 处理消息
                String msg = new String(body);
                System.out.println("发布订阅模式消费者2号收到消息:" + msg);
                 /*
                    签收消息
                    P1:消息ID
                    P2:false表示只确认签收当前的消息 true表示签收该消费者所有未签收的消息
                 */
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

代码实现路由模式通信

路由模式 生产者 发送消息时需要指定routing key。

package com.example.rabbitmq.routing;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * 路由模式 生产者
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022/12/10 21:43
 */
public class Producer {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 模拟要发送的消息 key:routing-key value:消息详情
        Map<String, String> messages = new HashMap<>(10);
        int times = 10;
        for (int i = 0; i < times; i++) {
            messages.put("routing-key" + (i + 1), "消息详情" + (i + 1));
        }
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        Channel channel = connection.createChannel();
        Iterator<Map.Entry<String, String>> iterator = messages.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            /*
                发送消息
                P1:交换机名称
                P2:routing key
                P3:其他参数
                P4:要发送的消息
             */
            channel.basicPublish(RabbitConstant.EXCHANGE_DIRECT, entry.getKey(), null, entry.getValue().getBytes(StandardCharsets.UTF_8));
        }
        // 关闭连接
        channel.close();
        connection.close();
    }
}

路由模式 消费者1号

package com.example.rabbitmq.routing;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * 路由模式 消费者1号
 * 路由模式消费自己指定routing key的消息
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022/12/10 21:43
 */
public class Consumer1 {

    public static void main(String[] args) throws IOException {
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        /*
            声明队列
            P1:队列名称 如果队列名不存在 会自动创建
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        channel.queueDeclare(RabbitConstant.QUEUE_ROUTING_1, false, false, false, null);
        /*
            绑定交换机
            P1:队列名
            P2:交换机名 必须先创建好,否则会报错。路由模式的交换机类型是direct(point-to-point)。
            P3:路由key 发布订阅模式还用不到这个参数
         */
        channel.queueBind(RabbitConstant.QUEUE_ROUTING_1, RabbitConstant.EXCHANGE_DIRECT, "routing-key5");
        channel.queueBind(RabbitConstant.QUEUE_ROUTING_1, RabbitConstant.EXCHANGE_DIRECT, "routing-key6");
        channel.queueBind(RabbitConstant.QUEUE_ROUTING_1, RabbitConstant.EXCHANGE_DIRECT, "routing-key7");
         /*
            如果不写basicQos(1),MQ会自动把所有的消息平均发给所有的消费者。
            写了,MQ不再对消费者一次性发送多个消息,而是消费者处理完一个消息后(确认后),再从队列里获取一个新的。
         */
        channel.basicQos(1);
         /*
            从MQ服务器中获取数据
            P1:队列名
            P2:是否自动确认收到消息,false表示需要手动编码确认消息(MQ推荐手动)。
            P3:DefaultConsumer的实现类对象 做消息处理
         */
        channel.basicConsume(RabbitConstant.QUEUE_ROUTING_1, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 处理消息
                String msg = new String(body);
                System.out.println("路由模式消费者1号收到消息:" + msg);
                 /*
                    签收消息
                    P1:消息ID
                    P2:false表示只确认签收当前的消息 true表示签收该消费者所有未签收的消息
                 */
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

路由模式 消费者2号

package com.example.rabbitmq.routing;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * 路由模式 消费者2号
 * 路由模式消费自己指定routing key的消息
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022/12/10 21:43
 */
public class Consumer2 {

    public static void main(String[] args) throws IOException {
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        /*
            声明队列
            P1:队列名称 如果队列名不存在 会自动创建
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        channel.queueDeclare(RabbitConstant.QUEUE_ROUTING_2, false, false, false, null);
        /*
            绑定交换机
            P1:队列名
            P2:交换机名 必须先创建好,否则会报错。路由模式的交换机类型是direct(point-to-point)。
            P3:路由key 发布订阅模式还用不到这个参数
         */
        channel.queueBind(RabbitConstant.QUEUE_ROUTING_2, RabbitConstant.EXCHANGE_DIRECT, "routing-key1");
        channel.queueBind(RabbitConstant.QUEUE_ROUTING_2, RabbitConstant.EXCHANGE_DIRECT, "routing-key2");
        channel.queueBind(RabbitConstant.QUEUE_ROUTING_2, RabbitConstant.EXCHANGE_DIRECT, "routing-key3");
         /*
            如果不写basicQos(1),MQ会自动把所有的消息平均发给所有的消费者。
            写了,MQ不再对消费者一次性发送多个消息,而是消费者处理完一个消息后(确认后),再从队列里获取一个新的。
         */
        channel.basicQos(1);
         /*
            从MQ服务器中获取数据
            P1:队列名
            P2:是否自动确认收到消息,false表示需要手动编码确认消息(MQ推荐手动)。
            P3:DefaultConsumer的实现类对象 做消息处理
         */
        channel.basicConsume(RabbitConstant.QUEUE_ROUTING_2, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 处理消息
                String msg = new String(body);
                System.out.println("路由模式消费者2号收到消息:" + msg);
                 /*
                    签收消息
                    P1:消息ID
                    P2:false表示只确认签收当前的消息 true表示签收该消费者所有未签收的消息
                 */
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

代码实现主题模式通信

主题模式 生产者

package com.example.rabbitmq.topic;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * 主题模式 生产者
 * 主题模式的交换机类型是topic
 * @author YangWanYi
 * @version 1.0
 * @date 2022/12/10 22:29
 */
public class Producer {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 模拟要发送的消息 key:routing-key value:消息详情
        Map<String, String> messages = new HashMap<>(10);
        int times = 10;
        for (int i = 0; i < times; i++) {
            messages.put("routing.key." + (i + 1), "消息详情" + (i + 1));
        }
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        Channel channel = connection.createChannel();
        Iterator<Map.Entry<String, String>> iterator = messages.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            /*
                发送消息
                P1:交换机名称
                P2:routing key
                P3:其他参数
                P4:要发送的消息
             */
            channel.basicPublish(RabbitConstant.EXCHANGE_TOPIC, entry.getKey(), null, entry.getValue().getBytes(StandardCharsets.UTF_8));
        }
        // 关闭连接
        channel.close();
        connection.close();
    }
}

主题模式 消费者1号

package com.example.rabbitmq.topic;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * 主题模式 消费者1号
 * topic模式与routing模式都可以根据routing key把消息路由到不同的队列,
 * 只是topic类型的交换机可以让队列在绑定routing key的时候使用通配符。
 * 通配符规则:
 * # 匹配一个或多个词;
 * * 匹配一个词。
 * 比如text.#能匹配text.abc或text.abc.123,text.*只能匹配text.abc。
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022/12/10 22:29
 */
public class Consumer1 {

    public static void main(String[] args) throws IOException {
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        /*
            声明队列
            P1:队列名称 如果队列名不存在 会自动创建
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        channel.queueDeclare(RabbitConstant.QUEUE_ROUTING_1, false, false, false, null);
        /*
            绑定交换机
            P1:队列名
            P2:交换机名 必须先创建好,否则会报错。主题模式的交换机类型是topics
            P3:路由key 发布订阅模式还用不到这个参数
         */
        channel.queueBind(RabbitConstant.QUEUE_ROUTING_1, RabbitConstant.EXCHANGE_TOPIC, "routing.#");
         /*
            如果不写basicQos(1),MQ会自动把所有的消息平均发给所有的消费者。
            写了,MQ不再对消费者一次性发送多个消息,而是消费者处理完一个消息后(确认后),再从队列里获取一个新的。
         */
        channel.basicQos(1);
         /*
            从MQ服务器中获取数据
            P1:队列名
            P2:是否自动确认收到消息,false表示需要手动编码确认消息(MQ推荐手动)。
            P3:DefaultConsumer的实现类对象 做消息处理
         */
        channel.basicConsume(RabbitConstant.QUEUE_ROUTING_1, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 处理消息
                String msg = new String(body);
                System.out.println("主题模式消费者1号收到消息:" + msg);
                 /*
                    签收消息
                    P1:消息ID
                    P2:false表示只确认签收当前的消息 true表示签收该消费者所有未签收的消息
                 */
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

主题模式 消费者2号

package com.example.rabbitmq.topic;

import com.example.rabbitmq.helloworld.RabbitConstant;
import com.example.rabbitmq.helloworld.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * 主题模式 消费者2号
 * topic模式与routing模式都可以根据routing key把消息路由到不同的队列,
 * 只是topic类型的交换机可以让队列在绑定routing key的时候使用通配符。
 * 通配符规则:
 * # 匹配一个或多个词;
 * * 匹配一个词。
 * 比如text.#能匹配text.abc或text.abc.123,text.*只能匹配text.abc。
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022/12/10 22:29
 */
public class Consumer2 {

    public static void main(String[] args) throws IOException {
        // 获取TCP长连接
        Connection connection = RabbitUtil.getConnection();
        // 创建通信通道 相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        /*
            声明队列
            P1:队列名称 如果队列名不存在 会自动创建
            P2:是否持久化,false表示不持久化数据,MQ停掉数据就会丢失。
            P3:是否队列私有化,false表示所有消费者都可以访问,true表示只有第一次拥有该队列的消费者才能一直使用。
            P4:是否自动删除,false表示连接停掉后不自动删除这个队列。
            P5:其他参数
         */
        channel.queueDeclare(RabbitConstant.QUEUE_ROUTING_2, false, false, false, null);
        /*
            绑定交换机
            P1:队列名
            P2:交换机名 必须先创建好,否则会报错。路由模式的交换机类型是direct(point-to-point)。
            P3:路由key 发布订阅模式还用不到这个参数
         */
        channel.queueBind(RabbitConstant.QUEUE_ROUTING_2, RabbitConstant.EXCHANGE_TOPIC, "*.*.1");
         /*
            如果不写basicQos(1),MQ会自动把所有的消息平均发给所有的消费者。
            写了,MQ不再对消费者一次性发送多个消息,而是消费者处理完一个消息后(确认后),再从队列里获取一个新的。
         */
        channel.basicQos(1);
         /*
            从MQ服务器中获取数据
            P1:队列名
            P2:是否自动确认收到消息,false表示需要手动编码确认消息(MQ推荐手动)。
            P3:DefaultConsumer的实现类对象 做消息处理
         */
        channel.basicConsume(RabbitConstant.QUEUE_ROUTING_2, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 处理消息
                String msg = new String(body);
                System.out.println("主题模式消费者2号收到消息:" + msg);
                 /*
                    签收消息
                    P1:消息ID
                    P2:false表示只确认签收当前的消息 true表示签收该消费者所有未签收的消息
                 */
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

Spring整合RabbitMQ

生产者客户端

POM依赖

 

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

 

配置文件 rabbitmq.properties

rabbitmq.host=192.168.0.42
rabbitmq.port=5672
rabbitmq.username=ywy
rabbitmq.password=ywy
rabbitmq.virtual-host=myHost

配置文件 spring-rabbitmq-producer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    <!--加载配置文件-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- 定义rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>
    <!--定义管理交换机、队列 有这行配置,下边配置的交换机和队列都会自动创建。-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机
    默认交换机类型为direct,名字为:"",路由键为队列的名称
    -->
    <!--
        id:bean的名称
        name:queue的名称
        auto-declare:自动创建
        auto-delete:自动删除。 最后一个消费者和该队列断开连接后,自动删除队列
        durable:是否持久化
    -->

    <rabbit:queue id="spring_queue" name="spring_queue"    auto-declare="true"/>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~广播;所有队列都能收到消息~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true"/>

    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/>

    <!--定义广播类型交换机;并绑定上述两个队列-->
    <rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange"  auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding  queue="spring_fanout_queue_1"  />
            <rabbit:binding queue="spring_fanout_queue_2"/>
        </rabbit:bindings>
    </rabbit:fanout-exchange>


    <!-- 定义队列-->
    <rabbit:queue id="spring_direct_queue" name="spring_direct_queue"  auto-declare="true"/>

    <!--
      定义 Routing  路由模式 交互机
    -->
    <rabbit:direct-exchange name="spring_direct_exchange" >
        <rabbit:bindings>
            <!--direct 类型的交换机绑定队列  key :路由key  queue:队列名称-->
            <rabbit:binding queue="spring_direct_queue" key="info"></rabbit:binding>
        </rabbit:bindings>

    </rabbit:direct-exchange>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~通配符;*匹配一个单词,#匹配多个单词 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_topic_queue_star" name="spring_topic_queue_star"  auto-declare="true"/>
    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_topic_queue_well" name="spring_topic_queue_well" auto-declare="true"/>
    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_topic_queue_well2" name="spring_topic_queue_well2" auto-declare="true"/>

    <!--
      声明  topic 类型的交换机
    -->
    <rabbit:topic-exchange id="spring_topic_exchange"  name="spring_topic_exchange" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding pattern="ywy.*"  queue="spring_topic_queue_star"/>
            <rabbit:binding pattern="ywy.#" queue="spring_topic_queue_well"/>
            <rabbit:binding pattern="test.#" queue="spring_topic_queue_well2"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>

消息发送测试类

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.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * 发送消息测试
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-10  23:49
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class SendMsgTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Test
    public void testHelloWorld() {
        // 简单模式 发消息
        rabbitTemplate.convertAndSend("spring_queue", "hello world spring....");
    }

    @Test
    public void testFanout() {
        // 队列模式 发消息
        rabbitTemplate.convertAndSend("spring_fanout_exchange", "", "spring fanout....");
    }

    @Test
    public void testDirect() {
        // 路由模式 发消息
        rabbitTemplate.convertAndSend("spring_direct_exchange", "info", "spring Direct....");
    }

    @Test
    public void testTopics() {
        // 主题模式 发消息
        rabbitTemplate.convertAndSend("spring_topic_exchange", "baiqi.hehe.haha", "spring topic....");
    }
}

消费者客户端

POM依赖【同上】

配置文件 rabbitmq.properties【同上】

配置文件 spring-rabbitmq-consumer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    <!--加载配置文件-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- 定义rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>

    <bean id="springQueueListener" class="com.ywy.rabbitmq.listener.SpringQueueListener"/>
    <bean id="fanoutListener1" class="com.ywy.rabbitmq.listener.FanoutListener"/>
    <!-- <bean id="fanoutListener2" class="com.ywy.rabbitmq.listener.FanoutListener2"/>
    <bean id="topicListenerStar" class="com.ywy.rabbitmq.listener.TopicListenerStar"/>
    <bean id="topicListenerWell" class="com.ywy.rabbitmq.listener.TopicListenerWell"/>
    <bean id="topicListenerWell2" class="com.ywy.rabbitmq.listener.TopicListenerWell2"/>
-->
    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
       <rabbit:listener ref="springQueueListener" queue-names="spring_queue"/>
        <rabbit:listener ref="fanoutListener1" queue-names="spring_fanout_queue_1"/>
        <!--<rabbit:listener ref="fanoutListener2" queue-names="spring_fanout_queue_2"/>
        <rabbit:listener ref="topicListenerStar" queue-names="spring_topic_queue_star"/>
        <rabbit:listener ref="topicListenerWell" queue-names="spring_topic_queue_well"/>
        <rabbit:listener ref="topicListenerWell2" queue-names="spring_topic_queue_well2"/>-->
    </rabbit:listener-container>
</beans>

接收消息测试类

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 消费端测试
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-11  00:32
 */
public class ConsumerTest {
    public static void main(String[] args) {
        // 初始化IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring-rabbitmq-consumer.xml");
    }
}

简单模式 消息监听类 其他模式一样,只需要实现监听接口MessageListener即可。 

package com.ywy.rabbitmq.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

/**
 * 简单模式消费者客户端监听器
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-11  00:28
 */
public class SpringQueueListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
        System.out.println("简单模式消费者客户端收到消息:" + new String(message.getBody()));
    }
}

SpringBoot整合RabbitMQ

生产者客户端

YML文件配置

# 配置RabbitMQ的基本信息
spring:
  rabbitmq:
    host: 192.168.0.42
    port: 5672
    username: ywy
    password: ywy
    virtual-host: myHost

POM依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.1.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.1.14.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>    
RabbitMQ配置文件
package com.example.rabbitmq.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;

/**
 * RabbitMQ配置文件
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-11  14:57
 */
@Configuration
public class RabbitMqConfig {

    /**
     * 定义交换机名称
     */
    public static final String EXCHANGE_NAME = "boot_topic_exchange";

    /**
     * 定义队列名称
     */
    public static final String QUEUE_NAME = "boot_queue";

    /**
     * 声明交换机
     *
     * @return org.springframework.amqp.core.Exchange
     * @author YangWanYi
     * @date 2022/12/11 15:04
     */
    @Bean
    public Exchange declareExchange() {
        // P1:交换机名称 P2:是否持久化
        return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
    }

    /**
     * 声明队列
     *
     * @return org.springframework.amqp.core.Queue
     * @author YangWanYi
     * @date 2022/12/11 15:07
     */
    @Bean
    public Queue declareQueue() {
        return QueueBuilder.durable(QUEUE_NAME).build();
    }

    /**
     * 绑定队列与交换机
     *
     * @param queue    队列
     * @param exchange 交换机
     * @return org.springframework.amqp.core.Binding
     * @author YangWanYi
     * @date 2022/12/11 15:13
     */
    @Bean
    public Binding bindQueueAndExchange(@Qualifier("declareQueue") Queue queue, @Qualifier("declareExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
    }

}

测试消息发送

package com.example.springbootrabbitmqproducer;

import com.example.rabbitmq.config.RabbitMqConfig;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootRabbitmqProducerApplicationTests {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void sendMSg() {
        this.rabbitTemplate.convertAndSend(RabbitMqConfig.EXCHANGE_NAME,"boot.money","超多钱超多钱……");
    }

}

消费者客户端

YML文件配置【同上】

POM依赖【同上】

消息监听类

package com.example.rabbitmq.listener;

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

/**
 * RabbitMQ监听器
 *
 * @author YangWanYi
 * @version 1.0
 * @date 2022-12-11  15:54
 */
@Component
public class RabbitMqListener {

    /**
     * 监听消息
     *
     * @param msg 收到的消息
     * @return void
     * @author YangWanYi
     * @date 2022/12/11 15:56
     */
    @RabbitListener(queues = "boot_queue")
    public void listenQueue(Message msg) {
        System.out.println("springboot消费端收到消息:" + msg);
    }

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2022-12-09 18:23  敲代码的小浪漫  阅读(24)  评论(0编辑  收藏  举报