IT牛哥与你分享
最近项目上需要接入很多外围系统接口,需要一个定时任务框架,以前的旧框架是用xml配置的方式,而且是需要部署到web容器上,每次停任务或者更改任务的执行频率,都需要重启应用,对于生产环境来说,这个是比较致命的!所以打算重新搭建一个定时任务框架。下面从需求、技术选型与实现、部署几个方面给大家分享一下搭建的过程。
需求
- 功能需求
- 启动、停止、配置执行频率:这个是最基本的需求,主要是满足任务按需启停或者更改执行的频率,不需要对工程进行启停。
- 日志:主要记录任务的调用情况、调用开始与结束时间等信息,便于查找问题。
- 监控与告警:对记录日志进行监控,针对调用异常的任务及时短信或邮件告警。
- restful接口文档自动生成与在线调用:对开发的restful api可以自动生成接口文档,并且可以在线调用。
- 非功能需求
- 更改配置不需要重启。
- 部署简单。
技术选型
经过一番考虑,最终选择使用springboot+quartz+mybatis+oracle+swagger+maven来搭建这个框架。
- springboot:①开源、轻量级框架②简化spring框架的搭建与开发过程③内嵌tomcat④不生成代码、不配置xml
- quartz:①开源作业调度框架②包含调度器监听、作业和触发器监听③使用简单,JOB接口就一个execute方法
- mybatis:①mapper中可以生成一些基本的方法,sql在xml中可配置②主要是感觉hibernate和jpa不好用
- oracle:①用户提供,所以没有②了
- swagger:①用于生成、描述、调用和可视化restful风格的服务②接口文档的在线自动生成③功能测试
- maven:①项目管理工具,管理jar包
实现
- 工程结构树状图
job_schedule_demo│ ├─src│ ├─main│ │ ├─java│ │ │ └─com│ │ │ └─demo│ │ │ └─schedule│ │ │ │ JobScheduleApp.java│ │ │ │ │ │ │ ├─base│ │ │ │ BaseTask.java│ │ │ │ JobFactory.java│ │ │ │ MybatisJavaTypeResolver.java│ │ │ │ QuartzManager.java│ │ │ │ SwaggerConfiguration.java│ │ │ │ │ │ │ ├─common│ │ │ │ Constants.java│ │ │ │ │ │ │ ├─controller│ │ │ │ JobConfigController.java│ │ │ │ │ │ │ ├─entity│ │ │ │ TJobRunningLog.java│ │ │ │ TJobRunningLogCriteria.java│ │ │ │ TJobsConfig.java│ │ │ │ TJobsConfigCriteria.java│ │ │ │ │ │ │ ├─job│ │ │ │ HelloWorldJob.java│ │ │ │ │ │ │ ├─listener│ │ │ │ ScheduleJobInitListener.java│ │ │ │ │ │ │ ├─mapper│ │ │ │ TJobRunningLogMapper.java│ │ │ │ TJobRunningLogMapper.xml│ │ │ │ TJobsConfigMapper.java│ │ │ │ TJobsConfigMapper.xml│ │ │ │ │ │ │ ├─service│ │ │ │ │ JobService.java│ │ │ │ │ │ │ │ │ └─ipml│ │ │ │ JobServiceImpl.java│ │ │ │ │ │ │ └─utils│ │ └─resources│ │ │ application.yml│ │ │ │ │ └─mybatis│ │ gencfg-mybatis-oracle.xml│ │ mybatis-config.xml│ │ │ └─test│ └─java└─target
- 启动类
@SpringBootApplication@MapperScan("com.demo.schedule.mapper")public class JobScheduleApp {public static void main(String[] args) {SpringApplication.run(JobScheduleApp.class, args);}}
- 监听器
@Component@Order(value = 1)public class ScheduleJobInitListener implements CommandLineRunner {private static Logger logger = LoggerFactory.getLogger(ScheduleJobInitListener.class); @Autowired JobService jobService; @Override public void run(String... arg0) throws Exception { try { jobService.initSchedule(); } catch (Exception e) { logger.error("ScheduleJobInitListener Exception", e); } }}
- 初始化有效的任务
/** * * @Title: initSchedule * @Description: 初始化所有有效job * @throws SchedulerException * @throws */@Overridepublic void initSchedule() throws SchedulerException {TJobsConfigCriteria example = new TJobsConfigCriteria();TJobsConfigCriteria.Criteria criteria = example.createCriteria();criteria.andJobStatusEqualTo(Constants.JObSTATUS_MAPPING.get("RUNNING"));criteria.andJobGroupEqualTo("DEMO");List jobList = tjobsconfigMapper.selectByExample(example);for (TJobsConfig job : jobList) {if (Constants.JObSTATUS_MAPPING.get("RUNNING").equals(job.getJobStatus())) {logger.info("will add job:{}", job.getJobName());quartzManager.addJob(job);}}}
这个地方使用了quartz的添加任务
/** * 添加任务 * * @param job * @throws SchedulerException */ @SuppressWarnings("unchecked") public void addJob(TJobsConfig job) { try { // 创建jobDetail实例,绑定Job实现类 // 指明job的名称,所在组的名称,以及绑定job类 Class extends Job> jobClass = (Class extends Job>) (Class.forName(job.getBeanClass()).newInstance() .getClass()); JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(job.getJobName(), job.getJobGroup())// 任务名称和组构成任务key .build(); // 定义调度触发规则 // 使用cornTrigger规则 Trigger trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())// 触发器key .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND)) .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())).startNow().build(); // 把作业和触发器注册到任务调度中 scheduler.scheduleJob(jobDetail, trigger); // 启动 if (!scheduler.isShutdown()) { scheduler.start(); } } catch (Exception e) { log.error("添加任务 异常", e); } }
- 配置用来测试的任务HelloWorldJob
BaseTask中实现了Job接口的execute方法,分别记录了任务执行前后的日志,所有的任务继承BaseTask。
@DisallowConcurrentExecution //作业不并发@Componentpublic class HelloWorldJob extends BaseTask{ public void perform() { System.out.println("欢迎使用,这是一个定时任务框架!"); }}
在数据库表中配置执行频率
执行启动类,定时任务就可以运行了!
部署
mvn install打成jar包上传到服务器指定目录,执行java -jar jar包就可以了。
感兴趣的朋友可以关注我或者给我留言,大家共同学习,共同进步!