分布式定时任务调度框架(文末有源码)
- 前言
- 1、Quartz
- 1.1 数据库
- 1.2 maven依赖
- 1.3 代码实现
- 1.3.1 创建一个job
- 1.3.1 为job设置trigger
- 1.4 配置文件
- 1.5 启动、测试
- 1.1 单机
- 1.2 集群
- 2、ElasticJob
- 2.1 下载zk
- 2.2 新建三个类型的作业
- 2.3 配置文件
- 2.4 启动项目,测试数据
- 2.5 分片作业(集群测试)
- 2.5.1 **官方的三个分片策略:**
- 2.5.2 启动三个实例,模拟集群
- 2.5.3 模拟集群故障
- 2.6 数据流作业
- 2.7 控制台console
- 3、xxl-job
- 1.搭建调度中心(必须要搭)
- 1.1 创建好数据库
- 1.2 修改application.properties文件
- 1.3 修改logback.xml文件
- 1.4 启动项目
- 1.5 打包部署
- 2.springboot项目中集成
- 2.1 maven依赖
- 2.2 配置文件
- 2.3 注入config
- 2.4 新增job
- 2.5 简单归纳一下
- 3.结合控制台使用
- 3.1 配置执行管理器
- 3.2 新增一个任务
- 3.3 启动
- 4.用分片任务,来验证xxl-job的集群功能
- 4.1 修改端口
- 4.2 新建执行器
- 4.3 新建任务代码+任务
- 4.4 运行一次验证
- 5.对Quartz的优化
- 4、结语
- 5、附录1
- 6、附录2
前言
本来是打算水三篇文章的,但是最后想想还是打包一起,这样后来自己看也方便一点,所以本文略长,大家可以按需目录跳转阅读。每个框架都包含了入门的代码示例,文末会贴上源码链接,大家可以下载阅读。
本文主要是为了示例,包含了 单机和集群 的代码,一些基本的概念知识就不说了,大家可以去官网详细阅读。
1、Quartz
最经典的一款框架,下面两个框架都是基于Quartz改进的。本文版本用的最新版本:2.3.0
核心理念
- scheduler:调度器
- job:任务本身
- trigger:触发器
1.1 数据库
Quartz是基于数据库来做任务调度的,所以我们要先把数据库构造好,用官网给的SQL脚本。
由于脚本太长了,放在这里占位置影响阅读,附录1有完整脚本,直接copy
如果大家有源码的,在src\org\quartz\impl\jdbcjobstore这个目录下面,也可以解压maven依赖,一样可以获取到SQL脚本
创建好的就这么11张表
1.2 maven依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.10.0</version></dependency>
我这里引入c3p0是因为会报连接错误,但是网上其他文章不用引入这个依赖,我猜测可能是版本不一样,然后某个Maven依赖冲突导致的。
1.3 代码实现
创建job的方式有两种,一种是比较古老的,用scheduler去绑定job和config,由于配置复杂,不是很推荐,推荐用spring bean的自动装配。
1.3.1 创建一个job
@DisallowConcurrentExecution注解是关键,用于集群中单个job只能被一个实例机器执行。
//禁止并发执行
@DisallowConcurrentExecution
//更新JobDataMap 副本
@PersistJobDataAfterExecution
public class ZedJob extends QuartzJobBean {private static final Logger logger = LoggerFactory.getLogger(ZedJob.class);@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {logger.info("我是ZedJob");}
}
1.3.1 为job设置trigger
@Configuration
public class QuartzConfig {@Beanpublic JobDetail jobDetail(){return JobBuilder.newJob(ZedJob.class)// 指定任务的名称.withIdentity("zedJob")// 任务描述.withDescription("任务描述:这是一个任务")// 每次任务执行后进行存储.storeDurably().build();}@Beanpublic Trigger trigger() {//创建触发器SimpleScheduleBuilder simpleScheduleBuilder1 = SimpleScheduleBuilder.repeatSecondlyForever(5);return TriggerBuilder.newTrigger()// 绑定工作任务.withIdentity("zedJob").forJob(jobDetail())// 每隔 5 秒执行一次 job.withSchedule(simpleScheduleBuilder1).build();}
}
这里的Schedule常用的有两种,一个是SimpleScheduleBuilder,一个是CronScheduleBuilder,写cron表达式的,大家可以按照需要选取。
1.4 配置文件
上面基本都有注释
spring:quartz:#存储方式,默认是内存memoryjob-store: jdbc#应用关闭时,是否等待定时任务执行完成wait-for-jobs-to-complete-on-shutdown: trueproperties:org:quartz:scheduler:#相同 Scheduler 名字的节点,形成一个 Quartz 集群instanceName: SC_SchedulerinstanceId: AUTOjobStore:class: org.quartz.impl.jdbcjobstore.JobStoreTX
# driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegatedataSource: quartz_jobs#是否集群部署isClustered: true# 集群检查周期,单位为毫秒,可以自定义缩短时间。当某一个节点宕机的时候,其他节点等待多久后开始执行任务clusterCheckinInterval: 5000threadPool:threadCount: 25 # 线程池大小。默认为 10 。threadPriority: 5 # 线程优先级class: org.quartz.simpl.SimpleThreadPool # 线程池类型dataSource:#这个名字是上面的jobStore.dataSource定义的quartz_jobs:driver: com.mysql.cj.jdbc.DriverURL: jdbc:mysql://172.16.72.134:3306/quartz_jobs?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8&useUnicode=trueuser: rootpassword: 123456
我这里挑几个比较重要的来说一下:
- job-store:指定存储方式,如果要部署分布式集群,必须要选择jdbc
- instanceName:同一个集群,instanceName要一样,quartz在数据库中是用名字来区分是否是一个集群的。
- isClustered:这是集群的开启开关
- dataSource:选择数据库集群后,才会有这个配置
- quartz_jobs:命名记得一致
1.5 启动、测试
1.1 单机
启动单个服务,看看打印,都是正常的。
1.2 集群
只修改端口,启动两个实例,测试@DisallowConcurrentExecution 并发执行,记得数据库这些配置信息要一致
可以看到,我们第二个实例中,没有任何打印
我们关闭第一个实例,假装宕机,看看第二个实例能否正常接续工作
当关闭1后,我们可以看到,示例2中检测到了1的关闭,并且有打印三句话,这三句话的意思翻译一下:
- 检测到集群中有一个实例失去连接或者在重启
- 扫描是否有其他示例可以接待挂掉的那个主机继续执行任务
- 找到了1个实例,去继续执行
所以由此也可以看出quartz集群的能力和容错能力。
2、ElasticJob
ElasticJob把任务命名为作业,等同于任务,job,叫法不同而已,所以下面都用作业来说
2.1 下载zk
-
去官网下载最新版本:下载链接
-
解压,进入conf 路径下,将文件 zoo_sample.cfg 修改为 zoo.cfg
mv zoo_sample.cfg zoo.cfg
-
在zk根目录下创建目录zkData
mkdir zkData
-
打开 zoo.cfg 文件,修改 dataDir 路径为新创建的zkData路径
dataDir=/usr/local/zookeeper/apache-zookeeper-3.9.2-bin/zkData
-
进入bin目录,启动zk
./zkServer.sh start
-
运行客户端,创建命名空间
./zkCli.sh
create /elastic-job-test
2.2 新建三个类型的作业
ZedJob:普通的作业
FragmentationJob:分片作业
FlowJob:数据流作业
@Component
public class ZedJob implements SimpleJob {private static final Logger logger = LoggerFactory.getLogger(ZedJob.class);@Overridepublic void execute(ShardingContext shardingContext) {logger.info("我是影流之主 Zed");}
}
@Component
public class FragmentationJob implements SimpleJob {private static final Logger logger = LoggerFactory.getLogger(FragmentationJob.class);@Overridepublic void execute(ShardingContext shardingContext) {logger.info("分片任务");switch (shardingContext.getShardingItem()) {case 0:logger.info("分片0:{}",shardingContext.getShardingParameter());break;case 1:logger.info("分片1:{}",shardingContext.getShardingParameter());break;case 2:logger.info("分片2:{}",shardingContext.getShardingParameter());break;default:break;}}
}
@Component
public class FlowJob implements DataflowJob<UserInfo> {private static final Logger logger = LoggerFactory.getLogger(FlowJob.class);@Overridepublic List<UserInfo> fetchData(ShardingContext shardingContext) {List<UserInfo> foos = new ArrayList<>();double random = Math.random();if (random > 0.5) {logger.info("fetchData------ {}", random);UserInfo foo = new UserInfo();foo.setUserName("小道仙");foos.add(foo);}return foos;}@Overridepublic void processData(ShardingContext shardingContext, List<UserInfo> list) {logger.info("收到流数据");list.forEach(i -> logger.info("name为:{}", i.getUserName()));}
}
2.3 配置文件
大多都有注释,大家看看就行,其中shardingTotalCount必填,等待重试的间隔也很重要。
elasticjob:regCenter:#zookeeper 的ip:portserverLists: 172.16.72.133:2181#名命空间,和前面zk里面创建的一样就行namespace: elastic-job-testbase-sleep-time-milliseconds: 10000# 等待重试的间隔时间的最大值,这个很重要,不然项目启动会出现timeout的报错max-sleep-time-milliseconds: 30000jobs:#我们创建的三个任务,在这里进行配置zedJob:#定时任务的全路径名elasticJobClass: com.wq.elasticjob.ZedJob#定时任务执行的cron表达式cron: 0/5 * * * * ?#分片数量shardingTotalCount: 1fragmentationJob:#定时任务的全路径名elasticJobClass: com.wq.elasticjob.FragmentationJob#定时任务执行的cron表达式cron: 0/10 * * * * ?#分片数量shardingTotalCount: 3#这是分片参数,代码中获取,记得加引号,不然获取不到shardingItemParameters: "0=text,1=image,2=video"flowJob:#定时任务的全路径名elasticJobClass: com.wq.elasticjob.FlowJob#定时任务执行的cron表达式cron: 0/10 * * * * ?#分片数量shardingTotalCount: 1
2.4 启动项目,测试数据
我们先看看普通的作业,这个没可说的,看看能否正常运行就好
接下来重点说说分片任务,以及流数据任务。
2.5 分片作业(集群测试)
2.5.1 官方的三个分片策略:
AverageAllocationJobShardingStrategy:根据分片项平均分片
如果分片不能整除,则不能整除的多余分片将依次追加到序号小的服务器。策略举例:
假设有3台服务器,
分成9片,则每台服务器分到的分片是:1【0,1,2】,2【3,4,5】,3【6,7,8】
分成8片,则每台服务器分到的分片是:1【0,1,6】,2【2,3,7】,3【4,5】
分成10片,则每台服务器分到的分片是:1【0,1,2】,2【3,4,5】,3【6,7,8】
OdevitySortByNameJobShardingStrategy:根据作业名称哈希值的奇偶数决定按照作业服务器 IP 升序或是降序的方式分片
缺点是,一旦分片数小于作业服务器数,作业将永远分配至IP地址靠前的服务器,导致IP地址靠后的服务器空闲
RotateServerByNameJobShardingStrategy:根据作业名称轮询分片
假设有3台服务器,顺序为 【0, 1, 2】,如果作业名的哈希值根据作业分片总数取模为 1, 作业节点顺序变为 【1, 2, 0】。
2.5.2 启动三个实例,模拟集群
回顾一下,我们分片任务的配置:shardingItemParameters: “0=text,1=image,2=video”,总数有三个,启动三个实例,刚好一个机器一个,而且参数也能打印出来,而且分片策略,默认采用的是平均分片。
2.5.3 模拟集群故障
现在我把三个集群中index为0的干掉,看看效果。
通过打印可以看到,本来三个集群一人一个,现在挂掉了一个,其中一个服务还是一个,另一个服务则是拥有了两个分片,到这里也就验证结束
2.6 数据流作业
回到我们流数据任务的代码:
@Overridepublic List<UserInfo> fetchData(ShardingContext shardingContext) {List<UserInfo> foos = new ArrayList<>();double random = Math.random();if (random > 0.5) {logger.info("fetchData------ {}", random);UserInfo foo = new UserInfo();foo.setUserName("小道仙");foos.add(foo);}return foos;}@Overridepublic void processData(ShardingContext shardingContext, List<UserInfo> list) {logger.info("收到流数据");list.forEach(i -> logger.info("name为:{}", i.getUserName()));}
我们实现的DataflowJob接口,覆盖了两个方法,一个fetchData,一个processData
- fetchData:对数据作处理
- processData:如果fetchData方法中产生了数据(数据发生了改变),则会携带者数据进入这个方法
我们看看日志打印,当我们在fetchData中设置了userName后,就会进入processData方法,就会拿到我们设置的userName属性
2.7 控制台console
- 选择对应版本下载
控制台官网下载:https://www.apache.org/dyn/closer.cgi/shardingsphere/elasticjob-ui-3.0.0-RC1/apache-shardingsphere-elasticjob-3.0.0-RC1-lite-ui-bin.tar.gz - 解压后进入bin目录,选择start.bat双击启动
- 网页访问localhost:8088/,用户名/密码:root/root
- 按照下图三个步骤配置zk注册中心,然后就能使用了
3、xxl-job
截止本文编写时候,xxl的最新Maven版本为2.4.1,所以我们就用最新的
我们把xxl-job看做两部分,一部分是搭建调度中心,一部分是代码中实现集成
1.搭建调度中心(必须要搭)
下载源码,仓库地址:
gitee地址:https://gitee.com/xuxueli0323/xxl-job/blob/master/doc/db/tables_xxl_job.sql
GitHub地址:https://github.com/xuxueli/xxl-job
1.1 创建好数据库
脚本在这个目录下面:doc/db/tables_xxl_job.sql
调度中心支持集群部署,集群情况下各节点务必连接同一个mysql实例;
如果mysql做主从,调度中心集群节点务必强制走主库;
这里给出所有数据库的作用:
- xxl_job_lock:任务调度锁表;
- xxl_job_group:执行器信息表,维护任务执行器信息;
- xxl_job_info:调度扩展信息表: 用于保存XXL-JOB调度任务的扩展信息,如任务分组、任务名、机器地址、执行器、执行入参和报警邮件等等;
- xxl_job_log:调度日志表: 用于保存XXL-JOB任务调度的历史信息,如调度结果、执行结果、调度入参、调度机器和执行器等等;
- xxl_job_log_report:调度日志报表:用户存储XXL-JOB任务调度日志的报表,调度中心报表功能页面会用到;
- xxl_job_logglue:任务GLUE日志:用于保存GLUE更新历史,用于支持GLUE的版本回溯功能;
- xxl_job_registry:执行器注册表,维护在线的执行器和调度中心机器地址信息;
- xxl_job_user:系统用户表;
不想去找的直接文末附录二有完整的SQL脚本,直接copy
1.2 修改application.properties文件
修改server.port为自己需要的
修改spring.datasource.url,数据库配置为自己的
修改spring.mail.username和from为自己的实际邮箱地址(不修改也可以,这个就是通知用的)
1.3 修改logback.xml文件
将name=“log.path” 这个路径修改为自己的路径,并且在路径下创建xxl-job-admin.log文件,如果你不修改用默认的也行。
1.4 启动项目
浏览器访问:localhost:8088/xxl-job-admin/,默认用户名密码为admin,123456
出现下面这个界面则搭建成功
1.5 打包部署
生产环境下,配置好以后,打包部署到服务器即可
2.springboot项目中集成
2.1 maven依赖
<dependency><groupId>com.xuxueli</groupId><artifactId>xxl-job-core</artifactId><version>2.4.1</version>
</dependency>
2.2 配置文件
xxl:job:admin:#如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册addresses: http://127.0.0.1:8088/xxl-job-adminexecutor:appname: zed-demologpath:logretentiondays: 30#执行器IP:默认为空表示自动获取IPip: 127.0.0.1#默认为9999port: 9999#优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址address:#执行器通讯TOKEN [选填]:非空时启用; 要和调度中心服务部署配置的accessToken一致accessToken: default_token
2.3 注入config
这个config是官方源码,内容一样,但是必须要copy进我们自己的项目里
@Configuration
public class XxlJobConfig {private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);@Value("${xxl.job.admin.addresses}")private String adminAddresses;@Value("${xxl.job.accessToken}")private String accessToken;@Value("${xxl.job.executor.appname}")private String appname;@Value("${xxl.job.executor.address}")private String address;@Value("${xxl.job.executor.ip}")private String ip;@Value("${xxl.job.executor.port}")private int port;@Value("${xxl.job.executor.logpath}")private String logPath;@Value("${xxl.job.executor.logretentiondays}")private int logRetentionDays;@Beanpublic XxlJobSpringExecutor xxlJobExecutor() {logger.info(">>>>>>>>>>> xxl-job config init.");XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();xxlJobSpringExecutor.setAdminAddresses(adminAddresses);xxlJobSpringExecutor.setAppname(appname);xxlJobSpringExecutor.setAddress(address);xxlJobSpringExecutor.setIp(ip);xxlJobSpringExecutor.setPort(port);xxlJobSpringExecutor.setAccessToken(accessToken);xxlJobSpringExecutor.setLogPath(logPath);xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);return xxlJobSpringExecutor;}/*** 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;** 1、引入依赖:* <dependency>* <groupId>org.springframework.cloud</groupId>* <artifactId>spring-cloud-commons</artifactId>* <version>${version}</version>* </dependency>** 2、配置文件,或者容器启动变量* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'** 3、获取IP* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();*/}
2.4 新增job
这里用最常用最推荐的一种写法,就是将任务绑定在方法上,用@XxlJob注解标明每一个任务。当然还有绑定在Bean上的,不过这样就会写很多Bean,用方法的话,一个类就够了。
@Component
public class XxlJobFactory {private static Logger logger = LoggerFactory.getLogger(XxlJobFactory.class);@XxlJob("zedJobTest")public void zedJobTest(){logger.info("这是zedJobTest");}
}
2.5 简单归纳一下
我们在springBoot项目集成的时候,有几个比较关键的点,我在这里给大家总结一下,免得绕进去。
- 第一步搭建的调度中心,你可以理解为es job 的zookeeper一样,或者nacos这样的角色,它是一个注册中心,是整个框架的驱动核心,采用db的方式来进行服务的注册。
- xxl.job.admin:这个在springboot中的配置,就是配置第一步的调度中心的,地址前面一定要加http
- xxl.job.executor:这个是接下来要说的执行管理器,去执行任务的executor,它配置在我们的网页控制台中。这个的ip和端口是单独分开的,专门控制executor
- 很多配置都是空的,因为都有默认的,但是我们在注入XxlJobConfig的时候必须要用,所以空着就空着
3.结合控制台使用
3.1 配置执行管理器
新增一个执行管理器,这就是我们前面说到的,xxl.job.executor配置所控制的东西,它有一个默认的,我们不用默认的用自己的。
这个appName一定要和xxl.job.executor里面的appname一样,一般默认自动注册就好,如果自动注册不行,就手动录入,输入我们的ip和端口,这个ip和端口,也一定要和xxl.job.executor.port一致
3.2 新增一个任务
这里面最主要注意三个点,就是我标出来的这三个
- 执行器选择我们第二步中创建的执行器
- 运行模式常用Bean
- 然后jobHander要和代码中,注解@XxlJob中的Value一致
其他的根据自己需求改写就行
3.3 启动
这里选择执行一次,忽略corn表达式,只执行一次,选择下面的启动,才会走corn表达式逻辑。
执行一次这里,可以加任务参数和重新指定机器地址,由于我们执行器里面配置好了的,所以不用填写让它自动获取。
看看控制台打印,任务是调动成功了的。
然后数据库里,也可以看到我们新增的任务,还有其他表的信息
4.用分片任务,来验证xxl-job的集群功能
4.1 修改端口
修改springboot的端口和executor的port,启动三个实例
4.2 新建执行器
新建一个执行器fragmentatio
这里自动注册如果获取不到,我们就手动录入,三个不同的端口之间用逗号分隔。外面查看也是正确的。
4.3 新建任务代码+任务
/*** 2、分片广播任务*/@XxlJob("shardingJobHandler")public void shardingJobHandler(String param) throws Exception {int shardTotal = XxlJobHelper.getShardTotal();int index = XxlJobHelper.getShardIndex();logger.info("总共:{}个分片,当前的index为:{}", shardTotal, index);}
记得路由策略选择分片广播
4.4 运行一次验证
我们看看控制台打印,我这里贴一个就行了,三个实例,总共3个分片,index为0,1,2,大家可以根据分片总数和index,自己去实现分片处理逻辑,比如取模。
5.对Quartz的优化
xxl-job,是徐雪里基于Quartz改进来的,Quartz有下面几点不足:
- 调用API的的方式操作任务,不人性化;
- 需要持久化业务QuartzJobBean到底层数据表中,系统侵入性相当严重。
- 调度逻辑和QuartzJobBean耦合在同一个项目中,这将导致一个问题,在调度任务数量逐渐增多,同时调度任务逻辑逐渐加重的情况下,此时调度系统的性能将大大受限于业务;
- quartz底层以“抢占式”获取DB锁并由抢占成功节点负责运行任务,会导致节点负载悬殊非常大;而XXL-JOB通过执行器实现“协同分配式”运行任务,充分发挥集群优势,负载各节点均衡。
XXL-JOB弥补了quartz的上述不足之处
4、结语
都看到这里了,给我点个赞和关注不过分吧,如果你只是使用分布式任务调度框架,本文的示例绝对够用,如果你们项目很庞大而且很复杂,那么还要去官网查看更详细的配置。
本文完整项目代码GitHub地址
https:https://github.com/wangqing-github/DubboAndNacos.git
ssh:git@github.com:wangqing-github/DubboAndNacos.git
有三个分支,代码都是独立的,大家可以从主干切换。
5、附录1
Quartz的数据库SQL脚本:
#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;CREATE TABLE QRTZ_JOB_DETAILS(SCHED_NAME VARCHAR(120) NOT NULL,JOB_NAME VARCHAR(200) NOT NULL,JOB_GROUP VARCHAR(200) NOT NULL,DESCRIPTION VARCHAR(250) NULL,JOB_CLASS_NAME VARCHAR(250) NOT NULL,IS_DURABLE VARCHAR(1) NOT NULL,IS_NONCONCURRENT VARCHAR(1) NOT NULL,IS_UPDATE_DATA VARCHAR(1) NOT NULL,REQUESTS_RECOVERY VARCHAR(1) NOT NULL,JOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);CREATE TABLE QRTZ_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,JOB_NAME VARCHAR(200) NOT NULL,JOB_GROUP VARCHAR(200) NOT NULL,DESCRIPTION VARCHAR(250) NULL,NEXT_FIRE_TIME BIGINT(13) NULL,PREV_FIRE_TIME BIGINT(13) NULL,PRIORITY INTEGER NULL,TRIGGER_STATE VARCHAR(16) NOT NULL,TRIGGER_TYPE VARCHAR(8) NOT NULL,START_TIME BIGINT(13) NOT NULL,END_TIME BIGINT(13) NULL,CALENDAR_NAME VARCHAR(200) NULL,MISFIRE_INSTR SMALLINT(2) NULL,JOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);CREATE TABLE QRTZ_SIMPLE_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,REPEAT_COUNT BIGINT(7) NOT NULL,REPEAT_INTERVAL BIGINT(12) NOT NULL,TIMES_TRIGGERED BIGINT(10) NOT NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_CRON_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,CRON_EXPRESSION VARCHAR(200) NOT NULL,TIME_ZONE_ID VARCHAR(80),PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_SIMPROP_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,STR_PROP_1 VARCHAR(512) NULL,STR_PROP_2 VARCHAR(512) NULL,STR_PROP_3 VARCHAR(512) NULL,INT_PROP_1 INT NULL,INT_PROP_2 INT NULL,LONG_PROP_1 BIGINT NULL,LONG_PROP_2 BIGINT NULL,DEC_PROP_1 NUMERIC(13,4) NULL,DEC_PROP_2 NUMERIC(13,4) NULL,BOOL_PROP_1 VARCHAR(1) NULL,BOOL_PROP_2 VARCHAR(1) NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_BLOB_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,BLOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_CALENDARS(SCHED_NAME VARCHAR(120) NOT NULL,CALENDAR_NAME VARCHAR(200) NOT NULL,CALENDAR BLOB NOT NULL,PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_FIRED_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,ENTRY_ID VARCHAR(95) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,INSTANCE_NAME VARCHAR(200) NOT NULL,FIRED_TIME BIGINT(13) NOT NULL,SCHED_TIME BIGINT(13) NOT NULL,PRIORITY INTEGER NOT NULL,STATE VARCHAR(16) NOT NULL,JOB_NAME VARCHAR(200) NULL,JOB_GROUP VARCHAR(200) NULL,IS_NONCONCURRENT VARCHAR(1) NULL,REQUESTS_RECOVERY VARCHAR(1) NULL,PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);CREATE TABLE QRTZ_SCHEDULER_STATE(SCHED_NAME VARCHAR(120) NOT NULL,INSTANCE_NAME VARCHAR(200) NOT NULL,LAST_CHECKIN_TIME BIGINT(13) NOT NULL,CHECKIN_INTERVAL BIGINT(13) NOT NULL,PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);CREATE TABLE QRTZ_LOCKS(SCHED_NAME VARCHAR(120) NOT NULL,LOCK_NAME VARCHAR(40) NOT NULL,PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);commit;
6、附录2
xxl的sql脚本
CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci;
use `xxl_job`;SET NAMES utf8mb4;CREATE TABLE `xxl_job_info` (`id` int(11) NOT NULL AUTO_INCREMENT,`job_group` int(11) NOT NULL COMMENT '执行器主键ID',`job_desc` varchar(255) NOT NULL,`add_time` datetime DEFAULT NULL,`update_time` datetime DEFAULT NULL,`author` varchar(64) DEFAULT NULL COMMENT '作者',`alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',`schedule_type` varchar(50) NOT NULL DEFAULT 'NONE' COMMENT '调度类型',`schedule_conf` varchar(128) DEFAULT NULL COMMENT '调度配置,值含义取决于调度类型',`misfire_strategy` varchar(50) NOT NULL DEFAULT 'DO_NOTHING' COMMENT '调度过期策略',`executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略',`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',`executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略',`executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间,单位秒',`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',`glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型',`glue_source` mediumtext COMMENT 'GLUE源代码',`glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',`glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间',`child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔',`trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态:0-停止,1-运行',`trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间',`trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `xxl_job_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`job_group` int(11) NOT NULL COMMENT '执行器主键ID',`job_id` int(11) NOT NULL COMMENT '任务,主键ID',`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址',`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',`executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数,格式如 1/2',`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',`trigger_code` int(11) NOT NULL COMMENT '调度-结果',`trigger_msg` text COMMENT '调度-日志',`handle_time` datetime DEFAULT NULL COMMENT '执行-时间',`handle_code` int(11) NOT NULL COMMENT '执行-状态',`handle_msg` text COMMENT '执行-日志',`alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态:0-默认、1-无需告警、2-告警成功、3-告警失败',PRIMARY KEY (`id`),KEY `I_trigger_time` (`trigger_time`),KEY `I_handle_code` (`handle_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `xxl_job_log_report` (`id` int(11) NOT NULL AUTO_INCREMENT,`trigger_day` datetime DEFAULT NULL COMMENT '调度-时间',`running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量',`suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量',`fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量',`update_time` datetime DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `xxl_job_logglue` (`id` int(11) NOT NULL AUTO_INCREMENT,`job_id` int(11) NOT NULL COMMENT '任务,主键ID',`glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型',`glue_source` mediumtext COMMENT 'GLUE源代码',`glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注',`add_time` datetime DEFAULT NULL,`update_time` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `xxl_job_registry` (`id` int(11) NOT NULL AUTO_INCREMENT,`registry_group` varchar(50) NOT NULL,`registry_key` varchar(255) NOT NULL,`registry_value` varchar(255) NOT NULL,`update_time` datetime DEFAULT NULL,PRIMARY KEY (`id`),KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `xxl_job_group` (`id` int(11) NOT NULL AUTO_INCREMENT,`app_name` varchar(64) NOT NULL COMMENT '执行器AppName',`title` varchar(12) NOT NULL COMMENT '执行器名称',`address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入',`address_list` text COMMENT '执行器地址列表,多地址逗号分隔',`update_time` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `xxl_job_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(50) NOT NULL COMMENT '账号',`password` varchar(50) NOT NULL COMMENT '密码',`role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员',`permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割',PRIMARY KEY (`id`),UNIQUE KEY `i_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `xxl_job_lock` (`lock_name` varchar(50) NOT NULL COMMENT '锁名称',PRIMARY KEY (`lock_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;INSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`, `update_time`) VALUES (1, 'xxl-job-executor-sample', '示例执行器', 0, NULL, '2018-11-03 22:21:31' );
INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `schedule_type`, `schedule_conf`, `misfire_strategy`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'CRON', '0 0 0 * * ? *', 'DO_NOTHING', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', '');
INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL);
INSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock');commit;