27 interruption of background tasks

preface

In daily development, we may have some time-consuming tasks that are processed asynchronously in the background. After completion, the front end polls or the back end actively pushes notifications to the front end

Usually, in such tasks, sometimes we may need to interrupt some time-consuming tasks. For example, if we suddenly don't want to do it halfway, we can interrupt it

Generally speaking, such asynchronous tasks should have a thread pool. There are threads in the thread pool to process these asynchronous tasks [for example, @ Async of spring can specify the ExecutorService to execute]

Hehe, what I'm talking about here is probably such a thing. My handling here is also a relatively simple handling

Sort out this process. The processes that users can perceive are as follows

1. Operation 1 triggers the creation of a background asynchronous task 1

2. Asynchronous task 1 starts execution

3. Operation 2 triggers and cancels asynchronous task 1

InterruptUtils

So how do we design the program? What is the process in the program?  

Let's sort out the general procedure first

1. Operation 1 triggers the creation of a background asynchronous task 1. Generally speaking, this asynchronous task 1 will be associated with a tag. Either this task will correspond to a record, or an id will be generated for this record and returned

2. Asynchronous task 1 starts to execute, which is handled by the specific asynchronous scheduling framework, such as thread pool, scheduling thread pool, timed task, etc

3. Operation 2 triggers the cancellation of asynchronous task 1. From the procedural point of view, operation 2 can only mark the id of the current asynchronous task 1

4. Asynchronous task 1 embeds detection marks at some points. If the current asynchronous task is interrupted, an exception will be thrown, the interrupt process will continue to go down, and finally do the relevant cleaning work

The above process involves several operations, such as marking that asynchronous task 1 is interrupted, such as detecting whether the asynchronous task is interrupted, and throwing an exception if interrupted

I designed an interrupt utils to handle this problem, and provided several APIs for reuse. Here, I use redis as a medium to store and read interrupt related information

markInterrupt: mark that the task corresponding to the record under the given record type is interrupted

isInterrupted: query whether the task corresponding to the record under the given record type is interrupted

doInterrupt: query whether the task corresponding to the record under a given record type is interrupted. If interrupted, throw an exception and record the log information

public final class InterruptUtils {

    /**
     * Interrupt a given record
     *
     * @param recordClazz recordClazz
     * @param recordId    recordId
     * @return void
     * @author Jerry.X.He
     * @date 2021-02-03 15:03
     */
    public static Long markInterrupt(Class recordClazz, String recordId) {
        String recordType = recordClazz.getName();
        String cacheKey = BizCacheKeyUtil.getInterruptKey(recordType, recordId);
        StringRedisTemplate redisTemplate = SpringContext.getBean(StringRedisTemplate.class);
        // Add a default expiration time
        redisTemplate.expire(cacheKey, 1, TimeUnit.HOURS);
        return redisTemplate.boundValueOps(cacheKey).increment();
    }

    /**
     * Judge whether the given record is interrupted
     *
     * @param recordClazz recordClazz
     * @param recordId    recordId
     * @return boolean
     * @author Jerry.X.He
     * @date 2021-02-03 15:03
     */
    public static boolean isInterrupted(Class recordClazz, String recordId) {
        String recordType = recordClazz.getName();
        String cacheKey = BizCacheKeyUtil.getInterruptKey(recordType, recordId);
        StringRedisTemplate redisTemplate = SpringContext.getBean(StringRedisTemplate.class);
        String value = redisTemplate.boundValueOps(cacheKey).get();
        if (!StringUtils.isNumeric(value)) {
            return false;
        }

        return Long.valueOf(value) > 0;
    }

    /**
     * Interrupt the given thread and throw an exception
     *
     * @param recordClazz recordClazz
     * @param recordId    recordId
     * @return void
     * @author Jerry.X.He
     * @date 2021-02-03 15:24
     */
    public static void doInterrupt(Class recordClazz, String recordId) {
        if (isInterrupted(recordClazz, recordId)) {
            String threadName = Thread.currentThread().getName();
            String msg = String.format("thread  %s Interrupted, recordType : %s, recordId : %s ",
                                       threadName, recordClazz.getName(), recordId);
            throw new RuntimeException(msg);
        }
    }

}

Mark interruption and detect interruption buried points

This can generally be handled using aop, with an annotation to mark the interrupt or detect the embedded point of the interrupt

Some special places need to add embedded points in the code, such as those that do not belong to calling their own operations that are time-consuming / expensive, and those that call tool classes that are time-consuming / expensive

Well, the way to invoke your own tools in the method is not very well handled, and it is very common to bridge another layer of service.  

Often, the more you understand the encapsulation, the better to deal with this situation

Using aop to process is a relatively less intrusive way

Finish

Keywords: Java async Task

Added by Okami on Sun, 02 Jan 2022 20:14:00 +0200