一。定时任务概述1。定时任务认识1。1。什么是定时任务 定时任务是按照指定时间周期运行任务。使用场景为在某个固定时间点执行,或者周期性的去执行某个任务,比如:每天晚上24点做数据汇总,定时发送短信等。1。2。常见定时任务方案WhileSleep:通过循环加休眠的方式定时执行Timer和TimerTask实现:JDK自带的定时任务,可以实现简单的间隔执行任务(在指定时间点执行某一任务,也能定时的周期性执行),无法实现按日历去调度执行任务。ScheduledExecutorService:Java并发包下,JDK1。5出现,是比较理想的定时任务实现方案。Eureka就使用的是它QuartZ:使用Quartz,它是一个异步任务调度框架,功能丰富,可以实现按日历调度,支持持久化。使用SpringTask,Spring3。0后提供SpringTask实现任务调度,支持按日历调度,相比Quartz功能稍简单,但是在开发基本够用,支持注解编程方式。SpringBoot中的Schedule:通过EnableSchedulingScheduled最实现定时任务,底层使用的是SpringTask2。分布式定时任务2。1。遇到什么问题 上述的定时任务都是集中式(单体项目使用)的定时任务,在分布式中将会面临一些问题或不足业务量大,单机性能瓶颈需要扩展多台机器部署如何保证定时任务不重复执行定时任务时间需要可调整,可以暂停机器发生故障down机,定时任务依然可用,如何实现故障转移定时任务,执行日志是否可监控2。2。分布式定时任务xxljob XXLJOB是一个分布式任务调度平台,于2015问世,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。其具备且不止如下能力简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手;动态:支持动态修改任务状态、启动停止任务,以及终止运行中任务,即时生效;调度中心HA(中心式):调度采用中心式设计,调度中心基于集群Quartz实现并支持集群部署,可保证调度中心HA;执行器HA(分布式):任务分布式执行,任务执行器支持集群部署,可保证任务执行HA;弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配任务;路由策略:执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;故障转移:任务路由策略选择故障转移情况下,如果执行器集群中某一台机器故障,将会自动Failover切换到一台正常的执行器发送调度请求。任务失败告警:默认提供邮件方式失败告警,同时预留扩展接口,可方面的扩展短信、钉钉等告警方式; 具体见:https:github。comxuxuelixxljobtreev2。0。0二。XXLJOB初体验1。xxljob架构设计1。1。设计思想 将调度行为抽象形成调度中心公共平台,而平台自身并不承担业务逻辑,调度中心负责发起调度请求。 将任务抽象成分散的JobHandler,交由执行器统一管理,执行器负责接收调度请求并执行对应的JobHandler中业务逻辑。因此,调度和任务两部分可以相互解耦,提高系统整体稳定性和扩展性;1。2。架构设计图 xxljob分为调度中心和执行器两大模块调度模块(调度中心) 负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块; 支持可视化、简单且动态的管理调度信息,包括任务新建,更新,删除,GLUE开发和任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器Failover(故障转移)。执行模块(执行器) 负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效; 接收调度中心的执行请求、终止请求和日志请求等。 调度中心高可用 基于数据库的集群方案,数据库选用M集群分布式并发环境中进行定时任务调度时,会在各个节点会上报任务,存到数据库中,执行时会从数据库中取出触发器来执行,如果触发器的名称和执行时间相同,则只有一个节点去执行此任务。并行调度 调度采用线程池方式实现,避免单线程因阻塞而引起任务调度延迟。XXLJOB调度模块默认采用并行机制,在多线程调度的情况下,调度模块被阻塞的几率很低,大大提高了调度系统的承载量。 XXLJOB的不同任务之间并行调度、并行执行。XXLJOB的单个任务,针对多个执行器是并行运行的,针对单个执行器是串行执行的。同时支持任务终止。执行器(任务)高可用 执行器如若集群部署,调度中心将会感知到在线的所有执行器,如127。0。0。1:9997,127。0。0。1:9998,127。0。0。1:9999。多个执行器可以选择路由策略来采用轮询,随机等方式进行多机器调度。 当任务路由策略选择故障转移(FAILOVER)时,当调度中心每次发起调度请求时,会按照顺序对执行器发出心跳检测请求,第一个检测为存活状态的执行器将会被选定并发送调度请求。调度成功后,可在日志监控界面查看调度备注2。xxljob安装2。1。下载源码 请下载项目源码并解压,使用IDEA工具导入项目 源码仓库地址https:github。comxuxuelixxljobhttp:gitee。comxuxueli0323xxljob 项目代码结构如下 doc:文档,即SQL脚本所在目录db:调度数据库建表脚本xxljobadmin:调度中心项目源码xxljobcore:核心模块,公共Jar依赖xxljobexecutorsamples:执行器,Sample示例项目(大家可以在该项目上进行开发,也可以将现有项目改造生成执行器项目)2。2。导入数据库 打开项目代码,获取调度数据库初始化SQL脚本并执行即可。调度数据库初始化SQL脚本位置为:xxljobdocdbtablesxxljob。sql,数据库名:xxljob 数据库如下 xxljoblock:任务调度锁表;xxljobgroup:执行器信息表,维护任务执行器信息;xxljobinfo:调度扩展信息表:用于保存XXLJOB调度任务的扩展信息,如任务分组、任务名、机器地址、执行器、执行入参和报警邮件等等;xxljoblog:调度日志表:用于保存XXLJOB任务调度的历史信息,如调度结果、执行结果、调度入参、调度机器和执行器等等;xxljoblogreport:调度日志报表:用户存储XXLJOB任务调度日志的报表,调度中心报表功能页面会用到;xxljoblogglue:任务GLUE日志:用于保存GLUE更新历史,用于支持GLUE的版本回溯功能;xxljobregistry:执行器注册表,维护在线的执行器和调度中心机器地址信息;xxljobuser:系统用户表;2。3。启动调度中心 打开xxljobadmin的配置文件,xxljobxxljobadminsrcmainresourcesapplication。properties对调度中心进行配置,重要配置如下server。port:根据情况修改端口spring。datasource。url:指向刚才准备的数据库spring。datasource。password:记得修改成自己的数据库密码spring。mail。username:配置自己的邮件账号spring。mail。password:邮件的授权码,我下面是以qq邮箱为例 下面根据自己的情况进行修改,不要直接复制调度中心JDBC链接:链接地址请保持和2。1章节所创建的调度数据库的地址一致spring。datasource。urljdbc:mysql:127。0。0。1:3306xxljob?useUnicodetruecharacterEncodingUTF8autoReconnecttrueserverTimezoneAsiaShanghaispring。datasource。usernamerootspring。datasource。passwordrootpwdspring。datasource。driverclassnamecom。mysql。jdbc。Driver报警邮箱spring。mail。hostsmtp。qq。comspring。mail。port25spring。mail。usernamexxxqq。comspring。mail。password邮箱授权码,不是登录密码spring。mail。properties。mail。smtp。authtruespring。mail。properties。mail。smtp。starttls。enabletruespring。mail。properties。mail。smtp。starttls。requiredtruespring。mail。properties。mail。smtp。socketFactory。classjavax。net。ssl。SSLSocketFactory调度中心通讯TOKEN〔选填〕:非空时启用;xxl。job。accessToken调度中心国际化配置〔必填〕:默认为zhCN中文简体,可选范围为zhCN中文简体,zhTC中文繁体anden英文;xxl。job。i18nzhCN调度线程池最大线程配置【必填】xxl。job。triggerpool。fast。max200xxl。job。triggerpool。slow。max100调度中心ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a表数据保存天数〔必填〕:过期ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a自动清理;限制大于等于7时生效,否则,如1,关闭自动清理功能;xxl。job。logretentiondays30 然后启动调度中心,执行XxlJobAdminApplicationmain方法,启动之后,浏览器访问http:localhost:18080xxljobadminjobinfo?jobGroup2;注意URL中有个上下文路径。默认登录账号admin123456,登录后运行界面如下图所示。 2。3。配置部署执行器项目 执行器项目:xxljobexecutorsamplespringboot(提供多种版本执行器供选择,现以springboot版本为例,可直接使用,也可以参考其并将现有项目改造成执行器) 作用:负责接收调度中心的调度并执行;可直接部署执行器,也可以将执行器集成到现有业务项目中。 修改配置:xxljobxxljobexecutorsamplesxxljobexecutorsamplespringbootsrcmainresourcesapplication。propertiesxxl。job。admin。addresses:调度中心的地址,如果调度中心修改过端口,这里也要对应修改调度中心部署跟地址〔选填〕:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行执行器心跳注册和任务结果回调;为空则关闭自动注册;xxl。job。admin。addresseshttp:127。0。0。1:18080xxljobadmin执行器通讯TOKEN〔选填〕:非空时启用;xxl。job。accessToken执行器AppName〔选填〕:执行器心跳注册分组依据;为空则关闭自动注册xxl。job。executor。appnamexxljobexecutorsample执行器注册〔选填〕:优先使用该配置作为注册地址,为空时使用内嵌服务IP:PORT作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。xxl。job。executor。address执行器IP〔选填〕:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于执行器注册和调度中心请求并触发任务;xxl。job。executor。ip执行器端口号〔选填〕:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;xxl。job。executor。port9999执行器运行ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a文件存储磁盘路径〔选填〕:需要对该路径拥有读写权限;为空则使用默认路径;xxl。job。executor。logpathdataapplogsxxljobjobhandler执行器ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a文件保存天数〔选填〕:过期ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a自动清理,限制值大于等于3时生效;否则,如1,关闭自动清理功能;xxl。job。executor。logretentiondays30 上面配置是为了在Spring容器中创建一个XxlJobSpringExecutor执行器Bean,见:com。xxl。job。executor。core。config。XxlJobConfigxxlJobExecutorBeanpublicXxlJobSpringExecutorxxlJobExecutor(){logger。info(xxljobconfiginit。);XxlJobSpringExecutorxxlJobSpringExecutornewXxlJobSpringExecutor();xxlJobSpringExecutor。setAdminAddresses(adminAddresses);xxlJobSpringExecutor。setAppname(appname);xxlJobSpringExecutor。setAddress(address);xxlJobSpringExecutor。setIp(ip);xxlJobSpringExecutor。setPort(port);xxlJobSpringExecutor。setAccessToken(accessToken);xxlJobSpringExecutor。setLogPath(logPath);xxlJobSpringExecutor。setLogRetentionDays(logRetentionDays);returnxxlJobSpringE} 在com。xxl。job。executor。service。jobhandler。SampleXxlJob中提供了简单的定时任务实例 为方便用户参考与快速实用,示例执行器内原生提供多个Bean模式任务Handler,可以直接配置实用,如下:demoJobHandler:简单示例任务,任务内部模拟耗时任务逻辑,用户可在线体验RollingLog等功能;shardingJobHandler:分片示例任务,任务内部模拟处理分片参数,可参考熟悉分片任务;httpJobHandler:通用HTTP任务H业务方只需要提供HTTP链接等信息即可,不限制语言、平台。示例任务入参如下:XxlJob开发示例(Bean模式)开发步骤:1、在SpringBean实例中,开发Job方法,方式格式要求为publicReturnTStringexecute(Stringparam)2、为Job方法添加注解XxlJob(value自定义jobhandler名称,initJobHandler初始化方法,destroyJobHandler销毁方法),注解value值对应的是调度中心新建任务的JobHandler属性的值。3、执行ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a:需要通过XxlJobLogger。log打印执行ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a;authorxuxueli2019121121:52:51ComponentpublicclassSampleXxlJob{privatestaticLoggerloggerLoggerFactory。getLogger(SampleXxlJob。class);1、简单任务示例(Bean模式)XxlJob(demoJobHandler)publicReturnTStringdemoJobHandler(Stringparam)throwsException{logger。info(XXLJOB,HelloWorld。param{},param);returnReturnT。SUCCESS;}。。。省略。。。} 【重要】如果我们要写自己的定时任务,参照上面方法,在方法上注解一个XxlJob(任务名字),方法可以接受一个字符串参数,方法需要返回ReturnT格式。 最后启动执行器项目。3。配置定时任务3。1。执行器创建 打开调度中心可视化界面,在执行器管理界面,添加新增执行器 appName:执行器的名字,可以任意填写名称:任意填写注册方式:调度中心是通过RPC的方式对执行器发起调度,所以这里需要的是执行器项目的ip:port,注意,该端口不是执行器项目的server。port,而是:xxl。job。executor。port端口。你可以选择自动注册,也可以手动录入。3。2。创建任务 在任务管理界面,新增任务 路由策略:有轮询,随机,故障转移等等策略,是用在集群模式下的调度方式。cron:定时任务的执行时间规则,时间表达式JobHandler:这个是要对应执行器项目中XxlJob(demoJobHandler)注解中的名字运行模式:Bean,使用内置代码方式,也可以执行在线执行代码方式报警邮件:如果定时任务失败,会发送报警邮件到邮箱任务参数:这个参数可以传递给XxlJob(demoJobHandler)所在方法的参数。 创建好任务之后就可以执行了 调度日志 IDEA工具控制台效果15:47:33。017logback〔Thread16〕INFOc。x。j。e。s。jobhandler。SampleXxlJobXXLJOB,HelloWorld。param15:47:34。007logback〔Thread16〕INFOc。x。j。e。s。jobhandler。SampleXxlJobXXLJOB,HelloWorld。param15:47:35。008logback〔Thread16〕INFOc。x。j。e。s。jobhandler。SampleXxlJobXXLJOB,HelloWorld。param。。。省略。。。4。GLUE模式(Java)4。1。添加任务 该模式支持在线编辑定时任务的内容,立刻执行,无需再开发工具中编辑代码,也无需重启项目。 请点击任务右侧GLUE按钮,进入GLUE编辑器开发界面,见下图。GLUE模式(Java)运行模式的任务默认已经初始化了示例任务代码,即打印HelloWorld。 任务以源码方式维护在调度中心,支持通过WebIDE在线更新,实时编译和生效,因此不需要指定JobHandler4。2。编写代码 保存之后可以在操作按钮里面去编写任务 (GLUE模式(Java)运行模式的任务实际上是一段继承自IJobHandler的Java类代码,它在执行器项目中运行,可使用ResourceAutowire注入执行器里中的其他服务),比如我的定时任务如下,编辑好之后点击保存 保存好之后,启动定时任务,效果如下 三。XXLJOB集群部署1。调度中心集群1。1。问题概述 调度中心支持集群部署,提升调度系统容灾和可用性。调度中心集群部署时,几点要求和建议:DB配置保持一致;集群机器时钟保持一致(单机集群忽视);当启动多个调度器时,执行器配置调度中心部署跟地址可以用逗号分隔。执行器将会使用该地址进行执行器心跳注册和任务结果回调;为空则关闭自动注册; 但是建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。1。2。启动多个调度中心 修改调度中心端口,启动多个调度中心,我这里启动两个如http:localhost:18080xxljobadminhttp:localhost:18081xxljobadmin1。3。配置Nginx负载均衡 当启动多个调度器时,执行器配置调度中心部署跟地址可以用逗号分隔。执行器将会使用该地址进行执行器心跳注册和任务结果回调;为空则关闭自动注册; 但是建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。 我们启动了2个调度中心,那么我的执行器项目该注册到哪个调度中心呢?我们通过Nginx来解决这个问题,原理如下图: 我们再hosts配置www。jobs。com作为nginx的主机域名,然后反向代理到多个调度中心,这样一来执行器就只需要注册到www。jobs。comNginx即可。 修改C:WindowsSystem32driversetchosts增加配置如下127。0。0。1www。jobs。com Nginx配置如下调度中心upstreamjobs{serverlocalhost:18080;serverlocalhost:18081;}server{listen80;使用域名servernamewww。jobs。charsetkoi8r;accessloglogshost。access。location{调度中心反向代理配置proxypasshttp:}errorpage404404。redirectservererrorpagestothestaticpage50x。htmlerrorpage50050250350450x。location50x。html{}} 启动Nginx,通过浏览器访问http:www。jobs。comxxljobadmin,可以访问到调度中心的管理界面2。执行器项目集群 执行器支持集群部署,提升调度系统可用性,同时提升任务处理能力。 执行器集群部署时,几点要求和建议:执行器回调地址(xxl。job。admin。addresses)需要保持一致;执行器根据该配置进行执行器自动注册等操作。同一个执行器集群内AppName(xxl。job。executor。appname)需要保持一致;调度中心根据该配置动态发现不同集群的在线执行器列表。2。1。启动多个执行器项目 现在对执行器项目做集群,修改xxljobexecutorsamplespringboot配置文件application。propertiesserver。port:既然是做集群,项目端口需要修改xxl。job。admin。addresses:调度中心地址需要修改成www。jobs。com,多个执行器配置同一个地址。xxl。job。executor。port:RPC通信端口也要修改,多个执行器该端口需要不一样 第一个实例配置server。port19090xxl。job。admin。addresseshttp:www。jobs。comxxljobadmin对应Nginx地址xxl。job。executor。port9999 第二个实例配置server。port19091xxl。job。admin。addresseshttp:www。jobs。comxxljobadminxxl。job。executor。port9998 在Configurations中配置,允许启动多个实例 启动实例如下 2。2。配置定时任务 通过http:www。jobs。comxxljobadmin访问调度中心管理界面,在执行器管理中可以看到多台执行器实例 在任务管理中,可以编辑任务,然后选择路由策略,比如:选择轮询,然后启动任务,就会看到两个执行器项目轮着执行定时任务。 说在最后 xxljob确实很强大,功能也很全,经过该文章学习相信你可以把xxljob给用起来了,但是如果你的项目是一个小体量的单体,我不太建议使用它,Quzrtz或者SpringBootTask就足够,对于xxljob个人还是有些笨重。