Example explanation
- In this example, slf4j-log4j12 is used to manage logs, and the logback classic dependency of spring boot start AMQP needs to be excluded to prevent conflicts.
- Key points of message queuing
- Producers: procedures for sending messages
- Consumer: a program that listens for and receives consumer messages
- Message: a stream of binary data
- Queues: staging / storage area for messages
- Converter: a relay station for messages, used to receive distribution messages. There are fanout, direct, topic and headers
- Route: bind with the converter to route messages to the specified queue!
Dependency import
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<!--Exclude log conflicts-->
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
configuration file
spring:
rabbitmq:
host: 192.168.1.117
port: 5672
username: admin
password: admin
publisher-confirm-type: correlated #This must be configured to confirm the callback
publisher-returns: true
listener:
type: simple
simple:
acknowledge-mode: manual #Manual validation
prefetch: 1 #Limit sending data one at a time.
concurrency: 3 #Start several consumers in the same queue
max-concurrency: 3 #Maximum number of start-up consumers
#Retry policy related configuration
retry:
enabled: true #Whether retry is supported
max-attempts: 5
stateless: false
#Time policy multiplier factor
multiplier: 1.0
initial-interval: 1000ms
max-interval: 10000ms
default-requeue-rejected: true
Bean configuration class
@Configuration
public class RabbitMQConfig {
private static Logger log = LoggerFactory.getLogger(RabbitMQConfig.class);
@Autowired
private CachingConnectionFactory connectionFactory;
//Target converter, which type of converter you need to create
@Bean
public DirectExchange exchangeHello(){
Map<String, Object> eArguments = new HashMap<>();
//Backup switch parameters
eArguments.put("alternate-exchange", "exchange.ae");
return new DirectExchange("exchange.hello",true,false,eArguments);
}
//Backup converter
@Bean
public FanoutExchange exchangeAE(){
return new FanoutExchange("exchange.ae",true,false,null);
}
//Dead letter converter
@Bean
public TopicExchange exchangeDLX(){
return new TopicExchange("exchange.dlx",true,false,null);
}
//Target alignment
@Bean
public Queue queueHello() {
Map<String, Object> args = new HashMap<>();
//Declaration dead letter exchanger
args.put("x-dead-letter-exchange", "exchange.dlx");
//Declare dead letter routing key
args.put("x-dead-letter-routing-key", "dlx.test" );
//Declaration queue message expiration time 5000ms
args.put("x-message-ttl", 5000);
return new Queue("queue.hello",true,false,false,args);
}
//Backup pairs
@Bean
public Queue queueAE() {
return new Queue("queue.ae",true,false,false,null);
}
//Dead letter pair
@Bean
public Queue queueDLX() {
return new Queue("queue.dlx",true,false,false,null);
}
//Bind target to column
@Bean
public Binding bindingExchangeDirect(@Qualifier("queueHello")Queue queueHello, @Qualifier("exchangeHello") DirectExchange exchangeHello){
return BindingBuilder.bind(queueHello).to(exchangeHello).with("helloKey");
}
//Bind backup pair column
@Bean
public Binding bindingExchangeAE(@Qualifier("queueAE")Queue queueAE, @Qualifier("exchangeAE") FanoutExchange exchangeAE){
return BindingBuilder.bind(queueAE).to(exchangeAE);
}
//Bind dead letter pair column
@Bean
public Binding bindingExchangeDLX(@Qualifier("queueDLX")Queue queueAE, @Qualifier("exchangeDLX") TopicExchange exchangeDLX){
return BindingBuilder.bind(queueAE).to(exchangeDLX).with("dlx.*");
}
/**
* If you need a callback after the producer needs the message to be sent,
* You need to set the confirmallback object on the rabbitTemplate,
* Because different producers need different ConfirmCallback,
* If rabbitTemplate is set as a singleton bean,
* Then the actual confirmallback of all rabbittemplates is the confirmallback of the last statement.
* @return
*/
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
return template;
}
}
Queue producers
@Controller
public class HelloSender implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback{
private static Logger log = LoggerFactory.getLogger(HelloSender.class);
private RabbitTemplate rabbitTemplate;
//Construction method injection
@Autowired
public HelloSender(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
//This is to set the callback to receive and send the response
rabbitTemplate.setConfirmCallback(this);
//No effect if backup queue is set
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback(this);
}
@RequestMapping("/send")
@ResponseBody
public void send() {
CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
String sendMsg = "hello1 " + new Date();
System.out.println("Sender : " + sendMsg);
//convertAndSend(exchange: switch name, routingKey: routing key, object: sent message content, correlationData: message ID)
rabbitTemplate.convertAndSend("exchange.hello","helloKey", sendMsg,correlationId);
}
//Callback confirmation
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(ack){
log.info("Message sent successfully:correlationData({}),ack({}),cause({})",correlationData,ack,cause);
}else{
log.info("Message sending failed:correlationData({}),ack({}),cause({})",correlationData,ack,cause);
}
}
//When the message is sent to the converter, there is no column. If the backup column is configured, the callback will not take effect
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
log.info("Message loss:exchange({}),route({}),replyCode({}),replyText({}),message:{}",exchange,routingKey,replyCode,replyText,message);
}
}
Queue consumer
@Component
public class HelloConsumer {
private static Logger log = LoggerFactory.getLogger(HelloConsumer.class);
@RabbitHandler
@RabbitListener(queues = "queue.hello")
public void process(Message message, Channel channel) throws IOException {
log.info("receive: " + new String(message.getBody())+"<Thread Name:"+Thread.currentThread().getName()+"<thread id:>"+Thread.currentThread().getId());
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}
}
Start class enable RabbitMQ
@EnableRabbit
@SpringBootApplication
public class RabbitmqApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitmqApplication.class,args);
}
}