Spring-rabbit consuming the same queue in multiple MQ

Source: Internet
Author: User
Tags rabbitmq


Because of the large size of the business, the use of multiple RABBITMQ server processing, in each RABBITMQ to establish the same exchange, the use of client-side Shard, the producer according to hash distribution messages to different servers.
As a consumer, you must be able to support the same exchange that consumes all rabbitmq.



Here, because of 4 MQ, we write the dead array subscript in the code.


package cn.jpush.sms.common;

import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
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.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
@EnableRabbit
public class RabbitMQConfig {

    @Autowired
    private RabbitProperties rabbitProperties;

    @Bean
    public ConnectionFactory connectionFactory() {
        return buildConnectionFactory(0);
    }

    @Bean
    public ConnectionFactory connectionFactory1() {
        return buildConnectionFactory(1);
    }

    @Bean
    public ConnectionFactory connectionFactory2() {
        return buildConnectionFactory(2);
    }

    @Bean
    public ConnectionFactory connectionFactory3() {
        return buildConnectionFactory(3);
    }

    private ConnectionFactory buildConnectionFactory(int i) {
        List<RabbitAccount> list = rabbitProperties.getAccount();
        RabbitAccount rabbitAccount = list.get(i);
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory(rabbitAccount.getHostname());
        connectionFactory.setUsername(rabbitAccount.getUsername());
        connectionFactory.setPassword(rabbitAccount.getPassword());
        connectionFactory.setPort(rabbitAccount.getPort());
        return connectionFactory;
    }

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory1() {
        return buildRabbitListenerContainerFactory(connectionFactory1(),rabbitAdmin1());
    }

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory2() {
        return buildRabbitListenerContainerFactory(connectionFactory2(),rabbitAdmin2());
    }

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory3() {
        return buildRabbitListenerContainerFactory(connectionFactory3(),rabbitAdmin3());
    }

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
        return buildRabbitListenerContainerFactory(connectionFactory(),rabbitAdmin());
    }

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

    @Bean
    public RabbitAdmin rabbitAdmin1() {
        RabbitAdmin r = new RabbitAdmin(connectionFactory1());
        return r;
    }

    @Bean
    public RabbitAdmin rabbitAdmin2() {
        RabbitAdmin r = new RabbitAdmin(connectionFactory2());
        return r;
    }

    @Bean
    public RabbitAdmin rabbitAdmin3() {
        RabbitAdmin r = new RabbitAdmin(connectionFactory3());
        return r;
    }

    private MySimpleRabbitListenerContainerFactory buildRabbitListenerContainerFactory(ConnectionFactory connectionFactory, RabbitAdmin rabbitAdmin) {
        MySimpleRabbitListenerContainerFactory factory = new MySimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setRabbitAdmin(rabbitAdmin);
        factory.setConcurrentConsumers(8);
        factory.setMaxConcurrentConsumers(10);
        factory.setPrefetchCount(1);
        factory.setAcknowledgeMode(AcknowledgeMode.NONE);
        return factory;
    }

}


Because the native Simplerabbitlistenercontra will configure a rabbitadmin for queue maintenance by default, the ConnectionFactory configured when this rabbitmqadmin is generated is global, This causes the Rabbitadmin to be unavailable.
We must manually inject the rabbitadmin and specify the correct cinnectionfactory.


package cn.jpush.sms.common;

import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;

public class MySimpleRabbitListenerContainerFactory extends SimpleRabbitListenerContainerFactory {

    private RabbitAdmin rabbitAdmin;

    @Override
    protected void initializeContainer (SimpleMessageListenerContainer instance) {
        super.initializeContainer (instance);
        // After initializing the Container, manually inject a RabbitAdmin
        // RabbitAdmin was originally designed so that only one instance is needed in the ApplicationContext. The main thing it does is queue declarations.
        // RabbitAdmin will scan all the Quene, Exchange, routeKey in the Context, and make a queue configuration declaration on the configured Connection. This design is normal when only one rabbitmq server is connected.
        // However, in the case of accessing multiple mqs, a RabbitAdmin must be configured in each Connection, otherwise redeclaration to the server cannot be performed.
        // A side effect of customizing multiple RabbitAdmins is that each Connection will declare all the queues configured in the project and will not distinguish between containerFactory.
        // Multiple mq servers are connected in the project, but all are queues with the same name. Even if there are side effects of multiple declarations, the final manifestation is the same as the declaration once.
        instance.setRabbitAdmin (rabbitAdmin);
    }

    public void setRabbitAdmin (RabbitAdmin rabbitAdmin) {
        this.rabbitAdmin = rabbitAdmin;
    }



}


If more than one MQ is attached, the queues are the same, and no problem occurs.
If more than one MQ is connected, but the queues are different, then Rabbitadmin declares the queue to declare all the queue configurations in the project to each server, and the Quene,exchange for each MQ is the same.
If more than one MQ is connected, but some quene,exchange only to configure a single MQ, it is recommended to peel off the project, separate service consumption.
Or have the ability to retrofit the Rabbitadmin code, declaring that the queue is separate from the Containerfactory zone and only responsible for declaring the configuration of the owning server.



Add handle by using annotations


package cn.jpush.sms.handler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class PushDeliveryProcessHandler {
    private static final Logger LOG = LoggerFactory.getLogger(PushDeliveryProcessHandler.class);

    @RabbitListener(containerFactory = "rabbitListenerContainerFactory1", bindings = @QueueBinding(value = @Queue(value = "${rb.queue}", durable = "false"), exchange = @Exchange(value = "${rb.exchange}", type = "direct"), key = "${rb.routeKey}"))
    @RabbitListener(containerFactory = "rabbitListenerContainerFactory2", bindings = @QueueBinding(value = @Queue(value = "${rb.queue}", durable = "false"), exchange = @Exchange(value = "${rb.exchange}", type = "direct"), key = "${rb.routeKey}"))
    @RabbitListener(containerFactory = "rabbitListenerContainerFactory3", bindings = @QueueBinding(value = @Queue(value = "${rb.queue}", durable = "false"), exchange = @Exchange(value = "${rb.exchange}", type = "direct"), key = "${rb.routeKey}"))
    @RabbitListener(containerFactory = "rabbitListenerContainerFactory", bindings = @QueueBinding(value = @Queue(value = "${rb.queue}", durable = "false"), exchange = @Exchange(value = "${rb.exchange}", type = "direct"), key = "${rb.routeKey}"))

    public void handle(Message message) {
       //to do something
    }

}

The recommends that you refer to the name of the bean for a corresponding autoconfigurer configuration.

Because of the discovery, spring-boot, if there is no bean with the name rabbitlistenercontainerfactory, it will create such a type bean, but we do not want it to be created, then we will take it out of the naming, This will not let the Spring-boot automatic configuration misunderstanding.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.