【RabbitMQ、Spring Boot】Spring Boot整合RabbitMQ

一、环境准备

引入pom依赖

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-amqpartifactId>
        dependency>

设置application.yml配置文件

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 7006
    username: admin
    password: admin
    # 是否触发回调方法
    # NONE值是禁用发布确认模式,是默认值
    # CORRELATED值是发布消息成功到交换器后会触发回调方法,如1示例
    # SIMPLE值经测试有两种效果,其一效果和CORRELATED值一样会触发回调方法,其二在发布消息成功后使用rabbitTemplate调用waitForConfirms或waitForConfirmsOrDie方法等待broker节点返回发送结果,根据返回结果来判定下一步的逻辑,要注意的点是waitForConfirmsOrDie方法如果返回false则会关闭channel,则接下来无法发送消息到broker;
    publisher-confirm-type: correlated

二、简单模式:一个消费者消费一个队列中的信息

1、注册

package com.rabbmq.config;

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

/**
 * 简单模式
 *
 * @author He PanFu
 * @date 2021-09-16 12:33:25
 */
@Configuration
public class RabbitSimpleConfig {
    
    @Bean
    public Queue simpleQueueWx() {
        // 队列名称,是否持久化
        return new Queue("simple_queue", true);
    }
}

2、添加消息到队列中

	@Autowired
    RabbitTemplate rabbitTemplate;

	 @GetMapping("/simple")
    public String sendSimpleMessage() {
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend("simple_queue", getStringObjectMap());
        }
        return "ok";
    }
    
	private Map<String, Object> getStringObjectMap() {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = "test message, hello!";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", createTime);
        return map;
    }

3、消费者消费消息

package com.rabbitmq.receiver;

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

import java.util.Map;


/**
 * 简单模式:微信订阅
 *
 * @author He PanFu
 * @date 2021-09-16 12:47:32
 */
@Component
@RabbitListener(queues = "simple_queue")
public class RabbitSimpleWXListener {

    @RabbitHandler
    public void init(Map testMessage) {
        System.out.println("简单模式(微信):" + testMessage);
    }
}

4、展示

【RabbitMQ、Spring Boot】Spring Boot整合RabbitMQ_第1张图片

备注:

  • @RabbitListener可以直接在方法上单独使用。
  • @RabbitListener 可以标注在类上面,需配合 @RabbitHandler 注解一起使用
  • @RabbitListener 标注在类上面表示当有收到消息的时候,就交给 @RabbitHandler 的方法处理,具体使用哪个方法处理,根据 MessageConverter 转换后的参数类型

三、工作模式(轮询分发):多个消费者共同消费同一个队列中的信息

  • 介绍:启动多个消费者,当生产者将消息发送给队列时,一条信息只会被一个消费者接收,rabbit默认采用轮询的方式将消息平均发送给消费者,消费者在处理完某个信息后才会接收到下一条信息。
  • 轮询分发(Round-robin):消息的分发是轮询的,即多个消费者依次从队列中获取消息并消费,每次获取一条,轮询分发不考虑消费者消费消息的速度。举个例子,队列中有100条消息,C1,C2两个消费者,无论它两的消费速度如何,最后的结果总是你一个我一个的每个人拿到25条消息。

1、注册

package com.cyun.demo.rabbitmq.provider;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * 工作模式
 *
 * @author He PanFu
 * @date 2021-09-16 12:33:25
 */
@Component
public class RabbitWorkManage {
    /**
     * 工作队列
     *
     * @return 队列
     */
    @Bean
    public Queue workQueue() {
        return new Queue("work_queue", true);
    }
}


2、添加消息到队列中

 	@GetMapping("/work")
    public String sendWorkMessage() {
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend("work_queue", getStringObjectMap());
        }
        return "ok";
    }

3、消费者消费消息

备注:正式环境是启动多台消费服务,我这里直接简单化模拟。

package com.cyun.demo.rabbitmq.consumer;

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

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


/**
 * 工作模式:队列监听
 *
 * @author He PanFu
 * @date 2021-09-16 12:47:32
 */
@Slf4j
@Component
public class RabbitWorkListener {

    @RabbitListener(queues = "work_queue")
    public void messageListener1(Object msg, Channel channel, Message message) throws InterruptedException, IOException {
       TimeUnit.SECONDS.sleep(1);
        log.info("1消息处理完成,消息内容:{}", msg);
    }

    @RabbitListener(queues = "work_queue")
    public void messageListener2(Object msg, Channel channel, Message message) throws IOException, InterruptedException {
       TimeUnit.SECONDS.sleep(2);
        log.info("2消息处理完成,消息内容:{}", msg);
    }
}

4、展示

【RabbitMQ、Spring Boot】Spring Boot整合RabbitMQ_第2张图片

四、工作模式(公平分发):多个消费者共同消费同一个队列中的信息

  • 公平分发(Fair dispatcher):消息的分发是公平的,即按照消费者消费的能力来分配消息,能者多劳。举个例子,队列中有90条消息,C1,C2两个消费者,其中C1消费一条消息的速度是1s,C2消费一条消息的速度是2s,那么最后的结果是C1消费了60条消息,C2消费了30条消息。

1、修改application.yml文件

开启手动ack,并且每次只能读取1条消息

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 7006
    username: admin
    password: admin
    # 是否触发回调方法
    # NONE值是禁用发布确认模式,是默认值
    # CORRELATED值是发布消息成功到交换器后会触发回调方法,如1示例
    # SIMPLE值经测试有两种效果,其一效果和CORRELATED值一样会触发回调方法,其二在发布消息成功后使用rabbitTemplate调用waitForConfirms或waitForConfirmsOrDie方法等待broker节点返回发送结果,根据返回结果来判定下一步的逻辑,要注意的点是waitForConfirmsOrDie方法如果返回false则会关闭channel,则接下来无法发送消息到broker;
    publisher-confirm-type: correlated
    listener:
      simple:
        # 开启手动确定
        acknowledge-mode: manual
        # 预处理模式更改为每次读取1条消息,在消费者未回执确认之前,不在进行下一条消息的投送
        prefetch: 1

2、改造消费者消费消息代码

package com.cyun.demo.rabbitmq.consumer;

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

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


/**
 * 工作模式:队列监听
 *
 * @author He PanFu
 * @date 2021-09-16 12:47:32
 */
@Slf4j
@Component
public class RabbitWorkListener {

    @RabbitListener(queues = "work_queue")
    public void messageListener1(Object msg, Channel channel, Message message) throws InterruptedException, IOException {
        log.info("1消息开始处理,消息内容:{}", msg);
        
        // 公平分发:手动进行确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        // 手动返回未确认消息,requeue 为true时,重新入队
        // channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);

    }

    @RabbitListener(queues = "work_queue")
    public void messageListener2(Object msg, Channel channel, Message message) throws IOException, InterruptedException {
        log.info("2消息开始处理,消息内容:{}", msg);
        
        TimeUnit.SECONDS.sleep(1);
        // 公平分发:手动进行确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
}

3、展示

【RabbitMQ、Spring Boot】Spring Boot整合RabbitMQ_第3张图片

五、发布/订阅模式:多个消费者消费同一个信息

1、注册

package com.cyun.demo.rabbitmq.provider;

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;

/**
 * 发布/订阅模式
 *
 * @author He PanFu
 * @date 2022-04-16 17:04:55
 */
@Configuration
public class RabbitmqFanoutConfig {

    /**
     * 设置交换机:
     * 

* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效 * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。 * * @return 发布/订阅交换机 */ @Bean public FanoutExchange fanoutExchange() { return new FanoutExchange("fanout_exchange", true, false); } /** * 微信队列 * * @return 队列 */ @Bean public Queue fanoutQueueWx() { return new Queue("fanout_queue_wx", true); } /** * 短信队列 * * @return 队列 */ @Bean public Queue fanoutQueueDx() { return new Queue("fanout_queue_dx", true); } /** * 微信队列绑定交换机 * * @return 绑定对象 */ @Bean public Binding fanoutBindingWx() { return BindingBuilder.bind(fanoutQueueWx()).to(fanoutExchange()); } /** * 短信队列绑定交换机 * * @return 绑定对象 */ @Bean public Binding fanoutBindingDx() { return BindingBuilder.bind(fanoutQueueDx()).to(fanoutExchange()); } }

2、添加消息到队列中

	 @GetMapping("/fanout")
    public String sendFanoutMessage() {
        for (int i = 0; i < 10; i++) {
            //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
            rabbitTemplate.convertAndSend("fanout_exchange", "", getStringObjectMap(), new CorrelationData(UUID.randomUUID().toString()));
        }
        return "ok";
    }

3、消费者消费消息

package com.cyun.demo.rabbitmq.consumer;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

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


/**
 * 发布/订阅模式:队列监听
 *
 * @author He PanFu
 * @date 2021-09-16 12:47:32
 */
@Slf4j
@Component
public class RabbitmqFanoutListener {

    @RabbitListener(queues = "fanout_queue_wx")
    public void messageListener1(Object msg, Channel channel, Message message, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws InterruptedException, IOException {
        log.info("订阅模式(微信),消息开始处理,消息内容:{}", msg);

        // 手动进行确认消息
        channel.basicAck(tag, false);

    }

    @RabbitListener(queues = "fanout_queue_dx")
    public void messageListener2(Object msg, Channel channel, Message message) throws IOException, InterruptedException {
        log.info("订阅模式(短信),消息开始处理,消息内容:{}", msg);

        // 手动进行确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
}

4、展示

【RabbitMQ、Spring Boot】Spring Boot整合RabbitMQ_第4张图片

六、路由模式

1、注册

package com.cyun.demo.rabbitmq.provider;

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;
import org.springframework.stereotype.Component;

/**
 * @author He PanFu
 * @date 2022-04-16 18:29:03
 */
@Configuration
public class RabbitmqDirectConfig {

    /**
     * 设置交换机:
     * 

* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效 * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。 * * @return 交换机: */ @Bean public DirectExchange directExchange() { return new DirectExchange("direct_exchange", true, false); } /** * 微信队列 * * @return 队列 */ @Bean public Queue directQueueWx() { return new Queue("direct_queue_wx", true); } /** * 短信队列 * * @return 队列 */ @Bean public Queue directQueueDx() { return new Queue("direct_queue_dx", true); } /** * 微信队列绑定交换机 * * @return 绑定 */ @Bean public Binding directBindingWx() { return BindingBuilder.bind(directQueueWx()).to(directExchange()).with("wx"); } /** * 短信队列绑定交换机 * * @return 绑定 */ @Bean public Binding directBindingDx() { return BindingBuilder.bind(directQueueDx()).to(directExchange()).with("dx"); } }

2、添加消息到队列中

    @GetMapping("/direct/{routingKey}")
    public String sendDirectMessage(@PathVariable("routingKey") String routingKey) {
        rabbitTemplate.convertAndSend("direct_exchange", routingKey, getStringObjectMap());
        return "ok";
    }

3、消费者消费消息

package com.cyun.demo.rabbitmq.consumer;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * 路由模式
 *
 * @author He PanFu
 * @date 2022-04-16 18:32:19
 */
@Slf4j
@Component
public class RabbitmqDirectListener {

    @RabbitListener(queues = "direct_queue_wx")
    public void messageListener1(Object msg, Channel channel, Message message, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws InterruptedException, IOException {
        log.info("订阅模式(微信),消息开始处理,消息内容:{}", msg);

        // 手动进行确认消息
        channel.basicAck(tag, false);

    }

    @RabbitListener(queues = "direct_queue_dx")
    public void messageListener2(Object msg, Channel channel, Message message) throws IOException, InterruptedException {
        log.info("订阅模式(短信),消息开始处理,消息内容:{}", msg);

        // 手动进行确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
}

4、展示

在这里插入图片描述

七、主题模式

1、注册

package com.cyun.demo.rabbitmq.provider;

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;

/**
 * 主题模式
 *
 * @author He PanFu
 * @date 2022-04-18 09:00:43
 */
@Configuration
public class RabbitmqTopicConfig {

    /**
     * 设置交换机:
     * 

* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效 * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。 * * @return 交换机 */ @Bean public TopicExchange topicExchange() { return new TopicExchange("topic_exchange", true, false); } /** * 设置微信队列 * * @return 队列 */ @Bean public Queue topicQueueWx() { return new Queue("topic_queue_wx", true); } /** * 设置短信队列 * * @return 队列 */ @Bean public Queue topicQueueDx() { return new Queue("topic_queue_dx", true); } /** * 进行微信队列绑定交换机 * "*" 表示任何一个词 * "#" 表示0或1个词 * * @return 绑定 */ @Bean public Binding topicBindingWx() { return BindingBuilder.bind(topicQueueWx()).to(topicExchange()).with("#.wx.#"); } /** * 进行短信队列绑定交换机 * "*" 表示任何一个词 * "#" 表示0或1个词 * * @return 绑定 */ @Bean public Binding topicBindingDx() { return BindingBuilder.bind(topicQueueDx()).to(topicExchange()).with("dx.*"); } }

2、添加消息到队列中

    @GetMapping("/topic")
    public String sendTopicMessage() {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = "test message, hello!";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", createTime);
        //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
        map.put("key", "wx");
        rabbitTemplate.convertAndSend("topic_exchange", "wx", map);
        map.put("key", "wx.www");
        rabbitTemplate.convertAndSend("topic_exchange", "wx.www", map);
        map.put("key", "wx.www.www");
        rabbitTemplate.convertAndSend("topic_exchange", "wx.www.www", map);
        map.put("key", "dx");
        rabbitTemplate.convertAndSend("topic_exchange", "dx", map);
        map.put("key", "dx.www");
        rabbitTemplate.convertAndSend("topic_exchange", "dx.www", map);
        map.put("key", "dx.www.www");
        rabbitTemplate.convertAndSend("topic_exchange", "dx.www.www", map);
        return "ok";
    }

3、消费者消费消息

package com.cyun.demo.rabbitmq.consumer;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

/**
 * 主题模式
 *
 * @author He PanFu
 * @date 2022-04-18 09:18:47
 */
@Slf4j
@Component
public class RabbitmqTopicListener {

    /**
     * 微信队列监听
     *
     * @param testMessage 数据
     */
    @RabbitListener(queues = "topic_queue_wx")
    public void messageListener2(Map testMessage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
        log.info("主题模式(微信),key:{},testMessage:{}", testMessage.get("key"),testMessage);

        channel.basicAck(tag, false);
    }
    
    /**
     * 短信队列监听
     *
     * @param testMessage 数据
     */
    @RabbitListener(queues = "topic_queue_dx")
    public void messageListener1(Map testMessage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
        log.info("主题模式(短信),key:{},testMessage:{}", testMessage.get("key"),testMessage);
        channel.basicAck(tag, false);
    }

}


4、展示

在这里插入图片描述

八、参数模式

1、注册

package com.cyun.demo.rabbitmq.provider;

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;

/**
 * 参数模式
 *
 * @author He PanFu
 * @date 2022-04-18 09:53:43
 */
@Configuration
public class RabbitmqHeadersConfig {

    /**
     * 设置交换机:
     * 

* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效 * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。 * * @return 交换机 */ @Bean public HeadersExchange headersExchange() { return new HeadersExchange("headers_exchange", true, false); } /** * 设置微信队列 * * @return 队列 */ @Bean public Queue headersQueueWx() { return new Queue("headers_queue_wx", true); } /** * 设置短信队列 * * @return 队列 */ @Bean public Queue headersQueueDx() { return new Queue("headers_queue_dx", true); } /** * 设置邮箱队列 * * @return 队列 */ @Bean public Queue headersQueueYx() { return new Queue("headers_queue_yx", true); } /** * 进行微信队列绑定交换机,请求头中包含两个 * * @return 绑定 */ @Bean public Binding headersBindingWx() { Map<String, Object> map = new HashMap<>(); map.put("One", "A"); map.put("Two", "B"); return BindingBuilder.bind(headersQueueWx()).to(headersExchange()).whereAll(map).match(); } /** * 进行短信队列绑定交换机,请求头中包含任意一个 * * @return 绑定 */ @Bean public Binding headersBindingDx() { Map<String, Object> map = new HashMap<>(); map.put("One", "A"); map.put("Two", "B"); return BindingBuilder.bind(headersQueueDx()).to(headersExchange()).whereAny(map).match(); } /** * 进行邮箱队列绑定交换机 * * @return 绑定 */ @Bean public Binding headersBindingYx() { return BindingBuilder.bind(headersQueueYx()).to(headersExchange()).where("Three").matches("C"); } }

2、添加消息到队列中


    @GetMapping("/topic")
    public String sendTopicMessage() {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = "test message, hello!";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", createTime);
        //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
        map.put("key", "wx");
        rabbitTemplate.convertAndSend("topic_exchange", "wx", map);
        map.put("key", "wx.www");
        rabbitTemplate.convertAndSend("topic_exchange", "wx.www", map);
        map.put("key", "wx.www.www");
        rabbitTemplate.convertAndSend("topic_exchange", "wx.www.www", map);
        map.put("key", "dx");
        rabbitTemplate.convertAndSend("topic_exchange", "dx", map);
        map.put("key", "dx.www");
        rabbitTemplate.convertAndSend("topic_exchange", "dx.www", map);
        map.put("key", "dx.www.www");
        rabbitTemplate.convertAndSend("topic_exchange", "dx.www.www", map);
        return "ok";
    }

3、消费者消费消息

package com.cyun.demo.rabbitmq.consumer;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

/**
 * 参数模式
 *
 * @author He PanFu
 * @date 2022-04-18 10:27:15
 */
@Slf4j
@Component
public class RabbitmqHeadersListener {

    /**
     * 微信队列监听
     *
     * @param testMessage 数据
     */
    @RabbitListener(queues = "headers_queue_wx")
    public void messageListener1(Message testMessage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
        log.info("参数模式(微信),testMessage:{}", testMessage);

        channel.basicAck(tag, false);
    }

    /**
     * 短信队列监听
     *
     * @param testMessage 数据
     */
    @RabbitListener(queues = "headers_queue_dx")
    public void messageListener2(Message testMessage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
        log.info("参数模式(短信),testMessage:{}", testMessage);

        channel.basicAck(tag, false);
    }

    /**
     * 邮箱队列监听
     *
     * @param testMessage 数据
     */
    @RabbitListener(queues = "headers_queue_yx")
    public void messageListener3(Message testMessage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
        log.info("参数模式(邮箱),testMessage:{}", testMessage);

        channel.basicAck(tag, false);
    }
}

4、展示

在这里插入图片描述

九、死信队列

介绍:“死信”是RabbitMQ中的一种消息机制,当你在消费消息时,如果队列里的消息出现以下情况,那么该消息将成为“死信”。

  1. 消息被否定确认,使用 channel.basicNack 或 channel.basicReject ,并且此时requeue 属性被设置为false。
  2. 消息在队列的存活时间超过设置的TTL时间。
  3. 消息队列的消息数量已经超过最大队列长度。

“死信”消息会被RabbitMQ进行特殊处理,如果配置了死信队列信息,那么该消息将会被丢进死信队列中,如果没有配置,则该消息将会被丢弃。

注意:如果修改了队列的配置信息,需重新删除原有的队列重新注册。
在这里插入图片描述

1、注册

package com.cyun.demo.rabbitmq.provider;

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;

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

/**
 * 发布/订阅模式
 *
 * @author He PanFu
 * @date 2022-04-16 17:04:55
 */
@Configuration
public class RabbitmqFanoutTtlConfig {

    /**
     * 设置交换机:
     * 

* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效 * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。 * * @return 发布/订阅交换机 */ @Bean public FanoutExchange fanoutTtlExchange() { return new FanoutExchange("fanout_ttl_exchange", true, false); } /** * 微信队列 * * @return 队列 */ @Bean public Queue fanoutTtlQueueWx() { Map<String, Object> map = new HashMap<>(); // 过期时间 map.put("x-message-ttl", 1000); // 死信队列,消息过期后,会放到死信交换机中 map.put("x-dead-letter-exchange", "direct_exchange"); map.put("x-dead-letter-routing-key", "wx"); return new Queue("fanout_ttl_queue_wx", true, false, false, map); } /** * 微信队列绑定交换机 * * @return 绑定对象 */ @Bean public Binding fanoutTtlBindingWx() { return BindingBuilder.bind(fanoutTtlQueueWx()).to(fanoutTtlExchange()); } }

2、添加消息到队列中


    @GetMapping("/fanout/ttl")
    public String sendFanoutTtlMessage() {
        rabbitTemplate.convertAndSend("fanout_ttl_exchange", "", getStringObjectMap());
        return "ok";
    }

3、消费者消费消息

package com.cyun.demo.rabbitmq.consumer;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

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


/**
 * 发布/订阅模式:队列监听
 *
 * @author He PanFu
 * @date 2021-09-16 12:47:32
 */
@Slf4j
@Component
public class RabbitmqFanoutTtlListener {

    @RabbitListener(queues = "fanout_ttl_queue_wx")
    public void messageListener1(Object msg, Channel channel, Message message, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws InterruptedException, IOException {
        log.info("订阅模式TTL(微信),消息开始处理,消息内容:{}", msg);
        
        // 触发情况一:消息被否定确定。multiple:是否批量处理.true:将一次性ack所有小于deliveryTag的消息,requeue:为true时,重新入队
        // channel.basicNack(tag,false,false);
        // 触发情况二:超时
        TimeUnit.SECONDS.sleep(15);
        channel.basicAck(tag, false);
    }

}

4、展示

在这里插入图片描述

5、拓展:添加消息到队列时设置过期时间信息

注意:两个过期时间,以最小准。

    @GetMapping("/fanout/ttl/5000")
    public String sendFanoutTTLMessageMessage() {
        // 设置过期时间
        MessagePostProcessor processor = message -> {
            message.getMessageProperties().setExpiration("5000");
            message.getMessageProperties().setContentEncoding("UTF-8");
            return message;
        };

        rabbitTemplate.convertAndSend("fanout_ttl_exchange", "", getStringObjectMap(), processor);
        return "ok";
    }

十、总结

具体步骤为:注册(注册交换机 -> 注册队列 -> 绑定交换机与队列) -> 监听 -> 加入队列

你可能感兴趣的:(RabbitMQ,spring,boot,分布式,微服务,rabbitmq,spring,cloud)