In practical development, many scenarios require asynchronous processing, where RabbitMQ is required, and as the number of scenarios increases, programs may need to connect multiple RabbitMQs.SpringBoot itself provides a default configuration to quickly configure the connection to RabbitMQ, but only one RabbitMQ can be connected. When multiple RabbitMQs need to be connected, the default configuration is not applicable and each connection needs to be written separately.
In the SpringBoot framework, two common classes are:
- RabbitTemplate: Used as production and consumption messages;
- RabbitAdmin: Used as an assertion, deletion, and binding relationship between queues and switches.
So if we connect multiple RabbitMQ s, we need to re-establish the connection and re-implement the two classes. The code is as follows:
To configure
The application.properties configuration file requires two connections to be configured:
server.port=8080 # rabbitmq v2.spring.rabbitmq.host=host v2.spring.rabbitmq.port=5672 v2.spring.rabbitmq.username=username v2.spring.rabbitmq.password=password v2.spring.rabbitmq.virtual-host=virtual-host #consume manual ack v2.spring.rabbitmq.listener.simple.acknowledge-mode=manual #1. When the mandatory flag bit is set to true, # If exchange cannot find a suitable queue to store messages based on its type and message routingKey, # The broker then calls the basic.return method to return the message to the producer; #2. When mandatory is set to false, broker discards the message directly; in general, # The mandatory flag tells the broker proxy server to route messages to at least one queue. # Otherwise, the message is return ed to the sender; v2.spring.rabbitmq.template.mandatory=true #publisher confirms send confirmation v2.spring.rabbitmq.publisher-confirms=true #returns callback : # 1. exchange not delivered # 2. Message callback returnCallback that delivers exchange without queue. (Note) When 2 occurs, publisher-confirms calls back true v2.spring.rabbitmq.publisher-returns=true v2.spring.rabbitmq.listener.simple.prefetch=5 # rabbitmq v1.spring.rabbitmq.host=host v1.spring.rabbitmq.port=5672 v1.spring.rabbitmq.username=username v1.spring.rabbitmq.password=password v1.spring.rabbitmq.virtual-host=virtual-host #consume manual ack v1.spring.rabbitmq.listener.simple.acknowledge-mode=manual #1. When the mandatory flag bit is set to true, # If exchange cannot find a suitable queue to store messages based on its type and message routingKey, # The broker then calls the basic.return method to return the message to the producer; #2. When mandatory is set to false, broker discards the message directly; in general, # The mandatory flag tells the broker proxy server to route messages to at least one queue. # Otherwise, the message is return ed to the sender; v1.spring.rabbitmq.template.mandatory=true #publisher confirms send confirmation v1.spring.rabbitmq.publisher-confirms=true #returns callback : # 1. exchange not delivered # 2. Message callback returnCallback that delivers exchange without queue. (Note) When 2 occurs, publisher-confirms calls back true v1.spring.rabbitmq.publisher-returns=true v1.spring.rabbitmq.listener.simple.prefetch=5
Rewrite Connection Factory
It is important to note that in the case of multiple sources, a connection needs to be annotated with @Primary to indicate the primary connection, which is used by default
package com.example.config.rabbitmq; import com.alibaba.fastjson.JSON; 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.Autowired; 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; /** * Created by shuai on 2019/4/23. */ @Configuration public class MultipleRabbitMQConfig { @Bean(name = "v2ConnectionFactory") public CachingConnectionFactory hospSyncConnectionFactory( @Value("${v2.spring.rabbitmq.host}") String host, @Value("${v2.spring.rabbitmq.port}") int port, @Value("${v2.spring.rabbitmq.username}") String username, @Value("${v2.spring.rabbitmq.password}") String password, @Value("${v2.spring.rabbitmq.virtual-host}") String virtualHost, @Value("${v2.spring.rabbitmq.publisher-confirms}") Boolean publisherConfirms, @Value("${v2.spring.rabbitmq.publisher-returns}") Boolean publisherReturns) { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setHost(host); connectionFactory.setPort(port); connectionFactory.setUsername(username); connectionFactory.setPassword(password); connectionFactory.setVirtualHost(virtualHost); connectionFactory.setPublisherConfirms(publisherConfirms); connectionFactory.setPublisherReturns(publisherReturns); return connectionFactory; } @Bean(name = "v2RabbitTemplate") public RabbitTemplate firstRabbitTemplate( @Qualifier("v2ConnectionFactory") ConnectionFactory connectionFactory, @Value("${v2.spring.rabbitmq.template.mandatory}") Boolean mandatory) { RabbitTemplate v2RabbitTemplate = new RabbitTemplate(connectionFactory); v2RabbitTemplate.setMandatory(mandatory); v2RabbitTemplate.setConfirmCallback((correlationData, ack, s) -> { if (!ack) { // LOGGER.info('{} failed to send RabbitMQ message ack confirmation: [{}], this.name, JSON.toJSONString(object))); } else { // LOGGER.info('{} send RabbitMQ message ack confirmation success: [{}], this.name, JSON.toJSONString(object))); } }); v2RabbitTemplate.setReturnCallback((message, code, s, exchange, routingKey) -> { // LOGGER.error('{}} sent RabbitMQ message returnedMessage sent by RabbitMQ message returnedMessage, exception, Exchange does not exist or sent to Exchange but did not send to Queue, message:[{}], code [{}], s[{}], exchange[{}]], routingKey [{}], new Object []{this.name, JSON.toJSON.toJSONSNString (message), JSON.toJSON JNSSONString (code), JSON.toJNSSON (toJSON) tring (code), JSON.toJNString (exchange), JNSSON.JSON (JSON) toJNString (exchange), JNSSON tring (rou)TingKey)}; }); return v2RabbitTemplate; } @Bean(name = "v2ContainerFactory") public SimpleRabbitListenerContainerFactory hospSyncFactory( @Qualifier("v2ConnectionFactory") ConnectionFactory connectionFactory, @Value("${v2.spring.rabbitmq.listener.simple.acknowledge-mode}") String acknowledge, @Value("${v2.spring.rabbitmq.listener.simple.prefetch}") Integer prefetch ) { SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setAcknowledgeMode(AcknowledgeMode.valueOf(acknowledge.toUpperCase())); factory.setPrefetchCount(prefetch); return factory; } @Bean(name = "v2RabbitAdmin") public RabbitAdmin iqianzhanRabbitAdmin( @Qualifier("v2ConnectionFactory") ConnectionFactory connectionFactory) { RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); rabbitAdmin.setAutoStartup(true); return rabbitAdmin; } // mq primary connection @Bean(name = "v1ConnectionFactory") @Primary public CachingConnectionFactory publicConnectionFactory( @Value("${v1.spring.rabbitmq.host}") String host, @Value("${v1.spring.rabbitmq.port}") int port, @Value("${v1.spring.rabbitmq.username}") String username, @Value("${v1.spring.rabbitmq.password}") String password, @Value("${v1.spring.rabbitmq.virtual-host}") String virtualHost, @Value("${v1.spring.rabbitmq.publisher-confirms}") Boolean publisherConfirms, @Value("${v1.spring.rabbitmq.publisher-returns}") Boolean publisherReturns) { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setHost(host); connectionFactory.setPort(port); connectionFactory.setUsername(username); connectionFactory.setPassword(password); connectionFactory.setVirtualHost(virtualHost); connectionFactory.setPublisherConfirms(publisherConfirms); connectionFactory.setPublisherReturns(publisherReturns); return connectionFactory; } @Bean(name = "v1RabbitTemplate") @Primary public RabbitTemplate publicRabbitTemplate( @Qualifier("v1ConnectionFactory") ConnectionFactory connectionFactory, @Value("${v1.spring.rabbitmq.template.mandatory}") Boolean mandatory) { RabbitTemplate v1RabbitTemplate = new RabbitTemplate(connectionFactory); v1RabbitTemplate.setMandatory(mandatory); v1RabbitTemplate.setConfirmCallback((correlationData, ack, s) -> { if (!ack) { // LOGGER.info('{} failed to send RabbitMQ message ack confirmation: [{}], this.name, JSON.toJSONString(object))); } else { // LOGGER.info('{} send RabbitMQ message ack confirmation success: [{}], this.name, JSON.toJSONString(object))); } }); v1RabbitTemplate.setReturnCallback((message, code, s, exchange, routingKey) -> { // LOGGER.error('{}} sent RabbitMQ message returnedMessage sent by RabbitMQ message returnedMessage, exception, Exchange does not exist or sent to Exchange but did not send to Queue, message:[{}], code [{}], s[{}], exchange[{}]], routingKey [{}], new Object []{this.name, JSON.toJSON.toJSONSNString (message), JSON.toJSON JNSSONString (code), JSON.toJNSSON (toJSON) tring (code), JSON.toJNString (exchange), JNSSON.JSON (JSON) toJNString (exchange), JNSSON tring (rou)TingKey)}; }); return v1RabbitTemplate; } @Bean(name = "v1ContainerFactory") @Primary public SimpleRabbitListenerContainerFactory insMessageListenerContainer( @Qualifier("v1ConnectionFactory") ConnectionFactory connectionFactory, @Value("${v1.spring.rabbitmq.listener.simple.acknowledge-mode}") String acknowledge, @Value("${v1.spring.rabbitmq.listener.simple.prefetch}") Integer prefetch) { SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setAcknowledgeMode(AcknowledgeMode.valueOf(acknowledge.toUpperCase())); factory.setPrefetchCount(prefetch); return factory; } @Bean(name = "v1RabbitAdmin") @Primary public RabbitAdmin publicRabbitAdmin( @Qualifier("v1ConnectionFactory") ConnectionFactory connectionFactory) { RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); rabbitAdmin.setAutoStartup(true); return rabbitAdmin; } }
Create Exchange, Queue, and Bind
Once RabbitAdmin is implemented, we need to create switches and queues based on RabbitAdmin and establish binding relationships
package com.example.config.rabbitmq; 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.context.annotation.Configuration; import javax.annotation.PostConstruct; import javax.annotation.Resource; /** * Create Queue, Exchange, and Bind Relationships * Created by shuai on 2019/5/16. */ @Configuration public class MyRabbitMQCreateConfig { @Resource(name = "v2RabbitAdmin") private RabbitAdmin v2RabbitAdmin; @Resource(name = "v1RabbitAdmin") private RabbitAdmin v1RabbitAdmin; @PostConstruct public void RabbitInit() { v2RabbitAdmin.declareExchange(new TopicExchange("exchange.topic.example.new", true, false)); v2RabbitAdmin.declareQueue(new Queue("queue.example.topic.new", true)); v2RabbitAdmin.declareBinding( BindingBuilder .bind(new Queue("queue.example.topic.new", true)) //Create Queue Directly .to(new TopicExchange("exchange.topic.example.new", true, false)) //Create switches directly to establish associations .with("routing.key.example.new")); //Specify Routing Key } }
Producer
In order to subsequently verify that each connection is established successfully and that messages can be produced, the producer sends a message using the newly generated RabbitTemplate here.
package com.example.topic; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component public class TopicProducer { @Resource(name = "v1RabbitTemplate") private RabbitTemplate v1RabbitTemplate; @Resource(name = "v2RabbitTemplate") private RabbitTemplate v2RabbitTemplate; public void sendMessageByTopic() { String content1 = "This is a topic type of the RabbitMQ message example from v1RabbitTemplate"; v1RabbitTemplate.convertAndSend( "exchange.topic.example.new", "routing.key.example.new", content1); String content2 = "This is a topic type of the RabbitMQ message example from v2RabbitTemplate"; v2RabbitTemplate.convertAndSend( "exchange.topic.example.new", "routing.key.example.new", content2); } }
Consumer
Note here that the ContainerFactory needs to be identified when configuring the consumer queue
package com.example.topic; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = "queue.example.topic.new", containerFactory = "v2ContainerFactory") public class TopicConsumer { @RabbitHandler public void consumer(String message) { System.out.println(message); } }
This completes the SpringBoot example of connecting multiple RabbitMQ sources, and then writes a test code to verify it.
Test Validation
package com.example.test; import com.example.topic.TopicProducer; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class RabbitMQMultipleTest { @Autowired private TopicProducer topicProducer; @Test public void topicProducerTest() { topicProducer.sendMessageByTopic(); } }
Execute the test code and verify that:
Verify that SpringBoot connects multiple RabbitMQ sources successfully!