优雅的使用Quartz进行任务调度

395 阅读5分钟
1.源起

在做java服务端开发的时候,免不了会接触到定时任务相关模块的开发,什么定时发送邮件,什么定时出报告啦。我这里采用的是quartz开源库,功能强大,还会自动维护数据库,省事,上手比较简单。但是在易用性方面却做的不是很便捷,在项目使用的过程中遇到了一些通点,经过几个版本在迭代,现在的软件界面是你下面看到的效果。我喜欢叫她Task,任务调度小助手。

任务管理界面

2.功能

经过几个版本的迭代,目前包含的功能主要有:

  • 创新的管理界面设计,直观的展示任务的启用状态、任务的执行状态等信息,将常用功能放在列表,方便使用;
  • 在quartz中job是任务执行的最小单元,我们在job的基础上抽象出更加细粒度的task,一个job可以包含多个task;
  • 每个task可单独配置对应的参数,task中的配置参数均是通过注解的形式添加;
  • task包含两种不同的类型,普通的task会按照配置的顺序,按序执行;另一种特别的task叫flat类型,可以将List中的数据挨个取出,一个接一个发送给后续的任务列表处理;此处命名参与自rxjava,嘿嘿;
  • 加入更加完善的任务中断功能,quartz中job中断只给一个bool类型的标记,需要开发者在job中自行处理。Task库中也是基于bool标记,会在任务链执行的过程中对标记进行判断,通过抛异常的方式中断任务。此方法仍然无法做到实时中断任务;
  • task具有排序功能,任务执行的顺序即为task列表的顺序;
  • 加入了任务日志功能,在任务执行过程中各个task返回的结果会以实时日志的形式通过websocket发送到前端展示,后续加入logger手动打日志的功能;
  • Task库中已内置管理界面,只需要在浏览器输入:http://ip:port/task/index.html即可查看任务管理界面。
3.使用

依赖

<dependency>
     <groupId>com.basic</groupId>
     <artifactId>basic-task</artifactId>
     <version>1.1.6</version>
</dependency>
#yml中加入如下配置, task-packages为task存放的包名,可以设置成项目包名
task:
  task-packages: com.piesat.project.admin.task

示例

/**
 * 任务分为公共组public和自定义组,在选择任务时,会按这两大类分组
 * 内置的四种任务均为public
 * 在项目实时的过程中如果有一些是封装好的公共任务可定义为public,方便配置时查找
 */
@TaskAnnotation(name = "公共测试", group = Constants.PUBLIC)
public class PublicTask extends BaseTask<String> {
    
    /*
     *@TaskParam 注解的变量会显示在task属性配置项,配置后在任务执行时会自动注入
     * type/defaultValue/options 三个选项暂时还不能用起来
     */

    private final FileFilter filter;
    @TaskParam(alias = "待扫描的目录路径", type = DataType.STRING)
    private String sourceDirectory;
    @TaskParam(alias = "是否扫描子目录", type = DataType.BOOLEAN, defaultValue = "true", options = {"true", "false"})
    private boolean needScanChildren;
    @TaskParam(alias = "文件过滤器", type = DataType.OBJECT)
    private String fileFilter;

    public PublicTask(TaskInfo taskInfo) {
        super(taskInfo);
        filter = new FileFilter();
        filter.createFromFilterInfo(new FilterInfo(FilterType.FILE.getType(), fileFilter));
    }

    /**
     * 该方法为任务执行时的实际执行者
     * @TaskParam 注解的变量在方法体内均可直接使用
     */
    @Override
    public TaskResult<?> execute() {
        
        return null;
    }
}
/**
 * 方法注解的方式在实际的项目过程中用的最多,直接写在service中是一个不错的方式,方便使用spring中的bean
 * 一个service中可以注入任一多个task,只要你喜欢
 * 你也可以把所有的任务都统一集中到一个service中
 * @Parser注解的方法最终会解析成Task对象,如果对这块感兴趣可以查看TaskInstanceCreator类
 */
@Service
public class TestMethodTask {

    /**
     * name定义任务名称
     * group定义任务组,这里的组跟quartz中的group不是同一个东西,需要区分
     * params用来定义任务接收的参数,没有数量限制,可通过“:”分隔符来设置参数的别名,方便在前端配置任务时识别
     */
    @Parser(name = "task1", group = "test", params = {"path:源地址", "destPath:目标地址"})
    public String execute(Map<String, Object> map) {
        String path = (String) map.get("path");
        String destPath = (String) map.get("destPath");

        return "成功了哦";
    }

    @Parser(name = "flat_task", group = "test", params = {"targetPath", "destPath"})
    public String test1(Map<String, Object> map) {
        return "成功了哦";
    }
}

经过上面两个配置,将会在管理界面显示出如下效果

2673171-cb7742f52a8176c1.webp

2673171-7da121c0ade89d69.webp

2673171-2bdc501cb0995e3f.webp

2673171-2ef2bda9ebbfea9e.webp

4.经过

当前的版本应该是第三代

  • 初代逻辑都写在job中,任务参数写在yml中,通过spring注入;
  • 二代修改为TaskFlow的任务执行形式,将job细化,抽象出task概念;实现将多种task组合成一个job,实现task的复用;
  • 三代加入任务参数定制配置,加入任务日志实时显示,重新设计任务展示界面;
  • 四代还在酝酿中,完善的日志组件?
  • ...... 所有这些换代都是被项目推着走的,做着做着发现维护起来已经十分棘手,唯有求变。 也许这也是我们人生活在这个时代的缩影吧,有时候我们在变得更好不是我们主动的选择,是被时代推着走的。致懒惰的自己!

还有其他好想法的,可以在评论区说道,咱们一起完善。

5.终章

项目后续会发到maven中心仓库,没啥经验,有知道的可在评论出说道说道。 服务器已经买好了,后续把使用文件编辑好发到服务器,方便各位看官使用。

项目地址:basic-task