RabbitMQ 多数据源配置

背景

公司多个研发团队,多套 RabbitMQ 地址,同一套地址中,也区分为多个 VirtualHost。如何控制某个 exchange/queue 只创建于某一个 virtualhost 中。

采用 Spring 的 @Configuration 配置方式。🔗 Spring AMQP

 

单数据源配置

  • org.springframework.amqp.rabbit.connection.CachingConnectionFactory

  • org.springframework.amqp.rabbit.core.RabbitTemplate

  • org.springframework.amqp.rabbit.core.RabbitAdmin

  • org.springframework.amqp.support.converter.SimpleMessageConverter

  • Listener

  • org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer

  • org.springframework.amqp.core.TopicExchange(DirectExchange、FanoutExchange、HeadersExchange)

  • org.springframework.amqp.core.Queue

  • org.springframework.amqp.core.Binding

假设需要配置一个用户创建的消息监听器,代码如下:

@Configuration
public class RabbitConfig {

    private static final String ADDRESSES = "localhost:5672";
    private static final String VIRTUALHOST = "vhost";
    private static final String USERNAME = "guest";
    private static final String PASSWORD = "guest";
    
    private static final String EXCHANGE_NAME = "exchange.biz.user";
    private static final String QUEUE_NAME = "queue.biz.user.add";
    private static final String ROUTING_KEY = "rk.biz.user.add.*";
    
    private static final Integer PREFETCH_COUNT = 10;

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setAddresses(ADDRESSES);
        factory.setVirtualHost(VIRTUALHOST);
        factory.setUsername(USERNAME);
        factory.setPassword(PASSWORD);
        return factory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        template.setMessageConverter(simpleMessageConverter());
        return template;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean
    public SimpleMessageConverter simpleMessageConverter() {
        return new SimpleMessageConverter();
    }

    // 业务监听器
    @Bean
    public UserMessageListener messageListener() {
        return new UserMessageListener();
    }

    @Bean
    public SimpleMessageListenerContainer messageListenerContainer(
            ConnectionFactory connectionFactory,
            RabbitAdmin rabbitAdmin,
            UserMessageListener listener) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.setRabbitAdmin(rabbitAdmin);
        container.setQueueNames(QUEUE_NAME);
        container.setMessageListener(listener);
        container.setPrefetchCount(PREFETCH_COUNT);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return container;
    }

    @Bean
    public TopicExchange exchange() {
        return new TopicExchange(EXCHANGE_NAME);
    }

    @Bean
    public Queue queue() {
        return new Queue(QUEUE_NAME);
    }

    @Bean
    public Binding binding() {
        return BindingBuilder.bind(queue()).to(exchange()).with(ROUTING_KEY);
    }
}

配置说明:

RabbitTemplate:用于发送和接收消息的关键类。

RabbitAdmin:其底层实现就是从 Spring 容器中获取 exchange、bingding、queue、routingkey 的 bean,然后使用 rabbitTemplate 的 execute 方法执行对应的声明、修改、删除等一系列 rabbitMQ 基础功能操作。「这样就不需要手动在控制台添加交换器与队列了。」

 

多数据源配置

若有多套 RabbitMQ 地址,或者同一套地址中,区分了多个 virtualhost,配置有所不同。

一般来说,addresses 和 virtualhost 相同的,称之为同一数据源,同一数据源的消息监听器配置在一个 Config 类文件中。

配置关键点在于:

  1. 为每个 Bean 设置名称,通过 @Qualifier 注解指定使用的 Bean。

  2. 通过 @Primary 设置主要工厂、模板、管理。

如果不使用 @Qualifier 指定 Bean,会在每个数据源中都创建所有的交换器和队列。比如:

addresses 相同,virtualhost 分别为 vhost1 和 vhost2,使用单数据源的配置方式,在两个 Config 类中分别声明了 exchange1、queue1 和 exchange2、queue2,在 RabbitMQ 的控制台中,我们会发现,vhost1 和 vhost2 中会同时存在两个交换器和队列。

如果通过 @Qualifier 指定,则 vhost1 中只会有 exchange1、queue1,vhost2 中只会有 exchange2、queue2。

代码如下:

@Configuration
public class RabbitConfig {

    private static final String ADDRESSES = "localhost:5672";
    private static final String VIRTUALHOST = "firsthost";
    private static final String USERNAME = "guest";
    private static final String PASSWORD = "guest";

    private static final String EXCHANGE_NAME = "exchange.biz.first";
    private static final String QUEUE_NAME = "queue.biz.first";
    private static final String ROUTING_KEY = "rk.biz.first.*";

    private static final String DEAD_LETTER_EXCHANGE_NAME = "dle.biz.first";
    private static final String DEAD_LETTER_QUEUE_NAME = "dlq.biz.first";
    private static final String DEAD_LETTER_ROUTING_KEY = "dle.biz.first";

    private static final Integer PREFETCH_COUNT = 10;

    @Bean(name = "firstConnectionFactory")
    @Primary
    public ConnectionFactory firstConnectionFactory() {
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setAddresses(ADDRESSES);
        factory.setVirtualHost(VIRTUALHOST);
        factory.setUsername(USERNAME);
        factory.setPassword(PASSWORD);
        return factory;
    }

    @Bean(name = "firstRabbitTemplate")
    @Primary
    public RabbitTemplate firstRabbitTemplate(@Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        template.setMessageConverter(new SimpleMessageConverter());
        return template;
    }

    @Bean(name = "firstRabbitAdmin")
    @Primary
    public RabbitAdmin firstRabbitAdmin(@Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean(name = "firstMessageEventListener")
    public FirstMessageEventListener firstMessageEventListener() {
        return new FirstMessageEventListener();
    }

    @Bean
    public SimpleMessageListenerContainer firstMessageListenerContainer(
            @Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory,
            @Qualifier("firstRabbitAdmin") RabbitAdmin rabbitAdmin,
            @Qualifier("firstMessageEventListener") FirstMessageEventListener listener) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.setRabbitAdmin(rabbitAdmin);
        container.setQueueNames(QUEUE_NAME, DEAD_LETTER_QUEUE_NAME);
        container.setMessageListener(listener);
        container.setPrefetchCount(PREFETCH_COUNT);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return container;
    }

    @Bean
    public TopicExchange firstExchange(@Qualifier("firstRabbitAdmin") RabbitAdmin rabbitAdmin) {
        TopicExchange topicExchange = new TopicExchange(EXCHANGE_NAME);
        topicExchange.setAdminsThatShouldDeclare(rabbitAdmin);
        return topicExchange;
    }

    @Bean
    public Queue firstQueue(@Qualifier("firstRabbitAdmin") RabbitAdmin rabbitAdmin) {
        Map<String, Object> arguments = Maps.newHashMap();
        arguments.put("x-dead-letter-exchange", firstDeadExchange(rabbitAdmin).getName());
        arguments.put("x-dead-letter-routing-key", firstDeadBinding(rabbitAdmin).getRoutingKey());
        Queue queue = new Queue(QUEUE_NAME, true, false, false, arguments);
        queue.setAdminsThatShouldDeclare(rabbitAdmin);
        return queue;
    }

    @Bean
    public Binding firstBinding(@Qualifier("firstRabbitAdmin") RabbitAdmin rabbitAdmin) {
        Binding binding = BindingBuilder
                .bind(firstQueue(rabbitAdmin))
                .to(firstExchange(rabbitAdmin))
                .with(ROUTING_KEY);
        binding.setAdminsThatShouldDeclare(rabbitAdmin);
        return binding;
    }

    @Bean
    public TopicExchange firstDeadExchange(@Qualifier("firstRabbitAdmin") RabbitAdmin rabbitAdmin) {
        TopicExchange topicExchange = new TopicExchange(DEAD_LETTER_EXCHANGE_NAME);
        topicExchange.setAdminsThatShouldDeclare(rabbitAdmin);
        return topicExchange;
    }

    @Bean
    public Queue firstDeadQueue(@Qualifier("firstRabbitAdmin") RabbitAdmin rabbitAdmin) {
        Queue queue = new Queue(DEAD_LETTER_QUEUE_NAME);
        queue.setAdminsThatShouldDeclare(rabbitAdmin);
        return queue;
    }

    @Bean
    public Binding firstDeadBinding(@Qualifier("firstRabbitAdmin") RabbitAdmin rabbitAdmin) {
        Binding binding = BindingBuilder
                .bind(firstDeadQueue(rabbitAdmin))
                .to(firstDeadExchange(rabbitAdmin))
                .with(DEAD_LETTER_ROUTING_KEY);
        binding.setAdminsThatShouldDeclare(rabbitAdmin);
        return binding;
    }
}

 

单数据源 xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/rabbit
      http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!-- 消息转换器 -->
    <bean id="simpleMessageConverter" class="org.springframework.amqp.support.converter.SimpleMessageConverter"/>

    <!-- 连接工厂 -->
    <bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
        <property name="addresses" value="${rabbitmq.addresses}" />
        <property name="virtualHost" value="${rabbitmq.virtualhost}"/>
        <property name="username" value="${rabbitmq.username}"/>
        <property name="password" value="${rabbitmq.password}"/>
    </bean>

    <rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory"/>
    <rabbit:admin id="middlewareAdmin" connection-factory="rabbitConnectionFactory"/>

    <!-- 定义exchange -->
    <rabbit:topic-exchange name="exchange.biz.action">
        <rabbit:bindings>
            <rabbit:binding queue="myQueue" pattern="exchange.biz.action.*"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!-- 定义dead-letter-exchange -->
    <rabbit:topic-exchange name="dle.biz.action">
        <rabbit:bindings>
            <rabbit:binding queue="deadQueue" pattern="dle.biz.action.#"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!--定义queue -->
    <rabbit:queue id="myQueue" name="queue.biz.action">
        <rabbit:queue-arguments>
            <entry key="x-dead-letter-exchange" value="dle.biz.action"/>
            <entry key="x-dead-letter-routing-key" value="dlq.biz.action"/>
        </rabbit:queue-arguments>
    </rabbit:queue>

    <!--定义dead-letter-queue -->
    <rabbit:queue id="deadQueue" name="dlq.biz.action"/>

    <!-- listener 手动确认-->
    <rabbit:listener-container acknowledge="manual" prefetch="10">
        <rabbit:listener queues="myQueue" ref="myMessageListener"/>
        <rabbit:listener queues="deadQueue" ref="myMessageListener"/>
    </rabbit:listener-container>

    <bean id="myMessageListener" class="com.xxx.xxx.mq.listener.MyMessageEventListener"/>

</beans>

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值