Asynchronous task scheduling

problem

When the client submits multiple tasks to the service layer at the same time, in order to give full play to the processing and computing power of the server, ordinary single thread can no longer meet our needs. It is necessary to use multi-thread or multi-process technology to process tasks concurrently. The server needs to meet the following requirements when processing concurrent tasks:

  • 1. After the task is submitted, I hope the server will process and return the result as quickly as possible
  • 2. It does not affect performance when multiple tasks are processed at the same time
  • 3. When an error occurs during the execution of a task, retry is allowed without hindering the processing of other tasks

analysis

  • 1. Submit multiple tasks at the same time, and hope that the server will process and return the results as quickly as possible, using multi-threading technology to achieve, for each task, if a thread is assigned to process, then when thousands of submissions are submitted at the same time Tasks can easily lead to exhaustion of server system resources and server downtime. When assigning tasks, creating and destroying threads is a waste of resources. Thread pool technology is used to improve thread execution efficiency, minus the time for thread creation and destruction during task execution, which greatly improves task execution efficiency. Through the thread pool technology, a certain amount of threads are stored when the system is initialized. After the client submits tasks to the server, each task is allocated to existing idle threads for processing. However, when many tasks are submitted, it is obvious that the threads in the thread pool The number of threads will be insufficient, so how to solve it? After the client submits the task to the server, the task is now submitted to the task queue (TaskQueue). When the system executes the task, the task is extracted from the task queue and then allocated to the idle thread in the thread pool to execute the task.
  • 2. After we release the task, the task is handed over to the service layer for processing. In the process of processing, it is inevitable that we will encounter some special problems, which will cause the task to fail. In order to ensure that the task can be effectively executed and achieve the tasks we need, It is also necessary to provide a retry mechanism for task processing, so that when the system fails to execute a task, it can start the retry mechanism to process the submitted task again, increasing the success rate of task execution.

 

design

 

  • Client
    Client simulates multiple clients. The submit() method is used to submit tasks. The submitted task type is AsyncTask, which is a dedicated task class, which will be described below.
  • AsyncExecutorService
    First of all, the purpose of this class is to create a thread pool executor for resource allocation. It needs to initialize some parameters, such as the number of threads, the maximum number of threads, thread survival time, the number of retries, and the waiting time for retry, etc. There is a hidden parameter --> task queue. When allocating resources, tasks are randomly selected from the task queue and allocated. When tasks are executed and there are idle threads, the executor will redistribute resources again, from the task queue Select the corresponding task to execute.
    This class also provides an interface for executing tasks. The tasks passed from the client are received in this class and internal logic is executed.
  • AsyncTask
    AsyncTask is a Runnable class that rectifies the place where the task is executed. The executed task is provided to the outside through the abstract method doWork(), so that different task operations can be realized. In this class, the logic of the retry mechanism is mainly implemented. When the task execution fails, the corresponding number of retry execution is performed to ensure the success of the task execution.

Client code

package com.ngsky.async;
 
/**
 * @Description TODO
 * @Author daxiong
 * @Date 7/7/2018 10:48 AM
 **/
public class AsyncTaskTest {
    public static void main(String[] args){
        AsyncExecutorService executorService = new AsyncExecutorService(5, 10, 30, TimeUnit.SECONDS, 2, 5);
        long beginTime = System.currentTimeMillis();
        for(int i = 0; i < 1000;i++){
            MockTask mockTask = new MockTask();
            mockTask.setName("task" + i);
            mockTask.setRetryTimes(3);
            mockTask.setRetryWaitTime(2000);
            executorService.execute(mockTask);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("The task spend on " + ((endTime - beginTime) / 60L) + " minutes");
    }
 
}
// every task spend 20s, (1000 * 2) / 60 = 33 minutes
class MockTask extends AsyncTask{
    public void doWork() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 AsyncTask.java class

package com.ngsky.async;
 
/**
 * @Description TODO
 * @Author daxiong
 * @Date 7/7/2018 10:14 AM
 **/
public abstract class AsyncTask implements Runnable {
 
    private String name;  // task name
    private boolean successDone; // whether the task completed successful
    private int retryTimes;  // if the task was't finished successful, system allow retry to do the task for retrytimes
    private int retryWaitTime;  // system cant't retry to do work immediately, but execute after retryWaitTime
 
    public void run() {
        System.out.println("(task) " + getName() + " beginning execute....");
        long beginTime = System.currentTimeMillis();
        int currentRetryTimes = 1;
        try {
            doWork();
            System.out.println("(task) " + getName() + " completed successful!");
            this.setSuccessDone(true);
        } catch (Exception e) {
            System.out.println("(task) " + " execute filed..., message " + e);
            this.setSuccessDone(false);
        }
        if (getRetryTimes() <= 0) return;
        while (!isSuccessDone() && currentRetryTimes <= getRetryTimes()) {
            System.out.println("(task) " + "Executing retry " + currentRetryTimes + "th!" );
            if (getRetryWaitTime() > 0) {
                try {
                    Thread.sleep(getRetryWaitTime());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                doWork();
                System.out.println("(task) " + getName() + " completed successful!");
                this.setSuccessDone(true);
            } catch (Exception e) {
                System.out.println("(task) " + getName() + " was failed, unknown reason! Please try again!");
                this.setSuccessDone(false);
                currentRetryTimes++;
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("(task) " + " spend on " + (endTime - beginTime) + "ms and result is " + (this.isSuccessDone() ? "successful!" : "failed,Please check your task!"));
    }
 
    public abstract void doWork() throws Exception;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public boolean isSuccessDone() {
        return successDone;
    }
 
    public void setSuccessDone(boolean successDone) {
        this.successDone = successDone;
    }
 
    public int getRetryTimes() {
        return retryTimes;
    }
 
    public void setRetryTimes(int retryTimes) {
        this.retryTimes = retryTimes;
    }
 
    public int getRetryWaitTime() {
        return retryWaitTime;
    }
 
    public void setRetryWaitTime(int retryWaitTime) {
        this.retryWaitTime = retryWaitTime;
    }
}
 

AsyncExecutorService.java

package com.ngsky.async;
 
import java.util.concurrent.*;
 
/**
 * @Description async service interface
 * @Author daxiong
 * @Date 7/7/2018 9:16 AM
 **/
public class AsyncExecutorService {
 
    private int corePoolSize;
    private int maximumPoolSize;
    private long keepLiveTime;
    private TimeUnit timeUnit;
    private int retryTimes;
    private int retryWaitTime;
    private LinkedBlockingQueue<Runnable> blockingQueue;
    private ThreadPoolExecutor executor;
 
    public AsyncExecutorService(int corePoolSize, int maximumPoolSize, long keepLiveTimes,
                                TimeUnit timeUnit, int retryTimes, int retryWaitTimes){
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.keepLiveTime = keepLiveTimes;
        this.timeUnit = timeUnit;
        this.retryTimes = retryTimes;
        this.retryWaitTime = retryWaitTimes;
        init();
    }
 
    private void init(){
        System.out.println("Async executor initializing...");
        blockingQueue = new LinkedBlockingQueue<Runnable>();
        executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepLiveTime, timeUnit, blockingQueue);
    }
 
    // init task information
    private AsyncTask initTask(AsyncTask task){
        System.out.println("(task) " + task.getName() + " initializing...");
        if(retryTimes > 0) task.setRetryTimes(retryTimes);
        if(retryWaitTime > 0) task.setRetryWaitTime(retryWaitTime);
        return task;
    }
 
    public void execute(AsyncTask task){
        task = initTask(task);
        executor.execute(task);
    }
 
    public <T> Future<T> submit(Callable<T> job){
        return executor.submit(job);
    }
 
    public void shutdown(){
        if(executor != null)
            executor.shutdown();
    }
 
    public int getCorePoolSize() {
        return corePoolSize;
    }
 
    public void setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
    }
 
    public int getMaximumPoolSize() {
        return maximumPoolSize;
    }
 
    public void setMaximumPoolSize(int maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
    }
 
    public long getKeepLiveTime() {
        return keepLiveTime;
    }
 
    public void setKeepLiveTime(long keepLiveTime) {
        this.keepLiveTime = keepLiveTime;
    }
 
    public TimeUnit getTimeUnit() {
        return timeUnit;
    }
 
    public void setTimeUnit(TimeUnit timeUnit) {
        this.timeUnit = timeUnit;
    }
 
    public int getRetryTimes() {
        return retryTimes;
    }
 
    public void setRetryTimes(int retryTimes) {
        this.retryTimes = retryTimes;
    }
 
    public int getRetryWaitTime() {
        return retryWaitTime;
    }
 
    public void setRetryWaitTime(int retryWaitTime) {
        this.retryWaitTime = retryWaitTime;
    }
 
    public LinkedBlockingQueue<Runnable> getBlockingQueue() {
        return blockingQueue;
    }
 
    public void setBlockingQueue(LinkedBlockingQueue<Runnable> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }
 
    public ThreadPoolExecutor getExecutor() {
        return executor;
    }
 
    public void setExecutor(ThreadPoolExecutor executor) {
        this.executor = executor;
    }
}
 

to sum up

There are many places that are not involved in theories and implementations related to asynchronous task scheduling. Here is an introduction, such as the interception operation during task execution, how to choose the size of the thread pool during initialization, and so on.

Task scheduling is aimed at high concurrent task requests, requiring system performance, processing tasks at the fastest speed, and improving processing efficiency and success rate.

Selected technology: multithreading, thread pool, concurrent processing, task scheduling, resource allocation, retry mechanism.

 

 

 

 

 

Guess you like

Origin blog.csdn.net/suixinsuoyu12519/article/details/108624028
Recommended