Usage scenarios
1, checkout success ,30 Minutes unpaid . Payment overtime , Automatic order cancellation
2, Order signing , After signing 7 There was no evaluation in the past three days . Order timeout not evaluated , System default praise
3, checkout success , business 5 No orders received in 15 minutes , Order cancellation
4, Delivery timeout , Push SMS reminder
......
For the scene with long delay , Scenes with low real-time performance , We can use the way of task scheduling to polling regularly . as :xxl-job
Today, we adopt a relatively simple method , Lightweight way , use Redis
Delay queue for processing . Of course, there is a better solution , According to the company's technology selection and business system to choose the best solution . as : Using message oriented middleware Kafka,RabbitMQ Delay queue for
The implementation principle is not discussed , Direct combat on the code first based on Redis Delay queue for
1, introduce Redisson rely on
<dependency> <groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId> <version>3.10.5</version>
</dependency>
2,Nacos to configure Redis connect
spring: redis: host: 127.0.0.1 port: 6379 password: 123456 database: 12
timeout: 3000
3, establish RedissonConfig to configure
/** * Created by LPB on 2020/04/20. */ @Configuration public class
RedissonConfig { @Value("${spring.redis.host}") private String host;
@Value("${spring.redis.port}") private int port;
@Value("${spring.redis.database}") private int database;
@Value("${spring.redis.password}") private String password; @Bean public
RedissonClient redissonClient() { Config config = new Config();
config.useSingleServer() .setAddress("redis://" + host + ":" + port)
.setDatabase(database) .setPassword(password); return Redisson.create(config);
} }
4, encapsulation Redis Delay queue tool class
/** * redis Delay queue tool * Created by LPB on 2021/04/20. */ @Slf4j @Component public
class RedisDelayQueueUtil { @Autowired private RedissonClient redissonClient;
/** * Add delay queue * @param value Queue value * @param delay delay time * @param timeUnit Time unit *
@param queueCode Queue key * @param <T> */ public <T> void addDelayQueue(T value,
long delay, TimeUnit timeUnit, String queueCode){ try { RBlockingDeque<Object>
blockingDeque = redissonClient.getBlockingDeque(queueCode);
RDelayedQueue<Object> delayedQueue =
redissonClient.getDelayedQueue(blockingDeque); delayedQueue.offer(value, delay,
timeUnit); log.info("( Add delay queue successfully ) Queue key :{}, Queue value :{}, delay time :{}", queueCode, value,
timeUnit.toSeconds(delay) + " second "); } catch (Exception e) { log.error("( Failed to add delay queue )
{}", e.getMessage()); throw new RuntimeException("( Failed to add delay queue )"); } } /** *
Get delay queue * @param queueCode * @param <T> * @return * @throws InterruptedException
*/ public <T> T getDelayQueue(String queueCode) throws InterruptedException {
RBlockingDeque<Map> blockingDeque = redissonClient.getBlockingDeque(queueCode);
T value = (T) blockingDeque.take(); return value; } }
5, Create delay queue service enumeration
/** * Delay queue service enumeration * Created by LPB on 2021/04/20. */ @Getter @NoArgsConstructor
@AllArgsConstructor public enum RedisDelayQueueEnum {
ORDER_PAYMENT_TIMEOUT("ORDER_PAYMENT_TIMEOUT"," Order payment timeout , Automatic order cancellation ",
"orderPaymentTimeout"),
ORDER_TIMEOUT_NOT_EVALUATED("ORDER_TIMEOUT_NOT_EVALUATED", " Order timeout not evaluated , System default praise ",
"orderTimeoutNotEvaluated"); /** * Delay queue Redis Key */ private String code; /** *
Chinese description */ private String name; /** * Implementation of delay queue service Bean * Available through Spring Context acquisition for */
private String beanId; }
6, Define delay queue executors
/** * Delay queue actuator * Created by LPB on 2021/04/20. */ public interface
RedisDelayQueueHandle<T> { void execute(T t); }
7, Create the Bean, And implement delay queue executor
* OrderPaymentTimeout: Order payment timeout delay queue processing class /** * Order payment overtime processing class * Created by LPB on
2021/04/20. */ @Component @Slf4j public class OrderPaymentTimeout implements
RedisDelayQueueHandle<Map> { @Override public void execute(Map map) {
log.info("( Receive order payment timeout delay message ) {}", map); // TODO Order payment timeout , Automatic order cancellation ... } }
* OrderTimeoutNotEvaluated: Order timeout not evaluated delay queue processing class /** * Order timeout not evaluated processing class * Created by LPB
on 2021/04/20. */ @Component @Slf4j public class OrderTimeoutNotEvaluated
implements RedisDelayQueueHandle<Map> { @Override public void execute(Map map)
{ log.info("( Received order timeout not evaluated delay message ) {}", map); // TODO Order timeout not evaluated , The system processes business by default ... } }
8, Create delay queue consumption thread , Start after the project is started
/** * Start delay queue * Created by LPB on 2021/04/20. */ @Slf4j @Component public class
RedisDelayQueueRunner implements CommandLineRunner { @Autowired private
RedisDelayQueueUtil redisDelayQueueUtil; @Override public void run(String...
args) { new Thread(() -> { while (true){ try { RedisDelayQueueEnum[] queueEnums
= RedisDelayQueueEnum.values(); for (RedisDelayQueueEnum queueEnum :
queueEnums) { Object value =
redisDelayQueueUtil.getDelayQueue(queueEnum.getCode()); if (value != null) {
RedisDelayQueueHandle redisDelayQueueHandle =
SpringUtil.getBean(queueEnum.getBeanId());
redisDelayQueueHandle.execute(value); } } } catch (InterruptedException e) {
log.error("(Redis Delay queue interrupt ) {}", e.getMessage()); } } }).start();
log.info("(Redis Delay queue started successfully )"); } }
The above steps ,Redis Delay queue core code is complete , Let's write a test interface , use PostMan Do a simulation test
9, Create a test interface , Simulate adding delay queue
/** * Delay queue test * Created by LPB on 2020/04/20. */ @RestController public class
RedisDelayQueueController { @Autowired private RedisDelayQueueUtil
redisDelayQueueUtil; @PostMapping("/addQueue") public void addQueue() {
Map<String, String> map1 = new HashMap<>(); map1.put("orderId", "100");
map1.put("remark", " Order payment timeout , Automatic order cancellation "); Map<String, String> map2 = new
HashMap<>(); map2.put("orderId", "200"); map2.put("remark", " Order timeout not evaluated , System default praise ");
// Add order payment timeout , Automatic order cancellation delay queue . In order to test the effect , delay 10 second redisDelayQueueUtil.addDelayQueue(map1,
10, TimeUnit.SECONDS, RedisDelayQueueEnum.ORDER_PAYMENT_TIMEOUT.getCode()); //
Order timeout not evaluated , System default praise . In order to test the effect , delay 20 second redisDelayQueueUtil.addDelayQueue(map2, 20,
TimeUnit.SECONDS, RedisDelayQueueEnum.ORDER_TIMEOUT_NOT_EVALUATED.getCode()); }
}
10, start-up SpringBoot project , use PostMan Call interface to add delay queue
* adopt Redis The client can see that two delay queues have been added successfully
* see IDEA The console log shows that the delay queue has been consumed successfully
Technology
Daily Recommendation