Spring boot using RabbitMQ code example

Keywords: Programming Spring RabbitMQ

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);
    }
}

Posted by darkvengance on Sun, 05 Apr 2020 18:23:16 -0700