SpringBoot集成多个RabbitMq(多个MQ链接)

##2023年12月16日 20:25:36  项目中使用RabbitMQ作为应用间信息互通,本次梳理下关于MQ的使用。

1、引入依赖

<!--  引入依赖,使用v2.5.6版本 -->
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.5.6</version>
        </dependency>
</dependencies> 

2、在Nacos中配置MQ信息,此次项目中使用了两个MQ实例(①已方提供MQ Server,本项目内各系统信息互通;②甲方提供MQ Server,与甲方做信息互通),本次介绍mq1

spring:
  rabbitmq:
    mq1:
      host: 127.0.0.1
      port: 5672
      username: admin
      password: ****
      enable: false ##队列是否启用,在Configuration中来确认是否初始化MQ
    mq2:
      host: 127.0.0.2
      port: 5672
      username: admin
      password: ***
      enable: false

3、编写配置文件

@Data
@Component("mq1RabbitmqConfig") 
@ConfigurationProperties(prefix = "spring.rabbitmq.mq1") //读取mq1的配置信息
@ConditionalOnProperty(name = "spring.rabbitmq.mq1.enable", havingValue = "true") //是否启用
public class MQ1RabbitConfiguration {
    private String host;
    private Integer port;
    private String username;
    private String password;

    @Autowired
    private ReturnCallBack returnCallBack;
    @Autowired
    private ConfirmCallBack confirmCallBack;

    @Bean(value = "mq1ConnectionFactory")   //命名mq1的ConnectionFactory,如果项目中只有一个mq则不必如此
    public ConnectionFactory createConnectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        //开启发送到交换机和队列的回调
        connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
        return connectionFactory;
    }
    @Bean(name = "mq1RabbitTemplate") //命名mq1的RabbitTemplate,如果项目中只有一个mq则不必如此
    public RabbitTemplate brainRabbitTemplate(@Qualifier("mq1ConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        //发送消息时设置强制标志,仅当提供了returnCallback时才适用
        rabbitTemplate.setMandatory(true);
        //确保消息是否发送到交换机,成功与失败都会触发
        rabbitTemplate.setConfirmCallback(confirmCallBack);
        //确保消息是否发送到队列,成功发送不触发,失败触发
        rabbitTemplate.setReturnsCallback(returnCallBack);
        return rabbitTemplate;
    }
    @Bean("mq1RabbitListenerContainerFactory")//命名mq1的RabbitListenerContainerFactory,如果项目中只有一个mq则不必如此
    SimpleRabbitListenerContainerFactory mq1RabbitListenerContainerFactory(
            SimpleRabbitListenerContainerFactoryConfigurer configurer,
            @Qualifier("mq1ConnectionFactory") ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory listenerContainerFactory = new SimpleRabbitListenerContainerFactory();
        configurer.configure(listenerContainerFactory, connectionFactory);
        return listenerContainerFactory;
    }
}

4、编写回调callback,此一举措是为了记录交换机、队列本身是否健康,如业务无此细纠,也可不记录。

ConfirmCallBack.java :消息发送到交换机成功、失败都会回调
ReturnCallBack.java:消息发送队列失败回调
@Component
@Slf4j
public class ConfirmCallBack implements RabbitTemplate.ConfirmCallback {
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String s) {
        if(!ack){
            log.info("消息发送交换机失败:{}",s);
        }else{
            log.info("消息发送交换机成功");
        }
    }
}

@Component
@Slf4j
public class ReturnCallBack implements RabbitTemplate.ReturnsCallback {
    @Override
    public void returnedMessage(ReturnedMessage returnedMessage) {
        log.info("消息发送队列失败:{}", JSON.toJSON(returnedMessage));
    }
}

5、MQ工具类,方便各业务点调用,减少代码冗余。

@Component
@ConditionalOnProperty(name = "spring.rabbitmq.mq1.enable", havingValue = "true") //是否启用
public class RabbitMQUtil {

    @Resource(name = "mq1RabbitTemplate")  //初始化mq1的RabbitTemplate对象,如果项目中只有一个MQ,则无需这么麻烦,
    private RabbitTemplate mq1RabbitTemplate;

    public void test1(List ob){
     //发送到fanout类型的交换机,跟这个交换机绑定的队列都会收到这一条消息,故第二个参数routekey无需填写 mq1RabbitTemplate.convertAndSend("fanout.exchange",null, JSON.toJSONString(ob)); } public void test2(String str){
      //发送到direct类型的交换机,根据routekey去找发送到哪个队列里,只有这一个队列才能收到这条消息 mq1RabbitTemplate.convertAndSend("direct.exchange","routekey",str); } }

6、生产者Producer

@Service
public class producer {

    @Autowired(required = false)  //一定要加required=false,否则mq配置不启用时,这里会报错
    private RabbitMQUtil rabbitMQUtil;

    public void producer(List ob) {
        
        if(rabbitMQUtil != null){
            rabbitMQUtil.test1(ob);
        }
    }
}

7、消费者Consumer(Listener)

①此处采用动态绑定生成队列的方式,可以满足多据点的模式,部署A据点后,根据A的特征值自动创建关于A的exchage、queue(mq1Queue_A),免去了在java类中显式声明并绑定exchage和queue的代码

②此处使用了死信队列 x-dead-letter-exchange,可以在msg消费失败时让msg进入对应死信队列,进而监听死信队列进行补偿操作。

③我们也可以用死信队列去做延时队列:给一个队列提前绑定好死信队列,发送消息到队列并设置消息的timeout,到点未消费也会扔到死信队列,通过监听对应的死信队列达到延时队列的作用

@Slf4j
@Component
@ConditionalOnExpression(value = "${spring.rabbitmq.mq1.enable:false} && '${aa.bb}' eq 'cc' ")  //mq1队列启用,并且满足某些条件才初始化
public class MQ1ccRabbitListener {
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "mq1Queue_A"
                    , arguments = {@Argument(name = "x-dead-letter-exchange", value = "dead.letter.exchange"),
                    @Argument(name = "x-dead-letter-routing-key", value = "dead.letter.routekey")}
        ), exchange = @Exchange(name = "mq1_fanout_exchange",type = ExchangeTypes.FANOUT) ),ackMode = "MANUAL") public void mq1listener1(Message message,Channel channel, String poJson) throws IOException { try { MyClass po = JSONUtil.toBean(poJson,MyClass.class); if(myService.saveOrUpdate(po)){ channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); }else { //第二个参数为false时,进入死信队列消费,true时重新进入队列头 channel.basicReject(message.getMessageProperties().getDeliveryTag(),false); } } catch (Exception e) { log.info("队列{}消费省级目录信息失败:{},异常信息:{}",RabbitMqConstants.DOWN_REGION_QUEUES + "_${unified.areaCode}",poJson,e.getMessage()); //第二个参数为false时,进入死信队列消费,true时重新进入队列头 channel.basicReject(message.getMessageProperties().getDeliveryTag(),false); } } }

8、显式绑定死信队列的exchange和queue(其他队列也可以这样绑,死信队列和其他队列没有任何区别,只是说可以在普通队列中去用 x-dead-letter-exchange 去标记一个队列的死信队列,让普通队列消费失败时,将消息扔到死信队列。

@Configuration
@ConditionalOnProperty(name = "spring.rabbitmq.mq1.enable", havingValue = "true")
public class BrainDiedConfig {

    @Resource(name = "mq1RabbitTemplate")
    private RabbitTemplate brainRabbitTemplate;
    @Bean("dead.letter.exchange")
    public Exchange diedExchange(){
        return new DirectExchange("dead.letter.exchange");
    }

    @Bean("dead.letter.queues")
    public Queue diedQueue(){
        return new Queue("dead.letter.queues",true);
    }

    @Bean
    public Binding bindingDied(@Qualifier("dead.letter.queues") Queue diedQueue,@Qualifier("dead.letter.exchange") Exchange diedExchange){
        return BindingBuilder.bind(diedQueue).to(diedExchange).with("dead.letter.routeke").noargs();
    }
}

9、消费死信队列

    @RabbitListener(queues = "dead.letter.queues")
    public void diedLetterConsumer(Message message, Channel channel, String poJson){
        try {
            String queueName = (String) message.getMessageProperties().getHeaders().get("x-first-death-queue");
            String exchangeName = (String) message.getMessageProperties().getHeaders().get("x-first-death-exchange");
            dao.save(queueName, exchangeName, poJson);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

10、死信队列建表语句

create table died_letter
(
    id            bigint auto_increment
        primary key,
    queue_name    varchar(50)                        not null comment '队列名称'
    exchange_name varchar(50)                        not null comment '交换机名称',
    msg_json      mediumtext                         not null comment '队列信息',
    create_time   datetime default CURRENT_TIMESTAMP not null comment '生成时间',
    is_handle     tinyint  default 0                 not null comment '是否被处理,0:未处理;1:已处理'
)
    comment '死信队列信息';
  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot可以很容易地配置多个RabbitMQ实例。以下是配置多个RabbitMQ实例的步骤: 1. 在application.properties文件中添加多个RabbitMQ实例的配置信息。例如: spring.rabbitmq.host=host1 spring.rabbitmq.port=5672 spring.rabbitmq.username=user1 spring.rabbitmq.password=password1 spring.rabbitmq.second.host=host2 spring.rabbitmq.second.port=5672 spring.rabbitmq.second.username=user2 spring.rabbitmq.second.password=password2 2. 创建多个RabbitTemplate和ConnectionFactory bean。例如: @Configuration public class RabbitMQConfig { @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setHost(env.getProperty("spring.rabbitmq.host")); connectionFactory.setPort(env.getProperty("spring.rabbitmq.port", Integer.class)); connectionFactory.setUsername(env.getProperty("spring.rabbitmq.username")); connectionFactory.setPassword(env.getProperty("spring.rabbitmq.password")); return connectionFactory; } @Bean public RabbitTemplate rabbitTemplate() { RabbitTemplate template = new RabbitTemplate(connectionFactory()); return template; } @Bean(name = "secondConnectionFactory") public ConnectionFactory secondConnectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setHost(env.getProperty("spring.rabbitmq.second.host")); connectionFactory.setPort(env.getProperty("spring.rabbitmq.second.port", Integer.class)); connectionFactory.setUsername(env.getProperty("spring.rabbitmq.second.username")); connectionFactory.setPassword(env.getProperty("spring.rabbitmq.second.password")); return connectionFactory; } @Bean(name = "secondRabbitTemplate") public RabbitTemplate secondRabbitTemplate() { RabbitTemplate template = new RabbitTemplate(secondConnectionFactory()); return template; } } 3. 在需要使用RabbitMQ的地方,注入对应的RabbitTemplate或ConnectionFactory bean即可。例如: @Autowired private RabbitTemplate rabbitTemplate; @Autowired @Qualifier("secondRabbitTemplate") private RabbitTemplate secondRabbitTemplate; 使用以上步骤,就可以在Spring Boot应用中配置多个RabbitMQ实例了。 ### 回答2: 在Spring Boot配置多个RabbitMQ会让应用程序变得更加灵活,这意味着应用可以与多个RabbitMQ实例连接,并能够发送和接收消息。 Spring Boot通过在配置文件(application.properties或application.yml)中定义多个RabbitMQ实例来实现多个RabbitMQ配置。下面是在同一应用程序中配置两个RabbitMQ实例的示例: 在application.yml文件中添加以下内容: ``` spring.rabbitmq.host=server1 spring.rabbitmq.port=5672 spring.rabbitmq.username=user1 spring.rabbitmq.password=password1 spring.rabbitmq.virtual-host=/vhost1 spring.rabbitmq.second.host=server2 spring.rabbitmq.second.port=5672 spring.rabbitmq.second.username=user2 spring.rabbitmq.second.password=password2 spring.rabbitmq.second.virtual-host=/vhost2 ``` 在上面的示例中,我们定义了两个RabbitMQ实例,一个用于服务器1,另一个用于服务器2。对于每个实例,我们指定了名称,主机名,端口,用户名,密码和虚拟主机。 为了使用以上配置,我们需要通过@Resource注解或@Autowired注解在Java类中定义一个RabbitTemplate bean。同时在需要连接第二个RabbitMQ实例的地方指定使用哪个RabbitMQ实例即可。例如: ``` @SpringBootApplication public class DemoApplication implements CommandLineRunner { @Autowired private RabbitTemplate rabbitTemplate; @Autowired @Qualifier("secondRabbitTemplate") private RabbitTemplate secondRabbitTemplate; public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Override public void run(String... args) throws Exception { rabbitTemplate.convertAndSend("queue1", "message1"); secondRabbitTemplate.convertAndSend("queue2", "message2"); } } ``` 在上面的代码中,我们注入了两个不同的RabbitTemplate实例,分别用于发送到不同的RabbitMQ实例。我们可以使用这两个实例来发送消息到不同的队列。在这个例子中,我们把“message1”发送到“queue1”,把“message2” 发送到“queue2”。 总之,配置多个RabbitMQ实例可以让Spring Boot应用程序与多个RabbitMQ连接,并实现更复杂的异构应用程序。我们只需要简单地在配置文件中添加多个实例,并通过Java类中的@Resource或@Autowired注解即可使用。 ### 回答3: Spring Boot 是一种流行的 Java 框架,用于快速构建基于 Spring 框架的应用程序。RabbitMQ 是一个流行的开源消息队列,常用于构建分布式系统。Spring Boot 提供了对 RabbitMQ 的支持,并允许用户配置多个 RabbitMQ配置多个 RabbitMQ,可以通过在 application.properties 或 application.yml 文件中添加多个 RabbitMQ配置来实现。如下所示: application.yml: ``` spring: rabbitmq: 1: host: localhost port: 5672 username: guest password: guest virtual-host: / 2: host: localhost port: 5673 username: guest password: guest virtual-host: / ... ``` application.properties: ``` spring.rabbitmq.1.host=localhost spring.rabbitmq.1.port=5672 spring.rabbitmq.1.username=guest spring.rabbitmq.1.password=guest spring.rabbitmq.1.virtual-host=/ spring.rabbitmq.2.host=localhost spring.rabbitmq.2.port=5673 spring.rabbitmq.2.username=guest spring.rabbitmq.2.password=guest spring.rabbitmq.2.virtual-host=/ ... ``` 其中,`1` 和 `2` 表示 RabbitMQ 的标识符。我们可以根据需要添加更多的标识符。在我们的应用程序中,我们可以使用以下方式注入多个RabbitMQ: ``` @Bean @Primary public ConnectionFactory connectionFactory1() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost", 5672); connectionFactory.setUsername("guest"); connectionFactory.setPassword("guest"); return connectionFactory; } @Bean public RabbitTemplate rabbitTemplate1(ConnectionFactory connectionFactory1) { return new RabbitTemplate(connectionFactory1); } @Bean public SimpleMessageListenerContainer messageListenerContainer1( ConnectionFactory connectionFactory1) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory1); container.setQueueNames("queue1"); container.setDefaultRequeueRejected(false); container.setMessageListener(messageListenerAdapter1()); return container; } @Bean public MessageListenerAdapter messageListenerAdapter1() { return new MessageListenerAdapter(new MyMessageListener()); } @Bean public ConnectionFactory connectionFactory2() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost", 5673); connectionFactory.setUsername("guest"); connectionFactory.setPassword("guest"); return connectionFactory; } @Bean public RabbitTemplate rabbitTemplate2(ConnectionFactory connectionFactory2) { return new RabbitTemplate(connectionFactory2); } @Bean public SimpleMessageListenerContainer messageListenerContainer2( ConnectionFactory connectionFactory2) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory2); container.setQueueNames("queue2"); container.setDefaultRequeueRejected(false); container.setMessageListener(messageListenerAdapter2()); return container; } @Bean public MessageListenerAdapter messageListenerAdapter2() { return new MessageListenerAdapter(new MyMessageListener()); } ``` 这样,在我们的应用程序中就可以使用`rabbitTemplate1`和`rabbitTemplate2`来发送消息,使用 `messageListenerContainer1`和`messageListenerContainer2`监听队列消息。 总结: 在 Spring Boot 配置多个 RabbitMQ 的过程中,我们需要在 application.properties 或 application.yml 中添加多个配置,注入多个 ConnectionFactory,RabbitTemplate 和 SimpleMessageListenerContainer 来进行发送和监听消息。同时,我们还需要根据实际情况调整配置,确保消息传递的正确性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值