【RabbitMQ】配置文件动态绑定交换机、队列、生产者、消费者

367 阅读9分钟

2.jpg

【RabbitMQ】配置文件动态绑定交换机、队列、生产者、消费者

使用配置文件进行配置,然后代码动态产生队列,交换机,生产者,消费者,以及如果配置了死信队列则动态绑定死信队列。由此得出所有的这些都是根据配置进行操作。

配置

spring:
  rabbitmq:
    # 动态创建和绑定队列、交换机的配置
    modules:
      - routing-key: 路由KEY
        producer: 生产者
        consumer: 消费者
        autoAck: 是否自动ACK
        queue: 队列
          name: 队列名称
          dead-letter-exchange: 死信队列交换机
          dead-letter-routing-key: 死信队列路由KEY
          arguments: 队列其他参数,此配置支持RabbitMQ的扩展配置
            # 1分钟(测试),单位毫秒
            x-message-ttl: 3000 # 延迟队列
        exchange: 交换机
          name: 交换机名称

      ..... 省略其他配置

配置类实现

配置有了,接下来就是创建 Java 对象把配置对象化了,由于支持多个所以用的是集合接收配置。

package cn.com.codingce.utils.config;

import cn.com.codingce.utils.mq.ModuleProperties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.List;

/**
 * 绑定配置基础类
 *
 * @author ma
 */
@Data
@Configuration
@ConfigurationProperties("spring.rabbitmq")
public class RabbitModuleProperties {

    /**
     * 模块配置
     */
    List<ModuleProperties> modules;

}

对应YML的配置类

package cn.com.codingce.utils.mq;

import lombok.Data;

import java.util.Map;

/**
 * YML配置类
 *
 * @author ma
 */
@Data
public class ModuleProperties {

    /**
     * 路由Key
     */
    private String routingKey;

    /**
     * 消费者
     */
    private String consumer;

    /**
     * 生产者
     */
    private String producer;

    /**
     * 新添加!!!!!
     */
    private String retry;

    /**
     * 自动确认
     */
    private Boolean autoAck = true;

    /**
     * 队列信息
     */
    private Queue queue;

    /**
     * 交换机信息
     */
    private Exchange exchange;

    /**
     * 交换机信息类
     */
    @Data
    public static class Exchange {

        /**
         * 交换机类型
         * 默认主题交换机
         */
        private RabbitExchangeTypeEnum type = RabbitExchangeTypeEnum.TOPIC;

        /**
         * 交换机名称
         */
        private String name;

        /**
         * 是否持久化
         * 默认true持久化,重启消息不会丢失
         */
        private boolean durable = true;

        /**
         * 当所有队绑定列均不在使用时,是否自动删除交换机
         * 默认false,不自动删除
         */
        private boolean autoDelete = false;

        /**
         * 交换机其他参数
         */
        private Map<String, Object> arguments;
    }

    /**
     * 队列信息类
     */
    @Data
    public static class Queue {
        /**
         * 队列名称
         */
        private String name;

        /**
         * 是否持久化
         */
        private boolean durable = true; // 默认true持久化,重启消息不会丢失

        /**
         * 是否具有排他性
         */
        private boolean exclusive = false; // 默认false,可多个消费者消费同一个队列

        /**
         * 当消费者均断开连接,是否自动删除队列
         */
        private boolean autoDelete = false; // 默认false,不自动删除,避免消费者断开队列丢弃消息

        /**
         * 绑定死信队列的交换机名称
         */
        private String deadLetterExchange;

        /**
         * 绑定死信队列的路由key
         */
        private String deadLetterRoutingKey;

        /**
         * 交换机其他参数
         */
        private Map<String, Object> arguments;
    }

}

生产者&消费者,这里只需要定义个接口,后续会有实现类进行实现

生产者

package cn.com.codingce.utils.mq;

/**
 * 生产者接口
 */
public interface ProducerService {

    /**
     * 发送消息
     *
     * @param message
     */
    void send(Object message);

}

消费者

这里需要继承 RabbitMQ 的消费者接口,后续会直接把此接口给动态绑定到 RabbitMQ 中。

package cn.com.codingce.utils.mq;


import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;

/**
 * 消费者接口
 *
 * @author ma
 */
public interface ConsumerService extends ChannelAwareMessageListener {

}

重试处理器

package cn.com.codingce.utils.mq;


import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;

/**
 * 重试处理器
 *
 * @author ma
 */
public interface CustomRetryListener {

    /**
     * 最后一次重试失败回调
     *
     * @param context
     * @param callback
     * @param throwable
     * @param <E>
     * @param <T>
     */
    <E extends Throwable, T> void lastRetry(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);

    /**
     * 每次失败的回调
     *
     * @param context
     * @param callback
     * @param throwable
     * @param <E>
     * @param <T>
     */
    <E extends Throwable, T> void onRetry(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);

}

本次测试自定义重试。

package cn.com.codingce.utils.mq.retry;

import cn.com.codingce.utils.mq.CustomRetryListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.stereotype.Component;

/**
 * HRA3报告
 *
 * @author ma
 */
@Component
@Slf4j
public class HraReportConsumerRetryListener implements CustomRetryListener {


    @Override
    public <E extends Throwable, T> void lastRetry(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.info("lastRetry 最后一次重试失败回调");
    }

    @Override
    public <E extends Throwable, T> void onRetry(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.info("onRetry 每次失败的回调");
    }
}

常量枚举定义

交换机类型枚举

package cn.com.codingce.utils.mq;

/**
 * 交换机类型枚举
 *
 * @author ma
 */
public enum RabbitExchangeTypeEnum {

    /**
     * 直连交换机
     * <p>
     * 根据routing-key精准匹配队列(最常使用)
     */
    DIRECT,

    /**
     * 主题交换机
     * <p>
     * 根据routing-key模糊匹配队列,*匹配任意一个字符,#匹配0个或多个字符
     */
    TOPIC,

    /**
     * 扇形交换机
     * <p>
     * 直接分发给所有绑定的队列,忽略routing-key,用于广播消息
     */
    FANOUT,
    /**
     * 头交换机
     * <p>
     * 类似直连交换机,不同于直连交换机的路由规则建立在头属性上而不是routing-key(使用较少)
     */
    HEADERS;
}

队列,交换机,路由 常量枚举

package cn.com.codingce.utils.mq;

import lombok.Getter;

/**
 * 队列,交换机。路由 常量枚举
 *
 * @author ma
 */
public enum RabbitEnum {

    // QUEUE("BSH.{}.QUEUE", "队列名称"),
    QUEUE("{}", "队列名称"),

    // EXCHANGE("BSH.{}.EXCHANGE", "交换机名称"),
    EXCHANGE("{}", "交换机名称"),

    // ROUTER_KEY("BSH.{}.KEY", "路由名称"),
    ROUTER_KEY("{}", "路由名称"),
    ;

    RabbitEnum(String value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    @Getter
    private String value;

    @Getter
    private String desc;

}

核心代码

生产者实现类

其它生产者可继承此类,重写方法实现自己业务。

package cn.com.codingce.utils.mq;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Date;

/**
 * 生产者实现类
 */
@Slf4j
public class AbsProducerService implements ProducerService {

    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 交换机
     */
    private String exchange;

    /**
     * 路由
     */
    private String routingKey;

    /**
     * 调整异步发送
     *
     * @param msg
     */
    @Override
    public void send(Object msg) {
        MessagePostProcessor messagePostProcessor = (message) -> {
            MessageProperties messageProperties = message.getMessageProperties();
            messageProperties.setMessageId(IdUtil.randomUUID());
            messageProperties.setTimestamp(new Date());
            return message;
        };
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentEncoding("UTF-8");
        messageProperties.setContentType("text/plain");
        String data = JSONUtil.toJsonStr(msg);
        Message message = new Message(data.getBytes(StandardCharsets.UTF_8), messageProperties);
        rabbitTemplate.convertAndSend(this.exchange, this.routingKey, message, messagePostProcessor);
    }

    public void setExchange(String exchange) {
        this.exchange = exchange;
    }

    public void setRoutingKey(String routingKey) {
        this.routingKey = routingKey;
    }
}

消息监听工厂类实现

此实现非常重要,此处的代码就是绑定消费者的核心代码

package cn.com.codingce.utils.mq;

import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.aop.Advice;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.retry.RejectAndDontRequeueRecoverer;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

import java.util.Objects;

/**
 * MQ具体消息监听器工厂
 *
 * @author ma
 */
@Data
@Slf4j
@Builder
public class ConsumerContainerFactory implements FactoryBean<SimpleMessageListenerContainer> {

    /**
     * MQ连接工厂
     */
    private ConnectionFactory connectionFactory;

    /**
     * 操作MQ管理器
     */
    private AmqpAdmin amqpAdmin;

    /**
     * 队列
     */
    private Queue queue;

    /**
     * 交换机
     */
    private Exchange exchange;

    /**
     * 消费者
     */
    private ConsumerService consumer;

    /**
     * 重试回调
     */
    private CustomRetryListener retryListener;

    /**
     * 最大重试次数
     */
    private final Integer maxAttempts = 5;

    /**
     * 是否自动确认
     */
    private Boolean autoAck;

    @Override
    public SimpleMessageListenerContainer getObject() throws Exception {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setAmqpAdmin(amqpAdmin);
        container.setConnectionFactory(connectionFactory);
        container.setQueues(queue);
        container.setPrefetchCount(20);
        container.setConcurrentConsumers(20);
        container.setMaxConcurrentConsumers(100);
        container.setDefaultRequeueRejected(Boolean.FALSE);
        container.setAdviceChain(createRetry());
        container.setAcknowledgeMode(autoAck ? AcknowledgeMode.AUTO : AcknowledgeMode.MANUAL);
        if (Objects.nonNull(consumer)) {
            container.setMessageListener(consumer);
        }
        return container;
    }

    /**
     * 配置重试
     *
     * @return
     */
    private Advice createRetry() {
        RetryTemplate retryTemplate = new RetryTemplate();
        retryTemplate.registerListener(new RetryListener() {
            @Override
            public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                // 第一次重试调用
                return true;
            }

            @Override
            public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {

            }

            @Override
            public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                if (Objects.nonNull(retryListener)) {
                    retryListener.onRetry(context, callback, throwable);
                    if (maxAttempts.equals(context.getRetryCount())) {
                        retryListener.lastRetry(context, callback, throwable);
                    }
                }
            }
        });
        retryTemplate.setRetryPolicy(new SimpleRetryPolicy(maxAttempts));
        retryTemplate.setBackOffPolicy(genExponentialBackOffPolicy());
        return RetryInterceptorBuilder.stateless()
                .retryOperations(retryTemplate).recoverer(new RejectAndDontRequeueRecoverer()).build();
    }

    /**
     * 设置过期时间
     *
     * @return
     */
    private BackOffPolicy genExponentialBackOffPolicy() {
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        // 重试间隔基数(秒)
        backOffPolicy.setInitialInterval(5000);
        // 从重试的第一次至最后一次,最大时间间隔(秒)
        backOffPolicy.setMaxInterval(86400000);
        // 重试指数
        backOffPolicy.setMultiplier(1);
        return backOffPolicy;
    }

    @Override
    public Class<?> getObjectType() {
        return SimpleMessageListenerContainer.class;
    }
}

核心配置类

package cn.com.codingce.utils.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import cn.com.codingce.utils.mq.AbsProducerService;
import cn.com.codingce.utils.mq.ConsumerContainerFactory;
import cn.com.codingce.utils.mq.ConsumerService;
import cn.com.codingce.utils.mq.CustomRetryListener;
import cn.com.codingce.utils.mq.ModuleProperties;
import cn.com.codingce.utils.mq.RabbitEnum;
import cn.com.codingce.utils.mq.RabbitExchangeTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AbstractExchange;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.HeadersExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * RabbitMQ 全局配置,SpringBoot 启动后会回调此类
 */
@Slf4j
@Configuration
public class RabbitMqConfig implements SmartInitializingSingleton {

    /**
     * MQ链接工厂
     */
    private final ConnectionFactory connectionFactory;

    /**
     * MQ操作管理器
     */
    private final AmqpAdmin amqpAdmin;

    /**
     * YML配置
     */
    private final RabbitModuleProperties rabbitModuleProperties;

    ///////////////////////////////////////////////////////////////////////////
    public static final String HEALTH_EXCHANGE = "EX_CHANGE";

    public static final String HEALTH_RISK_EVAL_REPT = "HEALTH_RISK_EVAL_REPT";

    public static final String CQZT_HRA3_ROUT_KEY = "cqzt.hra3.#";

    public static final String CQZT_PSY_ROUT_KEY = "cqzt.psyReport.#";

    public static final String CQZT_INTERVENTION_PROGRAM_ROUT_KEY = "cqzt.interventionProgram.#";

    public static final String HEALTH_MONITOR_ROUT_KEY = "health.monitor.#";

    /**
     * 危急值
     */
    public static final String HEALTH_MONITOR = "QUEUE_TO_HEALTH_MONITOR";

    /**
     * 干预方案队列
     */
    public static final String HEALTH_INTERVENE_PROGRAM = "HEALTH_INTERVENE_PROGRAM";

    /**
     * 队列 个人心里报告
     */
    public static final String HEALTH_PSY_REPT = "HEALTH_PSY_REPT";

    ///////////////////////////////////////////////////////////////////////////

    @Autowired
    public RabbitMqConfig(AmqpAdmin amqpAdmin, RabbitModuleProperties rabbitModuleProperties, ConnectionFactory connectionFactory) {
        this.amqpAdmin = amqpAdmin;
        this.rabbitModuleProperties = rabbitModuleProperties;
        this.connectionFactory = connectionFactory;
    }

    @Override
    public void afterSingletonsInstantiated() {
        StopWatch stopWatch = StopWatch.create("MQ");
        stopWatch.start();
        log.debug("afterSingletonsInstantiated 初始化MQ配置");
        List<ModuleProperties> modules = rabbitModuleProperties.getModules();
        if (CollUtil.isEmpty(modules)) {
            log.warn("afterSingletonsInstantiated 未配置MQ");
            return;
        }
        for (ModuleProperties module : modules) {
            try {
                Queue queue = genQueue(module);
                Exchange exchange = genQueueExchange(module);
                // 绑定交换机
                queueBindExchange(queue, exchange, module);
                // 绑定生产者
                bindProducer(module);
                // 绑定消费者
                bindConsumer(queue, exchange, module);
            } catch (Exception e) {
                log.error("afterSingletonsInstantiated 初始化失败", e);
            }
        }
        stopWatch.stop();
        log.info("afterSingletonsInstantiated 初始化MQ配置成功耗时: {}ms", stopWatch.getTotal(TimeUnit.MILLISECONDS));
    }

    /**
     * 绑定生产者
     *
     * @param module
     */
    private void bindProducer(ModuleProperties module) {
        try {
            log.info("bindProducer module:{}", JSON.toJSONString(module));
            AbsProducerService producerService = SpringUtil.getBean(module.getProducer());
            if (producerService != null) {
                producerService.setExchange(module.getExchange().getName());
                producerService.setRoutingKey(module.getRoutingKey());
                log.debug("bindProducer 绑定生产者: {}", module.getProducer());
            } else {
                log.debug("bindProducer 未绑定生产者");
            }
        } catch (Exception e) {
            log.warn("bindProducer 无法在容器中找到该生产者[{}],若需要此生产者则需要做具体实现", module.getConsumer());
        }
        log.debug("bindProducer 绑定生产者END -----------------------------------------------------------------");
    }

    /**
     * 绑定消费者
     *
     * @param queue
     * @param exchange
     * @param module
     */
    private void bindConsumer(Queue queue, Exchange exchange, ModuleProperties module) {
        if (StrUtil.isNotBlank(module.getConsumer())) {
            ConsumerService consumer = SpringUtil.getBean(module.getConsumer());
            if (consumer != null) {
                CustomRetryListener customRetryListener = null;
                try {
                    if (StrUtil.isNotBlank(module.getRetry())) {
                        customRetryListener = SpringUtil.getBean(module.getRetry());
                    }
                } catch (Exception e) {
                    log.debug("bindConsumer 无法在容器中找到该重试类[{}],若需要重试则需要做具体实现", module.getRetry());
                }
                try {
                    ConsumerContainerFactory factory = ConsumerContainerFactory.builder()
                            .connectionFactory(connectionFactory)
                            .queue(queue)
                            .exchange(exchange)
                            .consumer(consumer)
                            .autoAck(module.getAutoAck())
                            .amqpAdmin(amqpAdmin)
                            .build();
                    // 单独赋值
                    if (customRetryListener != null) {
                        log.debug("bindConsumer consumerContainerFactory setRetryListener done");
                        factory.setRetryListener(customRetryListener);
                    }
                    SimpleMessageListenerContainer container = factory.getObject();
                    if (Objects.nonNull(container)) {
                        container.start();
                    }
                    log.debug("bindConsumer 绑定消费者: {}", module.getConsumer());
                } catch (Exception e) {
                    log.warn("bindConsumer 无法在容器中找到该消费者[{}],若需要此消费者则需要做具体实现", module.getConsumer());
                }
            } else {
                log.debug("bindProducer 未绑定消费者");
            }
        } else {
            log.debug("bindProducer 未绑定消费者");
        }

        log.debug("bindConsumer 绑定消费者END -----------------------------------------------------------------");
    }

    /**
     * 队列绑定交换机
     *
     * @param queue
     * @param exchange
     * @param module
     */
    private void queueBindExchange(Queue queue, Exchange exchange, ModuleProperties module) {
        log.debug("queueBindExchange 初始化交换机: {}", module.getExchange().getName());
        String queueName = module.getQueue().getName();
        String exchangeName = module.getExchange().getName();
        module.setRoutingKey(StrUtil.format(RabbitEnum.ROUTER_KEY.getValue(), module.getRoutingKey()));
        String routingKey = module.getRoutingKey();
        Binding binding = new Binding(queueName,
                Binding.DestinationType.QUEUE,
                exchangeName,
                routingKey,
                null);
        amqpAdmin.declareQueue(queue);
        amqpAdmin.declareExchange(exchange);
        amqpAdmin.declareBinding(binding);
        log.debug("queueBindExchange 队列绑定交换机: 队列: {}, 交换机: {}", queueName, exchangeName);
        log.debug("queueBindExchange 队列绑定交换机END -----------------------------------------------------------------");
    }

    /**
     * 创建交换机
     *
     * @param module
     * @return
     */
    private Exchange genQueueExchange(ModuleProperties module) {
        ModuleProperties.Exchange exchange = module.getExchange();
        RabbitExchangeTypeEnum exchangeType = exchange.getType();
        exchange.setName(StrUtil.format(RabbitEnum.EXCHANGE.getValue(), exchange.getName()));
        String exchangeName = exchange.getName();
        Boolean isDurable = exchange.isDurable();
        Boolean isAutoDelete = exchange.isAutoDelete();
        Map<String, Object> arguments = exchange.getArguments();
        return getExchangeByType(exchangeType, exchangeName, isDurable, isAutoDelete, arguments);
    }

    /**
     * 根据类型生成交换机
     *
     * @param exchangeType
     * @param exchangeName
     * @param isDurable
     * @param isAutoDelete
     * @param arguments
     * @return
     */
    private Exchange getExchangeByType(RabbitExchangeTypeEnum exchangeType, String exchangeName, Boolean isDurable, Boolean isAutoDelete, Map<String, Object> arguments) {
        AbstractExchange exchange = null;
        switch (exchangeType) {
            // 直连交换机
            case DIRECT:
                exchange = new DirectExchange(exchangeName, isDurable, isAutoDelete, arguments);
                break;
            // 主题交换机
            case TOPIC:
                exchange = new TopicExchange(exchangeName, isDurable, isAutoDelete, arguments);
                break;
            //扇形交换机
            case FANOUT:
                exchange = new FanoutExchange(exchangeName, isDurable, isAutoDelete, arguments);
                break;
            // 头交换机
            case HEADERS:
                exchange = new HeadersExchange(exchangeName, isDurable, isAutoDelete, arguments);
                break;
            default:
                log.warn("getExchangeByType 未匹配到交换机类型");
                break;
        }
        return exchange;
    }

    /**
     * 创建队列
     *
     * @param module
     * @return
     */
    private Queue genQueue(ModuleProperties module) {
        ModuleProperties.Queue queue = module.getQueue();
        queue.setName(StrUtil.format(RabbitEnum.QUEUE.getValue(), queue.getName()));
        log.debug("genQueue 初始化队列: {}", queue.getName());
        Map<String, Object> arguments = queue.getArguments();
        if (MapUtil.isEmpty(arguments)) {
            arguments = new HashMap<>();
        }

        // 转换ttl的类型为long
        if (arguments.containsKey("x-message-ttl")) {
            arguments.put("x-message-ttl", Convert.toLong(arguments.get("x-message-ttl")));
        }

        // 绑定死信队列
        String deadLetterExchange = queue.getDeadLetterExchange();
        String deadLetterRoutingKey = queue.getDeadLetterRoutingKey();
        if (StrUtil.isNotBlank(deadLetterExchange) && StrUtil.isNotBlank(deadLetterRoutingKey)) {
            deadLetterExchange = StrUtil.format(RabbitEnum.EXCHANGE.getValue(), deadLetterExchange);
            deadLetterRoutingKey = StrUtil.format(RabbitEnum.ROUTER_KEY.getValue(), deadLetterRoutingKey);
            arguments.put("x-dead-letter-exchange", deadLetterExchange);
            arguments.put("x-dead-letter-routing-key", deadLetterRoutingKey);
            log.debug("genQueue 绑定死信队列: 交换机: {}, 路由: {}", deadLetterExchange, deadLetterRoutingKey);
        }

        return new Queue(queue.getName(), queue.isDurable(), queue.isExclusive(), queue.isAutoDelete(), arguments);
    }

}

以上配置完成后,开始RUN代码。

使用示例

这里配置了延迟队列和死信队列,开RUN。

spring:
  rabbitmq:
    # 动态创建和绑定队列、交换机的配置
    modules:
      # 正常队列
      - routing-key: health.hra3.#
        producer: hraReportProducerService
        consumer: hraReportConsumerService
        autoAck: false
        retry: hraReportConsumerRetryListener
        queue:
          name: HEALTH_RISK_EVAL_REPT
        exchange:
          name: EX_CHANGE

      # 死信队列
      - routing-key: dead
        #consumer: deadConsumerService
        #producer: deadProducerService
        autoAck: false
        queue:
          name: DEAD_TEST
        exchange:
          name: DEAD_EXCHANGE

项目启动部分日志如下

: afterSingletonsInstantiated 初始化MQ配置
: genQueue 初始化队列: HEALTH_RISK_EVAL_REPT
: queueBindExchange 初始化交换机: EX_CHANGE
: queueBindExchange 队列绑定交换机: 队列: HEALTH_RISK_EVAL_REPT, 交换机: EX_CHANGE
: queueBindExchange 队列绑定交换机END -----------------------------------------------------------------
: bindProducer module:{"autoAck":false,"consumer":"hraReportConsumerService","exchange":{"autoDelete":false,"durable":true,"name":"EX_CHANGE","type":"TOPIC"},"producer":"hraReportProducerService","queue":{"autoDelete":false,"durable":true,"exclusive":false,"name":"HEALTH_RISK_EVAL_REPT"},"retry":"hraReportConsumerRetryListener","routingKey":"health.hra3.#"}
: bindProducer 绑定生产者: hraReportProducerService
: bindProducer 绑定生产者END -----------------------------------------------------------------
: bindConsumer consumerContainerFactory setRetryListener done
: bindConsumer 绑定消费者: hraReportConsumerService
: bindConsumer 绑定消费者END -----------------------------------------------------------------
: genQueue 初始化队列: DEAD_TEST
: queueBindExchange 初始化交换机: DEAD_EXCHANGE
: queueBindExchange 队列绑定交换机: 队列: DEAD_TEST, 交换机: DEAD_EXCHANGE
: queueBindExchange 队列绑定交换机END -----------------------------------------------------------------
: bindProducer module:{"autoAck":false,"exchange":{"autoDelete":false,"durable":true,"name":"DEAD_EXCHANGE","type":"TOPIC"},"queue":{"autoDelete":false,"durable":true,"exclusive":false,"name":"DEAD_TEST"},"routingKey":"dead"}
: bindProducer 无法在容器中找到该生产者[null],若需要此生产者则需要做具体实现
: bindProducer 绑定生产者END -----------------------------------------------------------------
: bindProducer 未绑定消费者
: bindConsumer 绑定消费者END -----------------------------------------------------------------
: afterSingletonsInstantiated 初始化MQ配置成功耗时: 114ms

交换机

队列

生产者发送消息

...
@Resource(name = "hraReportProducerService")
private HraReportProducerService hraReportProducerService;
...
hraReportProducerService.send(JSON.toJSONString(comMq));

消费者监听消息

package cn.com.codingce.utils.mq.consumer;

import com.alibaba.fastjson.JSON;
import cn.com.codingce.utils.mq.AbsConsumerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * HRA3报告
 *
 * @author ma
 */
@Component
@Slf4j
public class HraReportConsumerService extends AbsConsumerService {

    @Override
    public void onConsumer(Object data) throws IOException {
        log.info("onConsumer data:{}", JSON.toJSONString(data));
        ack();
    }

}