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.