Remember the scenario of using delay queue in one work

Posted by Hikari on Thu, 07 May 2020 10:23:25 +0200

1 scenario

The company is engaged in the traffic management of the Internet of things related businesses recently connected with the mobile remote traffic card. The specific business involves card downtime and card recovery. After the user recharge, the background calls the remote card recovery interface for recovery, but the actual test is done during the docking. If the card is found to be shut down, it will immediately recover, and the operation failure will be prompted. After communication, it is learned that this is returned by the operator's interface, and the card cannot be recovered immediately after the shutdown. It is necessary to search the Internet for more than 10 minutes, think about it a little, and decide to use the way of delay queue to realize the recovery operation after shutdown. When the user fails to return to the recovery operation for the first time, put it in the delay queue, wait for 10 minutes, and then execute the following code

First, create a delay queue management class, which is managed by spring

package com.juyi.camera.config;

import com.alibaba.fastjson.JSONObject;
import com.juyi.camera.utils.task.DelayTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;

/**
 * Created by IntelliJ IDEA.
 * User: xuzhou
 * Date: 2020/3/31
 * Time: 9:18
 */
@Component
public class DelayQueueManager implements CommandLineRunner {
    private final Logger logger = LoggerFactory.getLogger(DelayQueueManager.class);
    private DelayQueue<DelayTask> delayQueue = new DelayQueue<>();
    @Resource
    private ExecutorService consumerExecutor;

    /**
     * Add to delay queue
     *
     * @param task
     */
    public void put(DelayTask task) {
        delayQueue.put(task);
    }

    @Override
    public void run(String... args) {
        consumerExecutor.execute(new Thread(this::excuteThread));
    }

    /**
     * Delay task execution thread
     */
    private void excuteThread() {
        while (true) {
            try {
                DelayTask task = delayQueue.take();
                processTask(task);
            } catch (InterruptedException e) {
                break;
            }
        }
    }

    /**
     * Internal execution delay task
     *
     * @param task
     */
    private void processTask(DelayTask task) {
        JSONObject cardInfo = task.getData().getPayload();
        String msisdn = cardInfo.getString("msisdn");
        String iccid = cardInfo.getString("iccid");
        logger.info("msisdn:{},iccid:{}", msisdn, iccid);
    }
}












The delayQueue in the executethread method blocks execution until there is data

package com.juyi.camera.utils.task;

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * Created by IntelliJ IDEA.
 * User: xuzhou
 * Date: 2020/3/31
 * Time: 9:17
 * Delay task
 */
public class DelayTask implements Delayed {
    final private TaskBase data;
    final private long expire;

    /**
     * Construct delay task
     *
     * @param data   Business data
     * @param expire Task delay time (ms)
     */
    public DelayTask(TaskBase data, long expire) {
        super();
        this.data = data;
        this.expire = expire + System.currentTimeMillis();
    }

    public TaskBase getData() {
        return data;
    }

    public long getExpire() {
        return expire;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof DelayTask) {
            String msgId = ((DelayTask) obj).getData().getMsgId();
            return this.data.getMsgId().equals(msgId);
        }
        return false;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(), unit);
    }

    @Override
    public int compareTo(Delayed o) {
        long delta = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        return (int) delta;
    }
}










DelayTask is a custom queue element that must implement Delayed

package com.juyi.camera.utils.task;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * Created by IntelliJ IDEA.
 * User: xuzhou
 * Date: 2020/3/31
 * Time: 9:17
 */
public class TaskBase {
    private String msgId;
    private JSONObject payload;


    public TaskBase(String msgId) {
        this.msgId = msgId;
    }

    public TaskBase(String msgId, JSONObject payload) {
        this.msgId = msgId;
        this.payload = payload;
    }

    public String getMsgId() {
        return msgId;
    }

    public void setMsgId(String msgId) {
        this.msgId = msgId;
    }

    public JSONObject getPayload() {
        return payload;
    }

    public void setPayload(JSONObject payload) {
        this.payload = payload;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}









TaskBase is the data base class of the delayed task to be processed by the user. It stores some customized data, such as device number, card number, ID, operation user, etc

package com.juyi.camera.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created by IntelliJ IDEA.
 * User: xuzhou
 * Date: 2020/4/29
 * Time: 18:54
 */
@Configuration
@Slf4j
public class ThreadPoolUtil implements DisposableBean {
    /**
     * Use bounded queues to avoid OOM
     */
    private static ExecutorService consumerExecutor;

    /**
     * Get thread pool instance
     *
     * @return Thread pool object
     */
    @Bean
    public ExecutorService getThreadPool() {
        consumerExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(512), new ThreadPoolExecutor.DiscardPolicy());
        log.info("ThreadPoolUtil Create a single instance:{}", ((ThreadPoolExecutor) consumerExecutor).getActiveCount());
        return consumerExecutor;
    }

    @Override
    public void destroy() {
        log.info("Destruction consumerExecutor");
        consumerExecutor.shutdown();
    }
}






ThreadPoolUtil is a spring managed thread pool, which is used to initialize the delay queue

Unit 2 test results are as follows, which can meet the requirements well

3 postscript

This part of the code only satisfies the requirement that if the traffic card is recovered, if it is less than 10 minutes, the execution fails, and then it will be placed in the delay queue, and the logic will be executed 10 minutes later. After careful consideration, if the traffic card in the delay queue fails to recover, do you want to add the mechanism of N retries? Send email alarm after n times, etc...

Topics: Programming Java IntelliJ IDEA Spring JSON