rabbitmq业务队列和死信队列使用实战

273 阅读2分钟

1、配置业务队列和死信队列

创建springboot工程 添加消息队列maven坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

添加mq配置

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=root
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/

spring.rabbitmq.listener.simple.acknowledge-mode=manual

创建业务队列和死信队列配置 BusinessAndDeadMQConfig

import lombok.extern.slf4j.Slf4j;
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 java.util.HashMap;
import java.util.Map;

@Slf4j
@Configuration
public class BusinessAndDeadMQConfig {

    public static final String DIRECT_QUEUE_BUSINESS = "direct.queue.business";
    public static final String DIRECT_EXCHANGE_BUSINESS = "direct.exchange.business";
    public static final String DIRECT_ROUTING_KEY_BUSINESS = "direct.routing.key.business";

    //死信队列
    public static final String DEAD_QUEUE = "dead.queue";
    public static final String DEAD_EXCHANGE = "dead.exchange";
    public static final String DEAD_ROUTING_KEY = "dead.routing.key";

    @Bean
    public Queue directDeadPreQueue() {
        //创建死信队列的组成成分map,用于存放组成成分的相关成员
        Map<String, Object> args = new <String, Object>HashMap(2);
        //设死信交换机
        args.put("x-dead-letter-exchange", DEAD_EXCHANGE);
        //死信队列的路由
        args.put("x-dead-letter-routing-key", DEAD_ROUTING_KEY);
        return new Queue(DIRECT_QUEUE_BUSINESS, true, false, false, args);
    }

    @Bean
    public DirectExchange directDeadPreExchange() {
        return new DirectExchange(DIRECT_EXCHANGE_BUSINESS, true, false);
    }

    @Bean
    public Binding directDeadPreBinding() {
        return BindingBuilder.bind(directDeadPreQueue()).to(directDeadPreExchange()).with(DIRECT_ROUTING_KEY_BUSINESS);
    }


    /**
     * 配置死信队列
     */
    @Bean
    public Queue deadQueue() {
        return new Queue(DEAD_QUEUE, true);
    }

    @Bean
    public DirectExchange deadExchange() {
        return new DirectExchange(DEAD_EXCHANGE, true, false);
    }

    @Bean
    public Binding deadBinding() {
        return BindingBuilder.bind(deadQueue()).to(deadExchange()).with(DEAD_ROUTING_KEY);
    }

}

创建业务队列消费者

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.Service;

import java.io.IOException;


@Service
@Slf4j
public class BusinessConsumer {

    public static final String DIRECT_QUEUE_BUSINESS = "direct.queue.business";

    @RabbitListener(queues = DIRECT_QUEUE_BUSINESS)
    public void consumeMsg(String msg, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) Long tag) throws IOException {
        try {
            log.info("业务queue-消费者,监听到消息:{},准备处理业务逻辑。", msg);
            int i = 1 / 0;
            channel.basicAck(tag, false);
        } catch (Exception e) {
            log.error("业务queue-消费者,监听到消息:{},发生异常,消息不再归入队列中,转向死信队列,异常e:", msg, e);
            channel.basicNack(tag, false, false);
//            channel.basicReject(tag, false);
        }
    }
}

创建业务队列生产者

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class BusinessPublisher {
    public static final String DIRECT_EXCHANGE_BUSINESS = "direct.exchange.business";
    public static final String DIRECT_ROUTING_KEY_BUSINESS = "direct.routing.key.business";
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMsg(String order) {
        try {
            //设置交换机、路由键,发送消息
            rabbitTemplate.convertAndSend(DIRECT_EXCHANGE_BUSINESS, DIRECT_ROUTING_KEY_BUSINESS, order);
            log.info("普通队列-生产者,发送消息:{}", order);
        } catch (Exception e) {
            log.error("普通队列-生产者,发送消息异常,消息:{},异常:", order, e);
        }
    }
}

创建测试类

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MqDemo2ApplicationTests {

    @Autowired
    BusinessPublisher businessPublisher;

    @Test
    public void testPublishMsg() {
        businessPublisher.sendMsg("123456");
    }

}

2、业务队列消费异常后转发到死信队列

先注释消费方法的代码,执行测试类中的发送消息

image.png 看到业务队列和对应的死信队列已创建,且业务队列中有一条新消息未被消费

接着将注释的消费方法代码放开,启动项目

image.png 业务队列中的消息被消费,执行过程中异常,basicNack并设置不重新入业务队列(requeue=false),此时消息转发到死信队列。

3、死信队列的消息处理

1、可以在产生异常的代码处理后,创建一个死信队列的消费者,将消息重新发送到业务队列,或执行其他逻辑。 2、通过rabbitmq的move messages将消息移动到业务队列,需要安装插件

image.png

安装插件后,填入目标队列 image.png

死信队列中的消息重新回到业务队列 image.png