springboot 针对rabbitmq多vhost情况处理

文章介绍了在SpringBoot项目中,如何自定义RabbitMQ的配置以支持多个vhost,包括重写连接工厂,创建队列、交换机并绑定,以及实现消费者的消费和生产者的发送。同时,文章强调了消息确认机制在处理过程中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

说明

需求场景:
项目中在已接入rabbitmq一个vhost的基础上,需要再引入多个vhost进行消息处理,spring原来的支持以及满足不了,所以自己要重写。

代码

自定义rabbitmq的pom信息:

spring:
  rabbitmq:
    one:
      host: xxxx
      port: 5672
      virtual-host: one
      username: xxxxx
      password: xxxx
      queue: xxx.xxx.xxx
      topic:
        exchange:
          name: xxx_xxx_xxx
    two:
      host: xxxx
      port: 5672
      virtual-host: two
      username: xxxx
      password: xxxxx
      queue: xxx.xxx.xxx
      topic:
        exchange:
          name: xxx_xxx_xxx

重写one的连接工厂

在连接多个 MQ 的情况下,需要在某个连接加上 @Primary 注解,表示主连接,默认使用这个连接,如果不加,服务会起不来

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Slf4j
@Configuration
/**
 * 重写连接工厂
 */
public class OneMQConfig {

    @Value("${spring.rabbitmq.one.host}")
    private String host;

    @Value("${spring.rabbitmq.one.port}")
    private int port;

    @Value("${spring.rabbitmq.one.username}")
    private String username;

    @Value("${spring.rabbitmq.one.password}")
    private String password;

    @Value("${spring.rabbitmq.one.virtual-host}")
    private String virtualHost;

    /**
     * 定义与one的连接工厂
     */
    @Bean(name = "oneConnectionFactory")
    @Primary
    public ConnectionFactory oneConnectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setPublisherConfirms(true);
        connectionFactory.setPublisherReturns(true);
        return connectionFactory;
    }

    @Bean(name = "oneRabbitTemplate")
    @Primary
    public RabbitTemplate oneRabbitTemplate(@Qualifier("oneConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate oneRabbitTemplate = new RabbitTemplate(connectionFactory);
        oneRabbitTemplate.setMandatory(true);
        oneRabbitTemplate.setConnectionFactory(connectionFactory);
        oneRabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 确认消息送到交换机(Exchange)回调
             * @param correlationData
             * @param ack
             * @param cause
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                log.info("确认消息送到交换机(Exchange)结果:");
                log.info("相关数据:{}", correlationData);
                boolean ret = false;
                if (ack) {
                    log.info("消息发送到交换机成功, 消息 = {}", correlationData.getId());
                    //下面可自定义业务逻辑处理,如入库保存信息等

                } else {
                    log.error("消息发送到交换机失败! 消息: {}}; 错误原因:cause: {}", correlationData.getId(), cause);
                    //下面可自定义业务逻辑处理,如入库保存信息等

                }
            }
        });

        oneRabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {

            /**
             * 只要消息没有投递给指定的队列 就触发这个失败回调
             * @param message  投递失败的消息详细信息
             * @param replyCode 回复的状态码
             * @param replyText 回复的文本内容
             * @param exchange 当时这个消息发给那个交换机
             * @param routingKey 当时这个消息用那个路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                //获取消息id
                String messageId = message.getMessageProperties().getMessageId();
                // 内容
                String result = null;
                try {
                    result = new String(message.getBody(), "UTF-8");
                } catch (Exception e) {
                    log.error("消息发送失败{}", e);
                }
                log.error("消息发送失败, 消息ID = {}; 消息内容 = {}", messageId, result);
                //下面可自定义业务逻辑处理,如入库保存信息等
            }
        });
        return oneRabbitTemplate;
    }

    @Bean(name = "oneFactory")
    @Primary
    public SimpleRabbitListenerContainerFactory oneFactory(@Qualifier("oneConnectionFactory") ConnectionFactory connectionFactory,
                                                           SimpleRabbitListenerContainerFactoryConfigurer configurer) {

        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        configurer.configure(factory, connectionFactory);
        return factory;
    }

    @Bean(name = "oneRabbitAdmin")
    @Primary
    public RabbitAdmin oneRabbitAdmin(@Qualifier("oneConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }
}

重写two的连接工厂:


import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class TwoMQConfig {

    @Value("${spring.rabbitmq.two.host}")
    private String host;

    @Value("${spring.rabbitmq.two.port}")
    private int port;

    @Value("${spring.rabbitmq.two.username}")
    private String username;

    @Value("${spring.rabbitmq.two.password}")
    private String password;

    @Value("${spring.rabbitmq.two.virtual-host}")
    private String virtualHost;

    /**
     * 定义与two的连接工厂
     */
    @Bean(name = "twoConnectionFactory")
    public ConnectionFactory twoConnectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);
        connectionFactory.setPublisherConfirms(true);
        connectionFactory.setPublisherReturns(true);
        return connectionFactory;
    }

    @Bean(name = "twoRabbitTemplate")
    public RabbitTemplate twoRabbitTemplate(@Qualifier("twoConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate twoRabbitTemplate = new RabbitTemplate(connectionFactory);
        twoRabbitTemplate.setMandatory(true);
        twoRabbitTemplate.setConnectionFactory(connectionFactory);
        twoRabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 确认消息送到交换机(Exchange)回调
             * @param correlationData
             * @param ack
             * @param cause
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                log.info("确认消息送到交换机(Exchange)结果:");
                log.info("相关数据:{}", correlationData);
                boolean ret = false;
                if (ack) {
                    log.info("消息发送到交换机成功, 消息 = {}", correlationData.getId());
                    //下面可自定义业务逻辑处理,如入库保存信息等

                } else {
                    log.error("消息发送到交换机失败! 消息: {}}; 错误原因:cause: {}", correlationData.getId(), cause);
                    //下面可自定义业务逻辑处理,如入库保存信息等

                }
            }
        });

        twoRabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {

            /**
             * 只要消息没有投递给指定的队列 就触发这个失败回调
             * @param message  投递失败的消息详细信息
             * @param replyCode 回复的状态码
             * @param replyText 回复的文本内容
             * @param exchange 当时这个消息发给那个交换机
             * @param routingKey 当时这个消息用那个路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                //获取消息id
                String messageId = message.getMessageProperties().getMessageId();
                // 内容
                String result = null;
                try {
                    result = new String(message.getBody(), "UTF-8");
                } catch (Exception e) {
                    log.error("消息发送失败{}", e);
                }
                log.error("消息发送失败, 消息ID = {}; 消息内容 = {}", messageId, result);
                //下面可自定义业务逻辑处理,如入库保存信息等
            }
        });
        return twoRabbitTemplate;
    }

    @Bean(name = "twoFactory")
    public SimpleRabbitListenerContainerFactory twoFactory(@Qualifier("twoConnectionFactory") ConnectionFactory connectionFactory,
                                                           SimpleRabbitListenerContainerFactoryConfigurer configurer) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        configurer.configure(factory, connectionFactory);
        return factory;
    }

    @Bean(name = "twoRabbitAdmin")
    public RabbitAdmin twoRabbitAdmin(@Qualifier("twoConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }
}

创建队列、交换机并绑定:


import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * 创建队列、交换机并绑定
 */
@Configuration
public class QueueConfig {
    @Resource(name = "oneRabbitAdmin")
    private RabbitAdmin oneRabbitAdmin;

    @Resource(name = "twoRabbitAdmin")
    private RabbitAdmin twoRabbitAdmin;

    @Value("${spring.rabbitmq.one.queue}")
    private String oneOutQueue;

    @Value("${spring.rabbitmq.one.queue}")
    private String oneRoutingKey;

    @Value("${spring.rabbitmq.two.queue}")
    private String twoOutQueue;

    @Value("${spring.rabbitmq.two.queue}")
    private String twoRoutingKey;

    @Value("${spring.rabbitmq.one.topic.exchange.name}")
    private String oneTopicExchange;

    @Value("${spring.rabbitmq.two.topic.exchange.name}")
    private String twoTopicExchange;

    @PostConstruct
    public void oneRabbitInit() {
        //声明交换机
        oneRabbitAdmin.declareExchange(new TopicExchange(oneTopicExchange, true, false));
        //声明队列
        oneRabbitAdmin.declareQueue(new Queue(oneOutQueue, true, false, false));
        //绑定队列及交换机
        oneRabbitAdmin.declareBinding(BindingBuilder.bind(new Queue(oneOutQueue, true, false, false))
                .to(new TopicExchange(oneTopicExchange, true, false))
                .with(oneRoutingKey + ".#"));
    }

    @PostConstruct
    public void twoRabbitInit() {
        //声明交换机
        twoRabbitAdmin.declareExchange(new TopicExchange(twoTopicExchange, true, false));
        //声明队列
        twoRabbitAdmin.declareQueue(new Queue(twoOutQueue, true));
        //绑定队列及交换机
        twoRabbitAdmin.declareBinding(BindingBuilder.bind(new Queue(twoOutQueue, true, false, false))
                .to(new TopicExchange(twoTopicExchange, true, false))
                .with(twoRoutingKey));
    }
}

消费者消费消息

不同vhost换containerFactory里面的工厂和queue

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import com.ziyun.iot.event.model.mysql.event.entity.UserEvent;
import com.ziyun.iot.event.model.mysql.event.mapper.EventMapper;
import com.ziyun.iot.event.model.mysql.event.mapper.UserEventMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;


@Slf4j
@Service
public class AddUserConsumer {

    @RabbitListener(queues = "${spring.rabbitmq.two.queue}", containerFactory = "twoFactory")
    public void addUserConsumer(Message message, Channel channel) {
        String body = new String(message.getBody(), StandardCharsets.UTF_8);
        log.info("收到rabbitmq消息: {}", body);
        try {
            //业务逻辑

                    //手动确认消息已经被消费
                    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                    log.info("新增用户:{} 消费消息成功:{}。", ssoIds, message.toString());
                } else {
                    log.info("新增用户:{} 消费消息失败:{}。", ssoIds, message.toString());
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

}

生产者发送消息

不同vhost换交换机和队列

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

import javax.annotation.Resource;

@Slf4j
@Service
public class SendMessage {

    @Resource(name = "oneRabbitTemplate")
    private RabbitTemplate oneRabbitTemplate;

    @Resource(name = "twoRabbitTemplate")
    private RabbitTemplate twoRabbitTemplate;

    @Value("${spring.rabbitmq.one.topic.exchange.name}")
    private String oneTopicExchange;

    @Value("${spring.rabbitmq.two.topic.exchange.name}")
    private String twoTopicExchange;

    @Value("${spring.rabbitmq.one.queue}")
    private String oneRoutingKey;

    @Value("${spring.rabbitmq.two.queue}")
    private String twoRoutingKey;

    public void sendToOneMessage(String msg) {
        oneRabbitTemplate.convertAndSend(oneTopicExchange, oneRoutingKey, msg);
    }

    public void sendToTwoMessage(String msg) {
        twoRabbitTemplate.convertAndSend(twoTopicExchange, twoRoutingKey, msg);
    }
}

就先说到这 \color{#008B8B}{ 就先说到这} 就先说到这
在下 A p o l l o \color{#008B8B}{在下Apollo} 在下Apollo
一个爱分享 J a v a 、生活的小人物, \color{#008B8B}{一个爱分享Java、生活的小人物,} 一个爱分享Java、生活的小人物,
咱们来日方长,有缘江湖再见,告辞! \color{#008B8B}{咱们来日方长,有缘江湖再见,告辞!} 咱们来日方长,有缘江湖再见,告辞!

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值