这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战
前言
本节介绍RuoYi-Vue的模块中是如何进行异步任务管理的,我们的系统中经常会遇到需要异步处理的情况,比如我们之前讲到的记录日志切面中最后向数据库中存入日志记录就需要用到这个异步任务管理。
// 保存数据库
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
异步任务管理器
public class AsyncManager {
/**
* 操作延迟10毫秒
*/
private final int OPERATE_DELAY_TIME = 10;
/**
* 异步操作任务调度线程池
*/
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/**
* 单例模式
*/
private AsyncManager() {
}
private static AsyncManager me = new AsyncManager();
public static AsyncManager me() {
return me;
}
/**
* 执行任务
*
* @param task 任务
*/
public void execute(TimerTask task) {
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* 停止任务线程池
*/
public void shutdown() {
Threads.shutdownAndAwaitTermination(executor);
}
}
单例
使用单例可以保证在一个进程中,某个类有且仅有一个实例,这个 具体的可以看我这篇单例的文章 上面的代码中是一个比较经典的静态工厂实现单例的代码,暴露了两个方法给其他类来进行调用。一个是执行任务,一个是停止任务线程池。
执行任务
执行任务这里使用的是ScheduledExecutorService,将定时任务与线程池功能结合使用,用来延迟10毫秒。
ScheduledExecutorService只是一个接口,我们需要实现它,在Ruoyi中的ThreadPoolConfig中
/**
* 执行周期性或定时任务
*/
@Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService()
{
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build())
{
@Override
protected void afterExecute(Runnable r, Throwable t)
{
super.afterExecute(r, t);
Threads.printException(r, t);
}
};
}
在ScheduledExecutorService的帮助下我们可以对各种异步任务进行执行而不阻塞主进程。
对应要执行的方法需要返回TimerTask,具体的代码可以在AsyncFactory
这个异步工厂类中找到。
停止任务线程池
在应用退出时我们需要能够将此时正在执行的后台进程关闭掉。
被
@PreDestroy修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreDestroy修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前。
停止线程池
先使用
shutdown, 停止接收新任务并尝试完成所有已存在任务.
如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
如果仍然超時,则強制退出.
另对在shutdown时线程本身被调用中断做了处理。
总结
除了异步任务这种本机线程池处理代码逻辑,我们也可以使用消息队列来进行耗时任务的操作,防止数据处理耗时太长,影响主逻辑的运行。