自定义执行引擎

664 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情

一、任务

public interface ExecuteTask{

    /**
     * 是否应该执行任务
     * @return
     */
    boolean shouldProcess();
}

1.1 延时任务

1.执行时间

延时任务会判断当前执行时间 - 最后一次执行时间是否大于任务时间间隔来判断是否需要执行

2.合并任务

如果任务队列中出现相同任务,需要进行合并

3.多次重试

执行失败,可以重试

public abstract class AbstractDelayTask implements ExecuteTask {
    /**
     * 任务间隔
     */
    private long taskInterval;

    /**
     * 最后一次执行时间
     */
    private long lastProcessTime;

    /**
     * 重试次数
     */
    private AtomicInteger retryTimes = new AtomicInteger(0);

    public AbstractDelayTask(long taskInterval) {
        this.taskInterval = taskInterval;
        this.lastProcessTime = System.currentTimeMillis();
    }

    public AbstractDelayTask(long taskInterval, long lastProcessTime) {
        this.taskInterval = taskInterval;
        this.lastProcessTime = lastProcessTime;
    }

    /**
     * 合并任务
     * @param task
     */
    public abstract void merge(AbstractDelayTask task);

    @Override
    public boolean shouldProcess() {
        return (System.currentTimeMillis() - this.lastProcessTime >= this.taskInterval);
    }

    public long getTaskInterval() {
        return taskInterval;
    }

    public void setTaskInterval(long taskInterval) {
        this.taskInterval = taskInterval;
    }

    public long getLastProcessTime() {
        return lastProcessTime;
    }

    public void setLastProcessTime(long lastProcessTime) {
        this.lastProcessTime = lastProcessTime;
    }

    public int getRetryTimes() {
        return retryTimes.get();
    }

    public void increment() {
        retryTimes.incrementAndGet();
    }
    
}

1.2 执行任务

执行任务实现了Runnable接口

public abstract class AbstractExecuteTask implements ExecuteTask,Runnable {
    
    @Override
    public boolean shouldProcess() {
        return true;
    }
}

二、执行器

对执行任务做处理的,可以按照不同的执行任务实现不同的执行器

public interface TaskProcessor<T extends ExecuteTask> {

    /**
     * 执行任务
     *
     * @param task
     * @return
     */
    boolean process(T task);
}

三、执行引擎

执行任务由执行引擎进行处理,将不同的执行任务分派给不同的执行引擎进行执行

public interface TaskExecuteEngine <T extends ExecuteTask> extends Closeable {


    /**
     * 添加任务
     * @param key
     * @param task
     */
    void addTask(Object key, T task);

    /**
     * 移除任务
     * @param key
     * @return
     */
    T removeTask(Object key);


    /**
     * 获取执行器
     * @param key
     * @return
     */
    TaskProcessor getProcessor(Object key);

    /**
     * 添加执行器
     * @param key
     * @param taskProcessor
     */
    void addProcessor(Object key, TaskProcessor taskProcessor);

    /**
     * 移除执行器
     * @param key
     */
    void removeProcessor(Object key);

    /**
     * 设置默认执行器
     * @param defaultTaskProcessor
     */
    void setDefaultTaskProcessor(TaskProcessor defaultTaskProcessor);


    /**
     * 获取所有任务key
     * @return
     */
    Collection<Object> getAllTaskKeys();
}

执行器引擎维护了对执行器的所有操作

public abstract class AbstractTaskExecuteEngine<T extends ExecuteTask> implements TaskExecuteEngine<T> {

    private final ConcurrentHashMap<Object, TaskProcessor> taskProcessors = new ConcurrentHashMap();

    private TaskProcessor defaultTaskProcessor;

    protected final Logger log;

    public AbstractTaskExecuteEngine(Logger logger) {
        this.log = null != logger ? logger : LogProvider.getLogger(AbstractTaskExecuteEngine.class);
    }

    @Override
    public TaskProcessor getProcessor(Object key) {
        return taskProcessors.containsKey(key) ? taskProcessors.get(key) : defaultTaskProcessor;
    }

    @Override
    public void addProcessor(Object key, TaskProcessor taskProcessor) {
        //如果不存在则放入执行器
        taskProcessors.putIfAbsent(key, taskProcessor);
    }

    @Override
    public void removeProcessor(Object key) {
        taskProcessors.remove(key);
    }

    @Override
    public void setDefaultTaskProcessor(TaskProcessor defaultTaskProcessor) {
        this.defaultTaskProcessor = defaultTaskProcessor;
    }

}

3.1延时任务执行引擎

3.1.1待执行任务列表

延时任务执行引擎中会将任务添加至延时任务列表中

3.1.2定时任务处理器

定时任务处理器按一定时间从延时任务列表中获取任务,然后匹配指定的处理器进行处理

3.1.3任务重试

如果定时任务处理失败,会按任务的重试次数重新放回任务队列重新执行

public class DelayTaskExecuteEngine extends AbstractTaskExecuteEngine<AbstractDelayTask> {

    protected final ConcurrentHashMap<Object, AbstractDelayTask> tasks;

    protected final ScheduledExecutorService processingExecutor;

    protected final ReentrantLock lock = new ReentrantLock();

    private int maxRetryTimes = 0;

    public DelayTaskExecuteEngine(String name) {
        this(name, 32, LogProvider.getLogger(DelayTaskExecuteEngine.class), 100L, 3);
    }

    public DelayTaskExecuteEngine(String name, Logger logger) {
        this(name, 32, logger, 100L, 3);
    }

    public DelayTaskExecuteEngine(String name, int initCapacity, Logger logger) {
        this(name, initCapacity, logger, 100L, 3);
    }

    public DelayTaskExecuteEngine(String name, int initCapacity, Logger logger, long processInterval) {
        this(name, initCapacity, logger, processInterval, 3);
    }

    public DelayTaskExecuteEngine(String name, int initCapacity, Logger logger, long processInterval, int maxRetryTimes) {
        super(logger);
        this.maxRetryTimes = maxRetryTimes;
        tasks = new ConcurrentHashMap<Object, AbstractDelayTask>(initCapacity);
        this.processingExecutor = Executors.newScheduledThreadPool(1, new NameThreadFactory(name));
        //延时定时任务
        processingExecutor
                .scheduleWithFixedDelay(new DelayProcessRunnable(), processInterval, processInterval, TimeUnit.MILLISECONDS);
    }

    @Override
    public void shutdown() throws CommonException {
        processingExecutor.shutdown();
    }

    @Override
    public void addTask(Object key, AbstractDelayTask newTask) {
        AbstractDelayTask existTask = tasks.get(key);
        lock.lock();
        try {

            if (null != existTask) {
                newTask.merge(existTask);
            }
            tasks.put(key, newTask);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public AbstractDelayTask removeTask(Object key) {
        AbstractDelayTask existTask = tasks.get(key);
        lock.lock();
        try {

            if (null != existTask) {
                return tasks.remove(key);
            }
            return null;
        } finally {
            lock.unlock();
        }
    }


    @Override
    public Collection<Object> getAllTaskKeys() {
        Collection<Object> keys = new HashSet<Object>();
        lock.lock();
        try {
            keys.addAll(tasks.keySet());
        } finally {
            lock.unlock();
        }
        return keys;
    }

    protected void processTasks() {
        Collection<Object> keys = getAllTaskKeys();
        for (Object taskKey : keys) {

            AbstractDelayTask task = removeTask(taskKey);
            if (null == task || !task.shouldProcess()) {
                continue;
            }
            TaskProcessor processor = getProcessor(taskKey);
            if (processor == null) {
                continue;
            }
            try {
                // 执行当前任务
                if (!processor.process(task)) {
                    //执行失败再次执行
                    retryFailedTask(taskKey, task);
                }
            } catch (Throwable e) {
                log.error("task execute error : " + e.toString(), e);
                //执行失败再次执行
                retryFailedTask(taskKey, task);
            }
        }
    }


    private void retryFailedTask(Object key, AbstractDelayTask task) {
        if (task.getRetryTimes() >= this.maxRetryTimes) {
            return;
        }
        task.increment();
        task.setLastProcessTime(System.currentTimeMillis());
        addTask(key, task);
    }

    private class DelayProcessRunnable extends ExecuteRunnable {
        @Override
        protected void executeBody() {
            processTasks();
        }
    }
}

3.2任务执行引擎

3.2.1 工作执行器

任务执行引擎中会会维护工作执行器,当任务没有匹配到执行器的时候由工作执行器执行任务

public class ExecuteTaskExecuteEngine extends AbstractTaskExecuteEngine<AbstractExecuteTask> {

    private final TaskExecuteWorker[] executeWorkers;

    public ExecuteTaskExecuteEngine(String name) {
        this(name, LogProvider.getLogger(ExecuteTaskExecuteEngine.class), ThreadUtil.getSuitableThreadCount(1));
    }

    public ExecuteTaskExecuteEngine(String name, Logger logger) {
        this(name, logger, ThreadUtil.getSuitableThreadCount(1));
    }

    public ExecuteTaskExecuteEngine(String name, Logger logger, int dispatchWorkerCount) {
        super(logger);
        executeWorkers = new TaskExecuteWorker[dispatchWorkerCount];
        for (int mod = 0; mod < dispatchWorkerCount; ++mod) {
            executeWorkers[mod] = new TaskExecuteWorker(name, mod, dispatchWorkerCount, this.log);
        }
    }

    @Override
    public void addTask(Object key, AbstractExecuteTask task) {
        TaskProcessor processor = getProcessor(key);
        if (null != processor) {
            processor.process(task);
            return;
        }
        TaskExecuteWorker worker = getWorker(key);
        worker.process(task);
    }

    @Override
    public AbstractExecuteTask removeTask(Object key) {
        log.warn("ExecuteTaskEngine do not support remove task");
        return null;
    }

    @Override
    public Collection<Object> getAllTaskKeys() {
        log.warn("ExecuteTaskEngine do not support get all task keys");
        return null;
    }

    @Override
    public void shutdown() throws CommonException {
        for (TaskExecuteWorker each : executeWorkers) {
            each.shutdown();
        }
    }

    /**
     * 获取工作线程的状态
     *
     * @return
     */
    public String workersStatus() {
        StringBuilder sb = new StringBuilder();
        for (TaskExecuteWorker worker : executeWorkers) {
            sb.append(worker.status()).append("\n");
        }
        return sb.toString();
    }

    private TaskExecuteWorker getWorker(Object tag) {
        int idx = (tag.hashCode() & Integer.MAX_VALUE) % workersCount();
        return executeWorkers[idx];
    }

    private int workersCount() {
        return executeWorkers.length;
    }


}

工作执行器

public final class TaskExecuteWorker implements TaskProcessor<ExecuteTask>, Closeable {

    /**
     * Max task queue size 32768.
     */
    private static final int QUEUE_CAPACITY = 1 << 15;

    private final Logger log;

    private final String name;

    private final BlockingQueue<Runnable> queue;

    private final AtomicBoolean closed;

    public TaskExecuteWorker(final String name, final int mod, final int total) {
        this(name, mod, total, null);
    }

    public TaskExecuteWorker(final String name, final int mod, final int total, final Logger logger) {
        this.name = name + "_" + mod + "%" + total;
        this.queue = new ArrayBlockingQueue<Runnable>(QUEUE_CAPACITY);
        this.closed = new AtomicBoolean(false);
        this.log = null == logger ? LogProvider.getLogger(TaskExecuteWorker.class) : logger;
        new InnerWorker(name).start();
    }

    public String getName() {
        return name;
    }

    @Override
    public boolean process(ExecuteTask task) {
        try {
            queue.put((Runnable) task);
        } catch (InterruptedException ire) {
            log.error(ire.toString(), ire);
            return false;
        }
        return true;
    }

    public int pendingTaskCount() {
        return queue.size();
    }

    /**
     * Worker status.
     */
    public String status() {
        return name + ", pending tasks: " + pendingTaskCount();
    }

    @Override
    public void shutdown() throws CommonException {
        queue.clear();
        closed.compareAndSet(false, true);
    }

    /**
     * Inner execute worker.
     */
    private class InnerWorker extends Thread {

        InnerWorker(String name) {
            setDaemon(false);
            setName(name);
        }

        @Override
        public void run() {
            while (!closed.get()) {
                try {
                    Runnable task = queue.take();
                    long begin = System.currentTimeMillis();
                    task.run();
                    long duration = System.currentTimeMillis() - begin;
                    if (duration > 1000L) {
                        log.warn("distro task {} takes {}ms", task, duration);
                    }
                } catch (Throwable e) {
                    log.error("[DISTRO-FAILED] " + e.toString(), e);
                }
            }
        }
    }
}

四、测试

4.1 延时工作引擎测试

public class DelayTaskExecuteEngineTest {

    private DelayTaskExecuteEngine executeEngine;

    @Before
    public void build() {
        executeEngine = new DelayTaskExecuteEngine("TEST");
        executeEngine.setDefaultTaskProcessor(new TestDelayProcessor());
    }


    @Test
    public void testAddTask() throws InterruptedException {
        String taskKey = "5000";
        executeEngine.addTask(taskKey, new TestDelayTask(1000, System.currentTimeMillis()));
        TimeUnit.SECONDS.sleep(200);

    }

    class TestDelayProcessor implements TaskProcessor {

        @Override
        public boolean process(ExecuteTask task) {
            Calendar now = Calendar.getInstance();
            System.out.println(now.getTime() + "== 进入TestDelay");
            return false;
        }
    }


    class TestDelayTask extends AbstractDelayTask {

        public TestDelayTask(long taskInterval, long lastProcessTime) {
            super(taskInterval, lastProcessTime);
        }

        @Override
        public void merge(AbstractDelayTask task) {
            Calendar now = Calendar.getInstance();
            System.out.println(now.getTime() + "== 进入merge");
        }
    }
}

4.1 工作引擎测试

ublic class ExecuteTaskExecuteEngineTest {

    private ExecuteTaskExecuteEngine executeEngine;

    @Before
    public void build() {
        executeEngine = new ExecuteTaskExecuteEngine("Execute");


    }

    @Test
    public void testAddTask() {
        for (int i = 0; i < 20; i++) {
            executeEngine.addTask(i, new TestDelayTask());
        }

    }

    class TestDelayTask extends AbstractExecuteTask {

        @Override
        public void run() {
            Calendar now = Calendar.getInstance();
            System.out.println(now.getTime() + "== 进入TestDelayTask");
        }
    }
}