Spring Boot RabbitMQ delay message implementation full version

Overview


When I went to Netease for an interview, the interviewer asked me a question, saying

After placing an order, if the user has not paid and needs to cancel the order, what can I do?

My answer at the time was to scan the DB table with a scheduled task. The interviewer was not very satisfied and suggested:

It is impossible to achieve quasi-real-time notification with timed tasks. Is there any other way?

My answer at the time was:

You can use the queue. After the order is placed, send a message to the queue and specify the expiration time. When the time is up, the callback interface is executed.

After the interviewer listened, they stopped asking. In fact, my thinking at the time was correct, but I was not very professional. The professional word is to use 延迟消息.

In fact, there are some problems with the use of timed tasks. The original business system hoped that after 10 minutes, if the order is not paid, the order will be cancelled immediately and the product inventory will be released. However, once the amount of data is large, the time for obtaining the data of unpaid orders will be prolonged, and some orders will not be cancelled after 10 minutes, maybe 15 minutes, 20 minutes, etc. In this case, the inventory will not be released in time, and it will affect the odd number. With the delayed message, it is theoretically possible to cancel the order according to the set time.

At present, most of the articles on the Internet about using RabbitMQ to implement delayed messages are about how to use RabbitMQ's dead letter queue to implement it. The implementation scheme seems to be cumbersome and complicated, and it is still implemented using the original RabbitMQ Client API, which is even more verbose.

Spring Boot has packaged the RabbitMQ Client API, which is much simpler to use. The following details how to use rabbitmq_delayed_message_exchangeplugins and Spring Boot to implement delayed messages.


software preparation


erlang

Please refer to installing erlang under Win10

The version used in this article is:

Erlang 20.3


RabbitMQ

Please refer to installing rabbitmq under win10

This article uses the windowversion of RabbitMQ, the version number is:

3.7.4


rabbitmq_delayed_message_exchange插件

Plugin download address:

http://www.rabbitmq.com/community-plugins.html

After opening the URL, ctrl + f, search rabbitmq_delayed_message_exchange.
write picture description here

Remember, you must choose the version number. Since I am using RabbitMQ 3.7.4, the corresponding rabbitmq_delayed_message_exchangeplugin must also choose 3.7.x.

If you don't choose the right version, you will encounter all kinds of weird problems when using delayed messages, and you can't find a solution online. I struggled all night with this problem. Remember to choose the correct plugin version.

After downloading the plugin, place it in a directory under the RabbitMQ installation directory pluginsand start the plugin with the following command:

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

If the startup is successful, the following message will appear:

The following plugins have been enabled:
rabbitmq_delayed_message_exchange

After starting the plugin successfully, remember to restart RabbitMQ for it to take effect.


Integrate RabbitMQ


This is very simple, add it directly to the pom.xml file of the maven project

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

The version of Spring Boot I am using is 2.0.1.RELEASE.

Next application.properties, add the redis configuration to the file:

spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

Define ConnectionFactory and RabbitTemplate


It is also very simple, the code is as follows:

package com.mq.rabbitmq;

import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitMqConfig {
    private String host;
    private int port;
    private String userName;
    private String password;

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(host,port);
        cachingConnectionFactory.setUsername(userName);
        cachingConnectionFactory.setPassword(password);
        cachingConnectionFactory.setVirtualHost("/");
        cachingConnectionFactory.setPublisherConfirms(true);
        return cachingConnectionFactory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
        return rabbitTemplate;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Exchange and Queue configuration


package com.mq.rabbitmq;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class QueueConfig {

    @Bean
    public CustomExchange delayExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange("test_exchange", "x-delayed-message",true, false,args);
    }

    @Bean
    public Queue queue() {
        Queue queue = new Queue("test_queue_1", true);
        return queue;
    }

    @Bean
    public Binding binding() {
        return BindingBuilder.bind(queue()).to(delayExchange()).with("test_queue_1").noargs();
    }
}

It is important to note here that using yes CustomExchange, no DirectExchange, another CustomExchangetype must be x-delayed-message.


implement message sending


package com.mq.rabbitmq;

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;

@Service
public class MessageServiceImpl {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMsg(String queueName,String msg) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("消息发送时间:"+sdf.format(new Date()));
        rabbitTemplate.convertAndSend("test_exchange", queueName, msg, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setHeader("x-delay",3000);
                return message;
            }
        });
    }
}

Note that when sending, you must add a header

x-delay

The delay time I set here is 3 seconds.


message consumer


package com.mq.rabbitmq;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class MessageReceiver {

    @RabbitListener(queues = "test_queue_1")
    public void receive(String msg) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("消息接收时间:"+sdf.format(new Date()));
        System.out.println("接收到的消息:"+msg);
    }
}

Running Spring Boot programs and sending messages


Run the Spring Boot program directly in the main method, and Spring Boot will automatically resolve MessageReceiverthe class.

Next, you only need to use Junit to run the interface for sending messages.

package com.mq.rabbitmq;

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 RabbitmqApplicationTests {

    @Autowired
    private MessageServiceImpl messageService;

    @Test
    public void send() {
        messageService.sendMsg("test_queue_1","hello i am delay msg");
    }

}

After running, you can see the following information:

Message sending time: 2018-05-03 12:44:53

After 3 seconds, the Spring Boot console outputs:

Message received time: 2018-05-03 12:44:56 Message received
: hello i am delay msg

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325345796&siteId=291194637