关于quartz:quartz学习quartz编码方式实现定时任务简例

[toc] 1. Job接口package com.niewj;import com.google.gson.Gson;import org.quartz.*;public class MyJob implements Job { Gson gson = new Gson(); @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { JobDetail jobDetail = jobExecutionContext.getJobDetail(); // 传数据 JobKey jobKey = jobDetail.getKey(); String description = jobDetail.getDescription(); JobDataMap jobDataMap = jobDetail.getJobDataMap(); StringBuffer buff = new StringBuffer(); buff.append("jobKey=").append(gson.toJson(jobKey)) .append("\t description=").append(description) .append("\t jobDataMap=").append(gson.toJson(jobDataMap)); System.out.println(buff); }}2. main办法package com.niewj;import lombok.extern.slf4j.Slf4j;import org.quartz.*;import org.quartz.impl.StdSchedulerFactory;@Slf4jpublic class JobTest { public static void main(String[] args) throws SchedulerException { String cron = "*/5 * * * * ?"; String identity = "1"; String group = "g1"; String desc = "JobDetail工作"; // 传给JobDetail的数据 JobDataMap map = new JobDataMap(); map.put("taskId", "1"); map.put("instanceId", "100"); // 1. 调度器 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.clear(); scheduler.start(); log.info("=================任务调度:调度器已启动==================="); // 2.定义触发器 //定义name/group Trigger trigger = TriggerBuilder.newTrigger().withIdentity(identity, group) .startNow() .withSchedule(CronScheduleBuilder.cronSchedule(cron)) .build(); // 3.定义一个工作JobDetail //定义Job类为HelloJob类,这是真正的执行逻辑所在 JobDetail jobDetail = JobBuilder.newJob(MyJob.class) //定义name/group .withIdentity(identity, group) .withDescription(desc) .usingJobData(map) .build(); // 4. 应用调度器调度JobDetail, 应用trigger触发 scheduler.scheduleJob(jobDetail, trigger); }}3. 输入15:47:10.393 [main] INFO org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor15:47:10.405 [main] INFO org.quartz.simpl.SimpleThreadPool - Job execution threads will use class loader of thread: main15:47:10.430 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl15:47:10.430 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.3.2 created.15:47:10.431 [main] INFO org.quartz.simpl.RAMJobStore - RAMJobStore initialized.15:47:10.433 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.3.2) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED' Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally. NOT STARTED. Currently in standby mode. Number of jobs executed: 0 Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads. Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.15:47:10.433 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'15:47:10.433 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.3.215:47:10.434 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.15:47:10.434 [main] INFO com.niewj.JobTest - =================任务调度:调度器已启动===================15:47:10.434 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 0 triggers15:47:10.489 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers15:47:10.493 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'g1.1', class=com.niewj.MyJob15:47:10.569 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers15:47:10.569 [DefaultQuartzScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job g1.1jobKey={"name":"1","group":"g1"} description=JobDetail工作 jobDataMap={"taskId":"1","instanceId":"100"}15:47:15.009 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'g1.1', class=com.niewj.MyJob15:47:15.010 [DefaultQuartzScheduler_Worker-2] DEBUG org.quartz.core.JobRunShell - Calling execute on job g1.115:47:15.010 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggersjobKey={"name":"1","group":"g1"} description=JobDetail工作 jobDataMap={"taskId":"1","instanceId":"100"}15:47:20.013 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'g1.1', class=com.niewj.MyJob15:47:20.013 [DefaultQuartzScheduler_Worker-3] DEBUG org.quartz.core.JobRunShell - Calling execute on job g1.115:47:20.014 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggersjobKey={"name":"1","group":"g1"} description=JobDetail工作 jobDataMap={"taskId":"1","instanceId":"100"}{"taskId":"1","instanceId":"100"}5秒钟输入一次 ...

December 29, 2022 · 2 min · jiezi

关于quartz:Quartz高可用定时任务快速上手

定时工作使用指南如果你想做定时工作,有高可用方面的需要,或者仅仅想入门快,上手简略,那么选用它准没错。 定时工作模块是对Quartz框架进一步封装,应用更加简洁。 1、引入依赖<dependency> <groupId>xin.altitude.cms</groupId> <artifactId>ucode-cms-quartz</artifactId> <version>1.5.4.1</version></dependency>2、疾速上手实现org.quartz.Job接口;应用注解CronExp增加工作的调度策略;应用注解Component将工作注入容器中。 启动我的项目,定时工作便处于监听与运行中。 @Component@DisallowConcurrentExecution@CronExp(cron = "0/5 * * * * ?")public class DemoJob implements Job { @Override public void execute(JobExecutionContext context) { System.out.println("工作1:" + LocalDateTime.now()); }}3、手动触发定时工作定时工作除了以既有频率周期性运行外,还有通过接口手动被触发的能力。 调用如下接口,可手动触发工作ID编号为jobId的工作。 http://localhost:8080/cms-api/quartz/job/{jobId}如果有手动触发定时工作的需要,则须要工作ID惟一并且已知,因而须要在编写定时工作时手动指定。 @CronExp(id = 1, cron = "0/5 * * * * ?")通过注解CronExp的id属性可指定工作ID,不显示指定则应用随机ID,不满足已知的条件,因而无奈手动触发。 4、带参数工作只管大多数工作不须要注入参数,但仍有大量的场景须要向定时工作注入参数。 public void execute(JobExecutionContext context) { /* 如果在调用工作时传入了参数,则可能从Map中获取 */ Map<String, Object> dataMap = context.getMergedJobDataMap(); /* 比方从Map中获取一个键值对,一般来说参数均为根本数据类型 */ Object key = dataMap.get("key"); System.out.println("工作2:" + LocalDateTime.now() + ": " + key);}在编写定时工作时,可从JobExecutionContext对象中解析一个Map,从而实现参数的注入。 ...

April 1, 2022 · 1 min · jiezi

关于quartz:SpringBoot-如何配置-quartz-为分布式定时任务

SpringBoot 如何配置 quartz 为分布式定时工作首发于 Dale’s blog 背景我的项目须要一个分布式的定时工作,预研之后抉择应用 quartz。quartz 的分布式须要依赖关系型数据库反对存储一些工作信息。建表sql存在于 quartz 我的项目中,github 可见库中提供了多种数据库的执行sql,我的我的项目采纳的 postgres 作为长久化数据库。 配置 quartzQuartzJobFactory /** * quartz job factory * * @author Dale */@Componentpublic class QuartzJobFactory extends AdaptableJobFactory { @Autowired private AutowireCapableBeanFactory autowireCapableBeanFactory; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Object jobInstance = super.createJobInstance(bundle); autowireCapableBeanFactory.autowireBean(jobInstance); return jobInstance; }}QuartzConf /** * 配置分布式 quartz * * @author Dale */@Configurationpublic class QuartzConf { private QuartzJobFactory quartzJobFactory; @Resource(name = "master") private DataSourceProperties masterProperties; @Autowired public void setQuartzJobFactory(QuartzJobFactory quartzJobFactory) { this.quartzJobFactory = quartzJobFactory; } @Bean public SchedulerFactoryBean schedulerFactoryBean() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); // 设置 quartz 的配置文件是 classpath 下的 quartz.properties 文件 propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties")); propertiesFactoryBean.afterPropertiesSet(); SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); schedulerFactoryBean.setQuartzProperties(Objects.requireNonNull(propertiesFactoryBean.getObject())); schedulerFactoryBean.setJobFactory(quartzJobFactory); schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContextKey"); schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true); schedulerFactoryBean.setOverwriteExistingJobs(false); schedulerFactoryBean.setStartupDelay(10); // 设置 quartz 的DataSource 为主库的配置 schedulerFactoryBean.setDataSource(masterProperties.initializeDataSourceBuilder().type(DruidDataSource.class).build()); return schedulerFactoryBean; } @Bean(name = "scheduler") public Scheduler scheduler() throws IOException { return schedulerFactoryBean().getScheduler(); }}留神:因为我的项目先前配置了数据库的主从拆散,所以专用数据库链接,防止额定再有一处数据库连贯配置。quartz.properties ...

October 19, 2021 · 1 min · jiezi

关于quartz:在线QuartzCron定时任务表达式在线生成

在线QuartzCron定时工作表达式在线生成在线QuartzCron定时工作表达式在线生成 Quartz是一个齐全由java编写的开源作业调度框架,Quartz最次要的性能就是调度器(实现定时工作)。本工具能够在线生成QuartzCron表达式 https://tooltt.com/quartzcron/

July 25, 2021 · 1 min · jiezi

关于quartz:项目中使用quartz定时任务框架记录

1.简述我的项目中需要是在开始工夫和完结工夫周期性的执行指定的工作(脚本),完结后可能更新工作状态,对应的工作比拟耗时,每个工作可能执行的工夫是10s到几分钟。根据需要进行设计,周期性的工作有定时工作框架quartz实现,定时工作每次执行将工作信息扔到音讯队列中,音讯队列消费者多线程生产音讯,工作执行实现后确认生产了音讯,并更新工作后果状态2.quartz 调度器A.注册Scheduler配置文件```javaorg.quartz.scheduler.instanceName = PeriodCheckQuartzSchedulerorg.quartz.scheduler.rmi.export = falseorg.quartz.scheduler.rmi.proxy = falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction = falseorg.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount = 5org.quartz.threadPool.threadPriority = 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = trueorg.quartz.jobStore.misfireThreshold = 5000org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.tablePrefix = QRTZ_org.quartz.jobStore.dataSource = qzDSorg.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driverorg.quartz.dataSource.qzDS.URL = jdbc:mysql://10.50.0.152:3306/cmp?characterEncoding=utf8org.quartz.dataSource.qzDS.user = rootorg.quartz.dataSource.qzDS.password = 123456```B.注册bean ```java @Configurationpublic class SchedulerConfig { @Autowired private SpringJobFactory springJobFactory; @Autowired private QuarzProperties quarzProperties; @Bean(name = "SchedulerFactory") public SchedulerFactoryBean schedulerFactoryBean() throws IOException { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setAutoStartup(true); factory.setStartupDelay(5);//延时5秒启动 factory.setQuartzProperties(quartzProperties()); factory.setJobFactory(springJobFactory); return factory; } @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setProperties(quarzProperties.getQuartzProperties()); propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } @Bean(name="Scheduler") public Scheduler scheduler() throws IOException { return schedulerFactoryBean().getScheduler(); }} ```3.自定义Job工作 import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.io.Serializable; @DisallowConcurrentExecution public class PeriodCheckJob implements Job, Serializable { private static final long serialVersionUID = 1L; private static final Logger logger = LoggerFactory.getLogger(PeriodCheckJob.class); @Autowired private PeriodCheckService periodCheckService; @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { //业务逻辑 } }4.JobDetail和TriggerClass dj = Class.forName(PeriodCheckJob.class.getName());dj.newInstance();JobDetail jobDetail = JobBuilder.newJob(dj).withIdentity(new JobKey(PeriodCheckJob.class.toString() + expression + checkId, JOB_GROUP)).withDescription("description").build();jobDetail.getJobDataMap().put("checkId", checkId);CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(expression).withMisfireHandlingInstructionDoNothing();//开始工夫和完结工夫,以及通过cron表达式定义执行工夫Trigger trigger = trigger = TriggerBuilder.newTrigger().withIdentity(new TriggerKey(PeriodCheck.class.toString() + expression + checkId, "TRIGGER_KEY_" + expression)) .startAt(startTime).endAt(endTime).withSchedule(cronScheduleBuilder).build();scheduler.scheduleJob(jobDetail, trigger);5.工作状态A.敞开工作JobKey jobKey = new JobKey(PeriodCheckJob.class.toString() + expression + checkId, JOB_GROUP); scheduler.pauseJob(jobKey);//暂停工作scheduler.deleteJob(jobKey);//敞开工作scheduler.resumeJob(jobKey);//重启工作//批改工作工作配置信息TriggerKey triggerKey = new TriggerKey(InspectPlan.class.toString(), planId.toString());CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(inspectPlan.getCronExpression());CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();scheduler.rescheduleJob(triggerKey, trigger);//从新调度工作###6.工作完结事件监听 ...

March 11, 2021 · 2 min · jiezi

关于quartz:quartz简单用法用cron表达式

1.实现Job接口 --你要做什么事? public class myJob implements Job { @Autowired private PileStateManager pileStateManager; @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { //判断以后工夫发送指定费率报文 int hours = new Date().getHours(); if (hours >= 7 && hours < 11) { System.out.println("我是尖"); // buf.writeBytes(ByteConvertUtil.hourChargeTo2Bytes((int) cachePile.getOneRate())); } else if (hours >= 19 && hours < 23) { System.out.println("我是峰"); // buf.writeBytes(ByteConvertUtil.hourChargeTo2Bytes((int) cachePile.getTwoRate())); } else if (hours >= 11 && hours < 19) { System.out.println("我是平"); pileStateManager.UpdateChargeRate(); //buf.writeBytes(ByteConvertUtil.hourChargeTo2Bytes((int) cachePile.getThreeRate())); } else { System.out.println("我是谷"); // buf.writeBytes(ByteConvertUtil.hourChargeTo2Bytes((int) cachePile.getFourRate())); } }2.Tirgger -触发器 --你什么时候去做3.Scheduler -任务调度器 --你什么时候须要去做什么事 ...

January 7, 2021 · 1 min · jiezi

关于quartz:Quartz使用大全

http://www.quartz-scheduler.o... 1.应用QuartzQuartz Scheduler一旦敞开,无奈重启 须要从新实例化提供了暂停状态SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();Scheduler sched = schedFact.getScheduler();sched.start();// define the job and tie it to our HelloJob classJobDetail job = newJob(HelloJob.class) .withIdentity("myJob", "group1") .build();// Trigger the job to run now, and then every 40 secondsTrigger trigger = newTrigger() .withIdentity("myTrigger", "group1") .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(40) .repeatForever()) .build();// Tell quartz to schedule the job using our triggersched.scheduleJob(job, trigger);2.Quartz API,JOB和Triggerhttp://www.quartz-scheduler.o... JobBuilder-用于定义/构建JobDetail实例,该实例定义Jobs实例。JobDetail-用于定义Job的实例。Job-由您心愿由调度程序执行的组件实现的接口。TriggerBuilder-用于定义/构建触发器实例。Trigger-定义将在其上执行给定作业的时间表的组件。Scheduler一个Scheduler的生命周期是由它的创作范畴,通过SchedulerFactory来调用他的创立和敞开。 创立Scheduler后,就能够应用它 增加删除列出Job和Trigger 以及执行其余与打算相干的操作(例如暂停触发器)。然而,调度程序在应用starter之前实际上不会对任何trigger执行工作起作用 Builder的演示 // define the job and tie it to our HelloJob class JobDetail job = newJob(HelloJob.class) .withIdentity("myJob", "group1") // name "myJob", group "group1" .build(); // Trigger the job to run now, and then every 40 seconds Trigger trigger = newTrigger() .withIdentity("myTrigger", "group1") .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(40) .repeatForever()) .build(); // Tell quartz to schedule the job using our trigger sched.scheduleJob(job, trigger);Jobs and TriggersJob是一个接口 ...

December 7, 2020 · 6 min · jiezi

关于quartz:Quartz框架实现定时业务

超时订单阐明:电商我的项目中,如果订单提交之后如果30分钟没有实现付款,则将订单状态由1(待付款)改为6(交易失败).示意订单交易敞开.问题:如何实现每个订单30分钟超时? 思路1: 利用数据库的计时的函数每当order入库之后,能够增加一个函数30分钟之后批改状态.该办法不敌对, 100万的订单刚刚入库. 100万个监听的事件.会使数据库因为监听而解体. 思路2: 利用音讯队列的形式实现 redis 开启线程向redis中存储数据之后设定超时工夫.当key一旦生效则批改数据库状态.Redis次要做缓存应用. 然而不适合. 思路3:开启独自的一个线程(异步),每隔1分钟查问一次数据库,批改超时的订单解决即可. Quartz框架介绍java原生的timer也能够实现开启独自线程实现定时工作,然而有一些缺点,当初这个Quartz是比拟罕用的模式. Quartz是OpenSymphony开源组织在Job scheduling畛域又一个开源我的项目,它能够与J2EE与J2SE应用程序相结合也能够独自应用。Quartz能够用来创立简略或为运行十个,百个,甚至是好几万个Jobs这样简单的程序。Jobs能够做成规范的Java组件或 EJBs。Quartz的最新版本为Quartz 2.3.2。 组件阐明: Job 是用户自定义的工作.JobDetail 负责封装工作的工具API.如果工作须要被执行,则必须通过jobDetail封装.Schedular调度器: 负责工夫监控,当工作的执行工夫一到则交给触发器解决Trigger触发器: 当接管到调度器的命令则开启新的线程执行job…Quartz实现导入jar包<!--增加Quartz的反对 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId></dependency>编辑配置类@Configurationpublic class OrderQuartzConfig { /** * 思维阐明: * 如果须要执行定时工作须要思考的问题 * 1.工作多久执行一次. 1分钟执行一次 * 2.定时工作应该执行什么 */ //定义工作详情 @Bean public JobDetail orderjobDetail() { //指定job的名称和长久化保留工作 return JobBuilder .newJob(OrderQuartz.class) //1.定义执行的工作 .withIdentity("orderQuartz") //2.工作指定名称 .storeDurably() .build(); } //定义触发器 @Bean public Trigger orderTrigger() { /*SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInMinutes(1) //定义工夫周期 .repeatForever();*/ CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0 0/1 * * * ?"); return TriggerBuilder .newTrigger() .forJob(orderjobDetail()) //执行的工作 .withIdentity("orderQuartz") //工作的名称 .withSchedule(scheduleBuilder).build(); }} 编辑定时工作//筹备订单定时工作@Componentpublic class OrderQuartz extends QuartzJobBean{ @Autowired private OrderMapper orderMapper; /** * 如果用户30分钟之内没有实现领取,则将订单的状态status由1改为6. * 条件判断的根据: now()-创立工夫 > 30分钟 <==> created < now()-30 * * sql: update tb_order set status=6,updated=#{updated} where status=1 and created< #{timeOut} * @param context * @throws JobExecutionException */ @Override @Transactional protected void executeInternal(JobExecutionContext context) throws JobExecutionException { //1.利用java工具API实现计算 Calendar calendar = Calendar.getInstance(); //获取以后的工夫 calendar.add(Calendar.MINUTE,-30); Date timeOut = calendar.getTime(); //获取超时工夫 Order order = new Order(); order.setStatus(6); //order.setUpdated(new Date()); UpdateWrapper<Order> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("status", "1").lt("created",timeOut); orderMapper.update(order, updateWrapper); System.out.println("定时工作执行"); }}

September 21, 2020 · 1 min · jiezi

cron表达式基础讲解

初识 corncorn 表达式是一个字符串,分为 6 或 7 个域,每个域都会代表一个含义。 语法格式6 个域:second minute hour day month week7 个域:second minute hour day month week year由上可见,7 个域与 6 个域的语法只差了 year,一般情况下,我们使用 6 个域的结构。 corn表达式结构corn 从左到右(中间用空格隔开):秒 分 小时 月份中的日 月份 星期中的日期 年份 各个字段的含义 位置时间域名允许值允许的特殊字符1秒0-59, - * /2分0-59, - * /3小时0-23, - * /4日1-31, - * ? / L W C5月0-12, - * / 6星期1-7, - * ? / L C #7年(可选)1970-2099, - * /代码示例声明:本次的 cron 讲解结合了 springboot 框架。由于只是讲解 cron 表达式,所以就不在介绍 springboot 中如何使用 @Scheduled 注解的相关配置了秒的位置允许值为 0-59,如果写 60,将会报错 ...

July 7, 2019 · 2 min · jiezi

XXLJOB-v210分布式任务调度平台

v2.0.2 Release Notes1、自研调度组件,移除quartz依赖:一方面是为了精简系统降低冗余依赖,另一方面是为了提供系统的可控度与稳定性; 触发:单节点周期性触发,运行事件如delayqueue;调度:集群竞争,负载方式协同处理,锁竞争-更新触发信息-推送时间轮-锁释放-锁竞争;2、底层表结构重构:移除11张quartz相关表,并对现有表结构优化梳理;3、任务日志主键调整为long数据类型,防止海量日志情况下数据溢出;4、底层线程模型重构:移除Quartz线程池,降低系统线程与内存开销;5、用户管理:支持在线管理系统用户,存在管理员、普通用户两种角色;6、权限管理:执行器维度进行权限控制,管理员拥有全量权限,普通用户需要分配执行器权限后才允许相关操作;7、调度线程池参数调优;8、注册表索引优化,缓解锁表问题;9、新增Jboot执行器Sample示例项目;10、任务列表优化,支持根据 "任务状态"、"负责人" 属性筛选任务;11、任务日志列表交互优化,操作按钮合并为分割按钮;12、项目依赖升级至较新稳定版本,如spring、springboot、groovy、xxl-rpc等等;并清理冗余POM;13、升级xxl-rpc至较新版本,修复代理服务初始化时远程服务不可用导致长连冗余创建的问题;14、首页调度报表的日期排序在TIDB下乱序问题修复;15、调度中心与执行器双向通讯超时时间调整为3s;16、调度组件销毁流程优化,先停止调度线程,然后等待时间轮内存量任务处理完成,最终销毁时间轮线程;17、执行器回调线程优化,回调地址为空时销毁问题修复;18、HttpJobHandler优化,响应数据指定UTF-8格式,避免中文乱码;19、代码优化,ConcurrentHashMap变量类型改为ConcurrentMap,避免因不同版本实现不同导致的兼容性问题;简介XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。 特性1、简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手;2、动态:支持动态修改任务状态、启动/停止任务,以及终止运行中任务,即时生效;3、调度中心HA(中心式):调度采用中心式设计,“调度中心”自研调度组件并支持集群部署,可保证调度中心HA;4、执行器HA(分布式):任务分布式执行,任务"执行器"支持集群部署,可保证任务执行HA;5、注册中心: 执行器会周期性自动注册任务, 调度中心将会自动发现注册的任务并触发执行。同时,也支持手动录入执行器地址;6、弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配任务;7、路由策略:执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;8、故障转移:任务路由策略选择"故障转移"情况下,如果执行器集群中某一台机器故障,将会自动Failover切换到一台正常的执行器发送调度请求。9、阻塞处理策略:调度过于密集执行器来不及处理时的处理策略,策略包括:单机串行(默认)、丢弃后续调度、覆盖之前调度;10、任务超时控制:支持自定义任务超时时间,任务运行超时将会主动中断任务;11、任务失败重试:支持自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;其中分片任务支持分片粒度的失败重试;12、任务失败告警;默认提供邮件方式失败告警,同时预留扩展接口,可方便的扩展短信、钉钉等告警方式;13、分片广播任务:执行器集群部署时,任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发集群中所有执行器执行一次任务,可根据分片参数开发分片任务;14、动态分片:分片广播任务以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。15、事件触发:除了"Cron方式"和"任务依赖方式"触发任务执行之外,支持基于事件的触发任务方式。调度中心提供触发任务单次执行的API服务,可根据业务事件灵活触发。16、任务进度监控:支持实时监控任务进度;17、Rolling实时日志:支持在线查看调度结果,并且支持以Rolling方式实时查看执行器输出的完整的执行日志;18、GLUE:提供Web IDE,支持在线开发任务逻辑代码,动态发布,实时编译生效,省略部署上线的过程。支持30个版本的历史版本回溯。19、脚本任务:支持以GLUE模式开发和运行脚本任务,包括Shell、Python、NodeJS、PHP、PowerShell等类型脚本;20、命令行任务:原生提供通用命令行任务Handler(Bean任务,"CommandJobHandler");业务方只需要提供命令行即可;21、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行, 多个子任务用逗号分隔;22、一致性:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行;23、自定义任务参数:支持在线配置调度任务入参,即时生效;24、调度线程池:调度系统多线程触发调度运行,确保调度精确执行,不被堵塞;25、数据加密:调度中心和执行器之间的通讯进行数据加密,提升调度信息安全性;26、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;27、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用;28、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等;29、全异步:任务调度流程全异步化设计实现,如异步调度、异步运行、异步回调等,有效对密集调度进行流量削峰,理论上支持任意时长任务的运行;30、跨平台:原生提供通用HTTP任务Handler(Bean任务,"HttpJobHandler");业务方只需要提供HTTP链接即可,不限制语言、平台;31、国际化:调度中心支持国际化设置,提供中文、英文两种可选语言,默认为中文;32、容器化:提供官方docker镜像,并实时更新推送dockerhub,进一步实现产品开箱即用;33、线程池隔离:调度线程池进行隔离拆分,慢任务自动降级进入"Slow"线程池,避免耗尽调度线程,提高系统稳定性;;34、用户管理:支持在线管理系统用户,存在管理员、普通用户两种角色;35、权限控制:执行器维度进行权限控制,管理员拥有全量权限,普通用户需要分配执行器权限后才允许相关操作;文档地址中文文档技术交流社区交流

July 7, 2019 · 1 min · jiezi

那些年我们追过的定时调度

定时调度作为后端开发人员,我们总会遇到这样的业务场景:每周同步一批数据;每半个小时检查一遍服务器运行状况;每天早上八点给用户发送一份包含今日待办事项的邮件,等等。 这些场景中都离不开“定时器”,就像一个定好时间规则的闹钟,它会在指定时间触发,执行我们想要定义的调度任务。那么我们今天就来数一下,那些年我们用过的“定时调度”。 1.job (oracle)从刚工作就一直使用oracle数据库,最早接触的定时任务就是oracle数据库的job。job有定时执行的功能,可以在指定的时间点或每天的某个时间点自行执行任务。 而且oracle重新启动后,job会继续运行,不用重新启动。而且job的机制非常完备,可以查询相关的表或视图,查询job的定时规则和执行情况。缺点是作为oracle数据库层面的工具,自定义功能扩展,二次开发的难度比较大。 1.1 创建job 1.2 删除job 1.3 查询job2.crontab (linux)crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务 工具,并且会自动启动crond进程,crond进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。 cron是服务名称,crond是后台进程,crontab则是定制好的计划任务表。大部分linux系统默认都安装了cron,可以检查一下。 crontab基本操作命令 crontab表达式格式 3.Timer和ScheduledExecutorService (java)Timer是jdk中提供的一个定时器工具,使用的时候会在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次。 TimerTask是一个实现了Runnable接口的抽象类,代表一个可以被Timer执行的任务。TimerTask类是一个抽象类,由Timer 安排为一次执行或重复执行的任务。它有一个抽象方法run()方法,该方法用于执行相应计时器任务要执行的操作。因此每一个具体的任务类都必须继承TimerTask,然后重写run()方法。 另外它还有两个非抽象的方法 当然,一般使用Timer的比较少,因为它的缺点比较明显: 1.单线程,当多个timer同时运行时,会等上一个执行完成,再执行下一个。2.Timer线程是不会捕获异常的,如果TimerTask抛出的了未检查异常则会导致Timer线程终止。 所以一般使用ScheduledExecutorService替代Timer。 ScheduledExecutorService:也是jdk自带的一个基于线程池设计的定时任务类。其每个调度任务都会分配到线程池中的一个线程执行,所以其任务是并发执行的,互不影响。 4.SpringTask (spring)Timer和ScheduledExecutorService都是属于jdk层面上实现定时调度的类,功能还不足以让我们满意,那么现在介绍一个比较完善的定时调度工具 - SpringTask,是Spring提供的,支持注解和配置文件形式,支持crontab表达式,使用简单但功能强大。我个人非常喜欢SpringTask,仅仅是因为支持crontab表达式。 在springboot里面使用方式非常简单: 1.启动类添加开启定时调度的注解 @EnableScheduling2.在需要定时执行的方法上,增加注解 @Scheduled(cron ="crontab表达式") 默认的简单的使用步骤只有以上两步,但是SpringTask的默认使用方式也有一些不足: 1.默认线程池的poolsize为1,可以理解为Timer类似的单线程模式。无法动态修改crontab表达式,修改完只能重新部署后,才能生效。 问题1的解决方式,可以通过自定义 TaskExecutor来修改当前的线程池。问题2,则可以直接使用 threadPoolTaskScheduler类实现自定义的定时调度规则。 附解决两个问题的源码 TaskTimer.class 5.Quartz (其他产品)Quartz是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。它是一个功能强大、十分成熟的重量级产品,还支持负载均衡,实现分布式调度。 不过,对于Quartz的安装你要多花点功夫了,从数据库要建哪些表,到应用程序该如何部署。对于这样一个庞大的产品,本篇文章就不附上它的使用说明书了。 本人创业团队产品MadPecker,主要做BUG管理、测试管理、应用分发,网址:www.madpecker.com,有需要的朋友欢迎试用、体验!本文为MadPecker团队技术人员编写,转载请标明出处

June 17, 2019 · 1 min · jiezi

Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)

照例附上项目github链接本项目实现的是将一个简单的天气预报系统一步一步改造成一个SpringCloud微服务系统的过程,本节主要讲的是通过引入Quartz实现天气数据的同步。存在问题当用户请求我们的数据的时候才去拉最新的数据,并将其更新到Redis缓存中,效率较低。且缓存中的数据只要存在就不再次做请求,不对数据进行更新,但是天气数据大概是每半个小时就做一次更新的,所以我们传给用户的数据可能不是较新的,数据存在一定误差。解决方案通过作业调度框架Quartz实现天气数据的自动同步。前期工作要实现定时拉取接口中的数据到Redis缓存中,需要一个城市Id的列表。通过对城市Id列表的遍历,调用weatherDataService中根据城市Id同步数据到Redis中的syncDataByCityId方法,我们就能实现所有城市数据的同步了。城市列表的构建由于在程序运行的过程中动态调用服务是有延时的,所以需要减少动态调用服务,因此我们将城市列表缓存到本地。xml文件的构建使用xml文件将列表存储到本地中,需要的时候再从本地进行读取,这样会比调用第三方的服务更快。xml文件如下:<?xml version=“1.0” encoding=“UTF-8”?><c c1=“0”><d d1=“101280101” d2=“广州” d3=“guangzhou” d4=“广东”/><d d1=“101280102” d2=“番禺” d3=“panyu” d4=“广东”/><d d1=“101280103” d2=“从化” d3=“conghua” d4=“广东”/><d d1=“101280104” d2=“增城” d3=“zengcheng” d4=“广东”/><d d1=“101280105” d2=“花都” d3=“huadu” d4=“广东”/><d d1=“101280201” d2=“韶关” d3=“shaoguan” d4=“广东”/><d d1=“101280202” d2=“乳源” d3=“ruyuan” d4=“广东”/><d d1=“101280203” d2=“始兴” d3=“shixing” d4=“广东”/><d d1=“101280204” d2=“翁源” d3=“wengyuan” d4=“广东”/><d d1=“101280205” d2=“乐昌” d3=“lechang” d4=“广东”/><d d1=“101280206” d2=“仁化” d3=“renhua” d4=“广东”/><d d1=“101280207” d2=“南雄” d3=“nanxiong” d4=“广东”/><d d1=“101280208” d2=“新丰” d3=“xinfeng” d4=“广东”/><d d1=“101280209” d2=“曲江” d3=“qujiang” d4=“广东”/><d d1=“101280210” d2=“浈江” d3=“chengjiang” d4=“广东”/><d d1=“101280211” d2=“武江” d3=“wujiang” d4=“广东”/><d d1=“101280301” d2=“惠州” d3=“huizhou” d4=“广东”/><d d1=“101280302” d2=“博罗” d3=“boluo” d4=“广东”/><d d1=“101280303” d2=“惠阳” d3=“huiyang” d4=“广东”/><d d1=“101280304” d2=“惠东” d3=“huidong” d4=“广东”/><d d1=“101280305” d2=“龙门” d3=“longmen” d4=“广东”/><d d1=“101280401” d2=“梅州” d3=“meizhou” d4=“广东”/><d d1=“101280402” d2=“兴宁” d3=“xingning” d4=“广东”/><d d1=“101280403” d2=“蕉岭” d3=“jiaoling” d4=“广东”/><d d1=“101280404” d2=“大埔” d3=“dabu” d4=“广东”/><d d1=“101280406” d2=“丰顺” d3=“fengshun” d4=“广东”/><d d1=“101280407” d2=“平远” d3=“pingyuan” d4=“广东”/><d d1=“101280408” d2=“五华” d3=“wuhua” d4=“广东”/><d d1=“101280409” d2=“梅县” d3=“meixian” d4=“广东”/><d d1=“101280501” d2=“汕头” d3=“shantou” d4=“广东”/><d d1=“101280502” d2=“潮阳” d3=“chaoyang” d4=“广东”/><d d1=“101280503” d2=“澄海” d3=“chenghai” d4=“广东”/><d d1=“101280504” d2=“南澳” d3=“nanao” d4=“广东”/><d d1=“101280601” d2=“深圳” d3=“shenzhen” d4=“广东”/><d d1=“101280701” d2=“珠海” d3=“zhuhai” d4=“广东”/><d d1=“101280702” d2=“斗门” d3=“doumen” d4=“广东”/><d d1=“101280703” d2=“金湾” d3=“jinwan” d4=“广东”/><d d1=“101280800” d2=“佛山” d3=“foshan” d4=“广东”/><d d1=“101280801” d2=“顺德” d3=“shunde” d4=“广东”/><d d1=“101280802” d2=“三水” d3=“sanshui” d4=“广东”/><d d1=“101280803” d2=“南海” d3=“nanhai” d4=“广东”/><d d1=“101280804” d2=“高明” d3=“gaoming” d4=“广东”/><d d1=“101280901” d2=“肇庆” d3=“zhaoqing” d4=“广东”/><d d1=“101280902” d2=“广宁” d3=“guangning” d4=“广东”/><d d1=“101280903” d2=“四会” d3=“sihui” d4=“广东”/><d d1=“101280905” d2=“德庆” d3=“deqing” d4=“广东”/><d d1=“101280906” d2=“怀集” d3=“huaiji” d4=“广东”/><d d1=“101280907” d2=“封开” d3=“fengkai” d4=“广东”/><d d1=“101280908” d2=“高要” d3=“gaoyao” d4=“广东”/><d d1=“101281001” d2=“湛江” d3=“zhanjiang” d4=“广东”/><d d1=“101281002” d2=“吴川” d3=“wuchuan” d4=“广东”/><d d1=“101281003” d2=“雷州” d3=“leizhou” d4=“广东”/><d d1=“101281004” d2=“徐闻” d3=“xuwen” d4=“广东”/><d d1=“101281005” d2=“廉江” d3=“lianjiang” d4=“广东”/><d d1=“101281006” d2=“赤坎” d3=“chikan” d4=“广东”/><d d1=“101281007” d2=“遂溪” d3=“suixi” d4=“广东”/><d d1=“101281008” d2=“坡头” d3=“potou” d4=“广东”/><d d1=“101281009” d2=“霞山” d3=“xiashan” d4=“广东”/><d d1=“101281010” d2=“麻章” d3=“mazhang” d4=“广东”/><d d1=“101281101” d2=“江门” d3=“jiangmen” d4=“广东”/><d d1=“101281103” d2=“开平” d3=“kaiping” d4=“广东”/><d d1=“101281104” d2=“新会” d3=“xinhui” d4=“广东”/><d d1=“101281105” d2=“恩平” d3=“enping” d4=“广东”/><d d1=“101281106” d2=“台山” d3=“taishan” d4=“广东”/><d d1=“101281107” d2=“蓬江” d3=“pengjiang” d4=“广东”/><d d1=“101281108” d2=“鹤山” d3=“heshan” d4=“广东”/><d d1=“101281109” d2=“江海” d3=“jianghai” d4=“广东”/><d d1=“101281201” d2=“河源” d3=“heyuan” d4=“广东”/><d d1=“101281202” d2=“紫金” d3=“zijin” d4=“广东”/><d d1=“101281203” d2=“连平” d3=“lianping” d4=“广东”/><d d1=“101281204” d2=“和平” d3=“heping” d4=“广东”/><d d1=“101281205” d2=“龙川” d3=“longchuan” d4=“广东”/><d d1=“101281206” d2=“东源” d3=“dongyuan” d4=“广东”/><d d1=“101281301” d2=“清远” d3=“qingyuan” d4=“广东”/><d d1=“101281302” d2=“连南” d3=“liannan” d4=“广东”/><d d1=“101281303” d2=“连州” d3=“lianzhou” d4=“广东”/><d d1=“101281304” d2=“连山” d3=“lianshan” d4=“广东”/><d d1=“101281305” d2=“阳山” d3=“yangshan” d4=“广东”/><d d1=“101281306” d2=“佛冈” d3=“fogang” d4=“广东”/><d d1=“101281307” d2=“英德” d3=“yingde” d4=“广东”/><d d1=“101281308” d2=“清新” d3=“qingxin” d4=“广东”/><d d1=“101281401” d2=“云浮” d3=“yunfu” d4=“广东”/><d d1=“101281402” d2=“罗定” d3=“luoding” d4=“广东”/><d d1=“101281403” d2=“新兴” d3=“xinxing” d4=“广东”/><d d1=“101281404” d2=“郁南” d3=“yunan” d4=“广东”/><d d1=“101281406” d2=“云安” d3=“yunan” d4=“广东”/><d d1=“101281501” d2=“潮州” d3=“chaozhou” d4=“广东”/><d d1=“101281502” d2=“饶平” d3=“raoping” d4=“广东”/><d d1=“101281503” d2=“潮安” d3=“chaoan” d4=“广东”/><d d1=“101281601” d2=“东莞” d3=“dongguan” d4=“广东”/><d d1=“101281701” d2=“中山” d3=“zhongshan” d4=“广东”/><d d1=“101281801” d2=“阳江” d3=“yangjiang” d4=“广东”/><d d1=“101281802” d2=“阳春” d3=“yangchun” d4=“广东”/><d d1=“101281803” d2=“阳东” d3=“yangdong” d4=“广东”/><d d1=“101281804” d2=“阳西” d3=“yangxi” d4=“广东”/><d d1=“101281901” d2=“揭阳” d3=“jieyang” d4=“广东”/><d d1=“101281902” d2=“揭西” d3=“jiexi” d4=“广东”/><d d1=“101281903” d2=“普宁” d3=“puning” d4=“广东”/><d d1=“101281904” d2=“惠来” d3=“huilai” d4=“广东”/><d d1=“101281905” d2=“揭东” d3=“jiedong” d4=“广东”/><d d1=“101282001” d2=“茂名” d3=“maoming” d4=“广东”/><d d1=“101282002” d2=“高州” d3=“gaozhou” d4=“广东”/><d d1=“101282003” d2=“化州” d3=“huazhou” d4=“广东”/><d d1=“101282004” d2=“电白” d3=“dianbai” d4=“广东”/><d d1=“101282005” d2=“信宜” d3=“xinyi” d4=“广东”/><d d1=“101282006” d2=“茂港” d3=“maogang” d4=“广东”/><d d1=“101282101” d2=“汕尾” d3=“shanwei” d4=“广东”/><d d1=“101282102” d2=“海丰” d3=“haifeng” d4=“广东”/><d d1=“101282103” d2=“陆丰” d3=“lufeng” d4=“广东”/><d d1=“101282104” d2=“陆河” d3=“luhe” d4=“广东”/></c>创建如下两个类,并且根据xml的内容定义其属性。package com.demo.vo;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlAttribute;import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement(name=“d”)@XmlAccessorType(XmlAccessType.FIELD)public class City { @XmlAttribute(name=“d1”) private String cityId; @XmlAttribute(name=“d2”) private String cityName; @XmlAttribute(name=“d3”) private String cityCode; @XmlAttribute(name=“d4”) private String province; public String getCityId() { return cityId; } public void setCityId(String cityId) { this.cityId = cityId; } public String getCityName() { return cityName; } public void setCityName(String cityName) { this.cityName = cityName; } public String getCityCode() { return cityCode; } public void setCityCode(String cityCode) { this.cityCode = cityCode; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } }package com.demo.vo;import java.util.List;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlElement;import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement(name = “c”)@XmlAccessorType(XmlAccessType.FIELD)public class CityList { @XmlElement(name = “d”) private List<City> cityList; public List<City> getCityList() { return cityList; } public void setCityList(List<City> cityList) { this.cityList = cityList; }}引入工具类,实现将xml转换成java对象的过程。public class XmlBuilder { /** * 将XML转为指定的POJO * @param clazz * @param xmlStr * @return * @throws Exception */ public static Object xmlStrToOject(Class<?> clazz, String xmlStr) throws Exception { Object xmlObject = null; Reader reader = null; JAXBContext context = JAXBContext.newInstance(clazz); // XML 转为对象的接口 Unmarshaller unmarshaller = context.createUnmarshaller(); reader = new StringReader(xmlStr); xmlObject = unmarshaller.unmarshal(reader); if (null != reader) { reader.close(); } return xmlObject; }}获取城市列表的接口创建CityDataService,定义获取城市列表的方法。@Servicepublic class CityDataServiceImpl implements CityDataService{ @Override public List<City> listCity() throws Exception { Resource resource=new ClassPathResource(“citylist.xml”); BufferedReader br=new BufferedReader(new InputStreamReader(resource.getInputStream(), “utf-8”)); StringBuffer buffer=new StringBuffer(); String line=""; while((line=br.readLine())!=null) { buffer.append(line); } br.close(); CityList cityList=(CityList)XmlBuilder.xmlStrToOject(CityList.class, buffer.toString()); return cityList.getCityList(); }}根据城市Id同步天气数据的接口首先通过城市Id构建对应天气数据的url,然后通过restTemplate的getForEntity方法发起请求,获取返回的内容后使用set方法将其保存到Redis服务器中。 @Override public void syncDataByCityId(String cityId) { String url=WEATHER_URI+“citykey=” + cityId; this.saveWeatherData(url); } //将天气数据保存到缓存中,不管缓存中是否存在数据 private void saveWeatherData(String url) { //将url作为天气的key进行保存 String key=url; String strBody=null; ValueOperations<String, String>ops=stringRedisTemplate.opsForValue(); //通过客户端的get方法发起请求 ResponseEntity<String>respString=restTemplate.getForEntity(url, String.class); //判断请求状态 if(respString.getStatusCodeValue()==200) { strBody=respString.getBody(); } ops.set(key, strBody,TIME_OUT,TimeUnit.SECONDS); }Quartz的引入Quartz是一个Quartz是一个完全由java编写的开源作业调度框架,在这里的功能相当于一个定时器,定时执行指定的任务。创建同步天气数据的任务在Quartz中每个任务就是一个job,在这里我们创建一个同步天气数据的job。通过cityDataService的listCity方法获取xml文件中所有城市的列表,通过对城市列表的迭代得到所有城市的Id,然后通过weatherDataService的syncDataByCityId方法将对应Id的城市天气数据更新到Redis缓存中//同步天气数据public class WeatherDataSyncJob extends QuartzJobBean{ private final static Logger logger = LoggerFactory.getLogger(WeatherDataSyncJob.class); @Autowired private CityDataService cityDataService; @Autowired private WeatherDataService weatherDataService; @Override protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException { logger.info(“Weather Data Sync Job. Start!”); //城市ID列表 List<City>cityList=null; try { //获取xml中的城市ID列表 cityList=cityDataService.listCity(); } catch (Exception e) {// e.printStackTrace(); logger.error(“Exception!”, e); } //遍历所有城市ID获取天气 for(City city:cityList) { String cityId=city.getCityId(); logger.info(“Weather Data Sync Job, cityId:” + cityId); //实现根据cityid定时同步天气数据到缓存中 weatherDataService.syncDataByCityId(cityId); } logger.info(“Weather Data Sync Job. End!”); } }配置QuartzTIME设置的是更新的频率,表示每隔TIME秒就执行任务一次。@Configurationpublic class QuartzConfiguration { private static final int TIME = 1800; // 更新频率 // JobDetail @Bean public JobDetail weatherDataSyncJobDetail() { return JobBuilder.newJob(WeatherDataSyncJob.class).withIdentity(“weatherDataSyncJob”) .storeDurably().build(); } // Trigger @Bean public Trigger weatherDataSyncTrigger() { SimpleScheduleBuilder schedBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(TIME).repeatForever(); return TriggerBuilder.newTrigger().forJob(weatherDataSyncJobDetail()) .withIdentity(“weatherDataSyncTrigger”).withSchedule(schedBuilder).build(); }}测试结果天气数据同步结果 ...

February 28, 2019 · 4 min · jiezi

Grails集成Quartz插件实现定时任务(Job)

build.gradle依赖://定时器compile ‘org.grails.plugins:quartz:2.0.0.M4’创建一个job,执行命令:create-job com.test.Test修改TestJob:package com.testclass TestJob {static triggers = {} def execute(context) { // execute job println “执行…” // 获取传递过来的参数 def msg = context.mergedJobDataMap.get(“tip”) println msg }}控制器中动态调用:def personPush() { println “xxxxx” // 传参到job中 TestJob.schedule(new Date(),[tip: “执行了定时器。。。”])}暂停定时任务def jobManagerServicedef index() { // 停止所有定时任务 jobManagerService.pauseAll() render “Stop all Job."}恢复定时任务jobManagerService.resumeAll()JobManagerService服务中常用方法Map <String , List<JobDescriptor>> getAllJobs() {}List<JobDescriptor> getJobs(String group) {}def getRunningJobs() {}def pauseJob(String group, String name) {}def resumeJob(String group, String name) {}def pauseTrigger(String group, String name) {}def resumeTrigger(String group, String name) {}def pauseTriggerGroup(String group) {}def resumeTriggerGroup(String group) {}def pauseJobGroup(String group) {}def resumeJobGroup(String group) {}def pauseAll() {}def resumeAll() {}def removeJob(String group, String name) {}def unscheduleJob(String group, String name) {}def interruptJob(String group, String name) {}可自行查看grails.plugins.quartz.JobManagerService类常用方法:MyJob.schedule(String cronExpression, Map params) // 创建cron触发器MyJob.schedule(Long repeatInterval, Integer repeatCount, Map params) // 创建简单的触发器:以repeatInterval毫秒的延迟重复作业repeatCount + 1次MyJob.schedule(Date scheduleDate, Map params) // 将一个作业执行安排到特定日期MyJob.schedule(Trigger trigger) // 使用自定义触发器来安排作业的执行MyJob.triggerNow(Map params) // 强制立即执行工作 ...

January 18, 2019 · 1 min · jiezi