RabbitMQ-SpringBoot 案例

RabbitMQ-SpringBoot 案例

00、环境搭建

1、IDEA创建生产者工程:springboot-rabbitmq-producer
2、IDEA创建消费者工程:springboot-rabbitmq-consumer
3、生产者和消费者分别引入spring-boot-rabbitmq的依赖和配置连接信息
4、进行消息的分发和测试
5、查看和观察web控制台的状况

1、首先使用IDEA创建SpringBoot项目(步骤略)

2、同时引入如下依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

3、生产者:application.properties文件配置

server.port=8080
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672

4、生产者:application.properties文件配置

server.port=8082
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672

01、Simple 模式

1、创建队列(生产者消费者同时定义最好)

package com.example.config;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 简单队列模式
 **/
@Configuration
public class SimpleRabbitConfig {
    @Bean
    public Queue simpleQueue() {
        /*
         * durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
         * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
         * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
         * return new Queue("TestDirectQueue",true,true,false);
         * 一般设置一下队列的持久化就好,其余两个就是默认false
         */
        return new Queue("simple.queue", true);
    }
}

2、生产者发送消息

    /**
     * 简单模式
     */
    @Test
    public void simpleSend() {
        // 简单对列的情况下routingKey即为Q名
        this.rabbitTemplate.convertAndSend("simple.queue", "点对点Simple模式测试");
    }

3、消费者

package com.example.service;

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

@Component
public class SimpleConsumer {
    //监听的队列名
    @RabbitListener(queues = "simple.queue")
    public void processOne(String name) {
        System.out.println("simple.queue 收到消息:" + name);
    }
}

4、消费者收到测试结果:

simple.queue 收到消息:点对点Simple模式测试

02、Work 模式

02-1、轮询分发模式

轮询分发、平均消费模式:2个消费者队列,轮训获取队列里的消息

1、创建队列(生产者消费者同时定义最好)

package com.example.config;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 工作队列模式
 **/
@Configuration
public class WorkRabbitConfig {
    @Bean
    public Queue workQueue(){
        /*
         * durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
         * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
         * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
         * return new Queue("TestDirectQueue",true,true,false);
         * 一般设置一下队列的持久化就好,其余两个就是默认false
         */
        return new Queue("work.queue",true);
    }
}

2、生产者发送消息

    /**
     * 工作队列模式
     */
    @Test
    public void workSend() {
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend("work.queue", "工作模式队列:" + i);
        }
    }

3、消费者-1(慢消费者)

package com.example.service;

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

/**
 * 慢消费者,模拟性能比较差的服务器
 */
@Component
public class WorkConsumer1 {
    @RabbitListener(queues = "work.queue")
    public void processOne(String name, Channel channel, Message message) throws Exception {
        // 模拟执行每次任务需要1秒
        Thread.sleep(1000);
        System.out.println("WorkConsumer1-慢消费者 收到消息:" + name);
    }
}

4、消费者-2(快消费者)

package com.example.service;

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

/**
 * 快消费者,模拟性能比较好的服务器
 */
@Component
public class WorkConsumer2 {
    @RabbitListener(queues = "work.queue")
    public void processTwo(String name, Channel channel, Message message) throws Exception {
        System.out.println("WorkConsumer2-快消费者 收到消息:" + name);
    }
}

5、消费者收到测试结果:

WorkConsumer2-快消费者 收到消息:工作模式队列:1
WorkConsumer2-快消费者 收到消息:工作模式队列:3
WorkConsumer2-快消费者 收到消息:工作模式队列:5
WorkConsumer2-快消费者 收到消息:工作模式队列:7
WorkConsumer2-快消费者 收到消息:工作模式队列:9
WorkConsumer1-慢消费者 收到消息:工作模式队列:0
WorkConsumer1-慢消费者 收到消息:工作模式队列:2
WorkConsumer1-慢消费者 收到消息:工作模式队列:4
WorkConsumer1-慢消费者 收到消息:工作模式队列:6
WorkConsumer1-慢消费者 收到消息:工作模式队列:8

可以看到效果,两个消费者都分别获取到了5条消息,但是问题来了,消费者-2 很快的消费完了自己的5条消息,然后就闲置了。但是消费者-1执行很慢,执行5条消息,就需要大于5秒的时间,这样就造成了性能浪费。我们应该让RabbitMQ智能一些,给每个消费者每次只给一个消息,当确认消息完成之后再下发另一个消息,这样就可以能者多劳了。

02-2、能者多劳模式

确认机制和能者多劳消费模式:2个消费者队列,手动确认接收消息,消费者1加了睡眠时间,用来模拟消费消息慢

1、修改配置文件增加配置

# 在单个请求中处理的消息个数,每个消费者每次监听时可拉取处理的消息数量
spring.rabbitmq.listener.simple.prefetch=1
# 消费手动确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual

**备注:**实际上不加手动ack消息确认也可以实现。不过 一般来说会两个配置同时加上

2、生产者发送消息不变

    /**
     * 工作队列模式
     */
    @Test
    public void workSend() {
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend("work.queue", "工作模式队列:" + i);
        }
    }

2、消费者-1(慢消费者)

package com.example.service;

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

/**
 * 慢消费者,模拟性能比较差的服务器
 */
@Component
public class WorkConsumer1 {
    @RabbitListener(queues = "work.queue")
    public void processOne(String name, Channel channel, Message message) throws Exception {
        // 模拟执行每次任务需要1秒
        Thread.sleep(1000);
        System.out.println("WorkConsumer1-慢消费者 收到消息:" + name);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
}

3、消费者-2(快消费者)

package com.example.service;

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

/**
 * 快消费者,模拟性能比较好的服务器
 */
@Component
public class WorkConsumer2 {
    @RabbitListener(queues = "work.queue")
    public void processTwo(String name, Channel channel, Message message) throws Exception {
        System.out.println("WorkConsumer2-快消费者 收到消息:" + name);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
}

4、消费者收到测试结果:

WorkConsumer2-快消费者 收到消息:工作模式队列:1
WorkConsumer2-快消费者 收到消息:工作模式队列:2
WorkConsumer2-快消费者 收到消息:工作模式队列:3
WorkConsumer2-快消费者 收到消息:工作模式队列:4
WorkConsumer2-快消费者 收到消息:工作模式队列:5
WorkConsumer2-快消费者 收到消息:工作模式队列:6
WorkConsumer2-快消费者 收到消息:工作模式队列:7
WorkConsumer2-快消费者 收到消息:工作模式队列:8
WorkConsumer2-快消费者 收到消息:工作模式队列:9
WorkConsumer1-慢消费者 收到消息:工作模式队列:0

可以看到,已经达到我们想要的效果了,消费-2者消费了9条消息,而消费者-1 只消费了一条消息。

03、Fanout 模式

1、创建队列、交换机、绑定关系(生产者消费者同时定义最好)

package com.example.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Fanout 订阅模式
 **/
@Configuration
public class FanoutRabbitMQConfig {
    /**
     * 声明队列
     */
    @Bean
    public Queue fanoutQueueOne() {
        /*
         * durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
         * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
         * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
         * return new Queue("TestFanoutQueue",true,true,false);
         * 一般设置一下队列的持久化就好,其余两个就是默认false
         */
        return new Queue("fanout.queue.one", true);
    }

    @Bean
    public Queue fanoutQueueTwo() {
        return new Queue("fanout.queue.two", true);
    }
    @Bean
    public Queue fanoutQueueThree() {
        return new Queue("fanout.queue.three", true);
    }

    /**
     * 创建 Fanout 模式交换机
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        /*
         * param1: 交换机名称
         * param2: durable:是否持久化,RabbitMQ关闭后,没持久化Exchange将被清除
         * param3: autoDelete:是否自动删除,,如果没有与之绑定的Queue直接删除
         */
        return new FanoutExchange("fanout_exchange", true, false);
    }

    /**
     * 绑定队列(不用指定routing key),参数名字要和bean名字一致
     */
    @Bean
    public Binding bindingFanout1() {
        return BindingBuilder.bind(fanoutQueueOne()).to(fanoutExchange());
    }
    @Bean
    public Binding bindingFanout2() {
        return BindingBuilder.bind(fanoutQueueTwo()).to(fanoutExchange());
    }
    @Bean
    public Binding bindingFanout3() {
        return BindingBuilder.bind(fanoutQueueThree()).to(fanoutExchange());
    }
}

2、生产者发送消息

    /**
     * 发布订阅模式
     */
    @Test
    public void fanoutSend() {
        // 发布订阅模式下routingKey为空
        this.rabbitTemplate.convertAndSend("fanout_exchange","","Fanout-发布订阅模式测试");
    }

3、消费者

package com.example.service;

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

@Component
public class FanoutConsumer {

    @RabbitListener(queues = "fanout.queue.one")
    public void processEmail(String name) {
        System.out.println("fanout.queue.one 收到消息:" + name);
    }

    @RabbitListener(queues = "fanout.queue.two")
    public void processSms(String name) {
        System.out.println("fanout.queue.two 收到消息:" + name);
    }

    @RabbitListener(queues = "fanout.queue.three")
    public void processWeiXin(String name) {
        System.out.println("fanout.queue.three 收到消息:" + name);
    }
}

5、消费者收到测试结果:

fanout.queue.one 收到消息:Fanout-发布订阅模式测试
fanout.queue.three 收到消息:Fanout-发布订阅模式测试
fanout.queue.two 收到消息:Fanout-发布订阅模式测试

04、Direct 模式

1、创建队列、交换机、绑定关系

package com.example.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Direct 路由模式
 **/
@Configuration
public class DirectRabbitMQConfig {
    /**
     * 声明队列
     */
    @Bean
    public Queue directQueueOne() {
        /*
         * durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
         * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
         * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
         * return new Queue("TestDirectQueue",true,true,false);
         * 一般设置一下队列的持久化就好,其余两个就是默认false
         */
        return new Queue("direct.queue.one", true);
    }
    @Bean
    public Queue directQueueTwo() {
        return new Queue("direct.queue.two", true);
    }
    @Bean
    public Queue directQueueThree() {
        return new Queue("direct.queue.three", true);
    }

    /**
     * 创建 Direct 模式交换机
     */
    @Bean
    public DirectExchange directExchange() {
        /*
         * param1: 交换机名称
         * param2: durable:是否持久化,RabbitMQ关闭后,没持久化Exchange将被清除
         * param3: autoDelete:是否自动删除,,如果没有与之绑定的Queue直接删除
         */
        return new DirectExchange("direct_exchange", true, false);
    }

    /**
     * 绑定队列(不用指定routing key),参数名字要和bean名字一致
     */
    @Bean
    public Binding bindingDirect1() {
        return BindingBuilder.bind(directQueueOne()).to(directExchange()).with("1");
    }
    @Bean
    public Binding bindingDirect2() {
        return BindingBuilder.bind(directQueueTwo()).to(directExchange()).with("2");
    }
    @Bean
    public Binding bindingDirect3() {
        return BindingBuilder.bind(directQueueThree()).to(directExchange()).with("3");
    }
}

2、生产者发送消息

    /**
     * 路由模式
     */
    @Test
    public void directSend() {
        // 路由模式,分别对不同的routingKey发送消息
        this.rabbitTemplate.convertAndSend("direct_exchange","1","Direct-One:" + new Date());
        this.rabbitTemplate.convertAndSend("direct_exchange","2","Direct-Two:" + new Date());
        this.rabbitTemplate.convertAndSend("direct_exchange","3","Direct-Three:" + new Date());
    }

3、消费者接收消息

package com.example.service;

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

@Component
public class DirectConsumer {

    @RabbitListener(queues = "direct.queue.one")
    public void processEmail(String name) {
        System.out.println("direct.queue.one 收到消息:" + name);
    }

    @RabbitListener(queues = "direct.queue.two")
    public void processSms(String name) {
        System.out.println("direct.queue.two 收到消息:" + name);
    }

    @RabbitListener(queues = "direct.queue.three")
    public void processWeiXin(String name) {
        System.out.println("direct.queue.three 收到消息:" + name);
    }
}

4、消费者收到测试结果:

direct.queue.one 收到消息:Direct-One测试
direct.queue.two 收到消息:Direct-Two测试
direct.queue.three 收到消息:Direct-Three测试

05、Topic 模式

1、创建队列、交换机、绑定关系

package com.example.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Topic 广播模式
 **/
@Configuration
public class TopicRabbitMQConfig {
    /**
     * 声明队列
     */
    @Bean
    public Queue topicQueueOne() {
        /*
         * durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
         * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
         * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
         * return new Queue("TestQueue",true,true,false);
         * 一般设置一下队列的持久化就好,其余两个就是默认false
         */
        return new Queue("topic.queue.one", true);
    }
    @Bean
    public Queue topicQueueTwo() {
        return new Queue("topic.queue.two", true);
    }
    @Bean
    public Queue topicQueueThree() {
        return new Queue("topic.queue.three", true);
    }

    /**
     * 创建 topic 模式交换器
     */
    @Bean
   public TopicExchange topicExchange(){
        /*
         * param1: 交换机名称
         * param2: durable:是否持久化,RabbitMQ关闭后,没持久化Exchange将被清除
         * param3: autoDelete:是否自动删除,,如果没有与之绑定的Queue直接删除
         */
        return new TopicExchange("topic_exchange", true, false);
    }

    /**
     * 绑定队列
     */
    @Bean
    public Binding bindingTopicOne(){
        return BindingBuilder.bind(topicQueueOne()).to(topicExchange()).with("#.error");
    }
    @Bean
    public Binding bindingTopicTwo(){
        return BindingBuilder.bind(topicQueueTwo()).to(topicExchange()).with("#.log");
    }
    @Bean
    public Binding bindingTopicThree(){
        return BindingBuilder.bind(topicQueueThree()).to(topicExchange()).with("good.#.timer");
    }
}

2、生产者发送消息

    /**
     * 广播模式
     */
    @Test
    public void topicSend() {
        rabbitTemplate.convertAndSend("topic_exchange","test.error","主题模式测试");
        rabbitTemplate.convertAndSend("topic_exchange","test.log","主题模式测试");
        rabbitTemplate.convertAndSend("topic_exchange","good.test.timer","主题模式测试");
    }

3、消费者接收消息

package com.example.service;

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

@Component
public class TopicConsumer {

    @RabbitListener(queues = "topic.queue.one")
    public void processOne(String name) {
        System.out.println("topic.queue.one 收到消息:" + name);
    }

    @RabbitListener(queues = "topic.queue.two")
    public void processTwo(String name) {
        System.out.println("topic.queue.tw 收到消息:" + name);
    }

    @RabbitListener(queues = "topic.queue.three")
    public void processThree(String name) {
        System.out.println("topic.queue.three 收到消息:" + name);
    }

}

4、消费者收到测试结果:

topic.queue.one 收到消息:主题模式测试
topic.queue.two 收到消息:主题模式测试
topic.queue.three 收到消息:主题模式测试

06、Header 模式

1、创建队列、交换机、绑定关系

package com.example.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.HeadersExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * Header 模式
 */
@Configuration
public class HeaderRabbitMQConfig {
    /**
     * Headers 模式
     * 设置header attribute参数类型的交换机,相较于 direct 和 topic 固定地使用 routing_key ,
     * headers 则是一个自定义匹配规则的类型. 在队列与交换器绑定时, 会设定一组键值对规则,
     * 消息中也包括一组键值对( headers 属性), 当这些键值对有一对, 或全部匹配时, 消息被投送到对应队列
     */
    @Bean
    public Queue headerQueueOne(){
        return  new Queue("header.queue.one",true);
    }
    @Bean
    public Queue headerQueueTwo(){
        return  new Queue("header.queue.two",true);
    }

    /**
     * 创建 Header 模式交换机
     */
    @Bean
    public HeadersExchange headersExchange(){
        /*
         * param1: 交换机名称
         * param2: durable:是否持久化,RabbitMQ关闭后,没持久化Exchange将被清除
         * param3: autoDelete:是否自动删除,,如果没有与之绑定的Queue直接删除
         */
        return new HeadersExchange("header_exchange", true, false);
    }

    @Bean
    public Binding headerBinding1(){
        Map<String, Object> map = new HashMap<>();
        map.put("header1","value1");
        map.put("header11","value11");
        // 匹配header中某一个即可
        return BindingBuilder.bind(headerQueueOne()).to(headersExchange()).whereAny(map).match();
    }

    @Bean
    public Binding headerBinding2(){
        Map<String, Object> map = new HashMap<>();
        map.put("header2","value2");
        map.put("header22","value22");
        // 必须全部匹配header
        return BindingBuilder.bind(headerQueueTwo()).to(headersExchange()).whereAll(map).match();
    }
}

2、生产者发送消息

    /**
     * Headers模式
     */
    @Test
    public void headersSend() {
        MessageProperties messageProperties1 = new MessageProperties();
        messageProperties1.setHeader("header1","value1");
        messageProperties1.setHeader("header11","value11");
        Message message1 = new Message(("Header模式—单个匹配").getBytes(), messageProperties1);
        rabbitTemplate.convertAndSend("header_exchange","",message1);

        MessageProperties messageProperties2 = new MessageProperties();
        messageProperties2.setHeader("header2","value2");
        messageProperties2.setHeader("header22","value22");
        Message message2 = new Message(("Header模式—全部匹配").getBytes(), messageProperties2);
        rabbitTemplate.convertAndSend("header_exchange","",message2);
    }

3、消费者接收消息

package com.example.service;

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

@Component
public class HeadersConsumer {

    @RabbitListener(queues = "header.queue.one")
    public void processOne(byte[] bytes) {
        System.out.println("header.queue.one 收到消息:" + new String(bytes));
    }

    @RabbitListener(queues = "header.queue.two")
    public void processTwo(byte[] bytes) {
        System.out.println("header.queue.two 收到消息:" + new String(bytes));
    }
    
}

4、消费者收到测试结果:

header.queue.one 收到消息:Header模式—单个匹配
header.queue.two 收到消息:Header模式—全部匹配
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值