官方文档 https://www.xuxueli.com/xxl-job/
参考文章:芋道 Spring Boot 定时任务入门
定时任务背景 定时任务需求几个非常常见的业务场景:
- 某系统凌晨要进行数据备份。
- 某电商平台,用户下单半个小时未支付的情况下需要自动取消订单。
- 某媒体聚合平台,每 10 分钟动态抓取某某网站的数据为自己所用。
- 某博客平台,支持定时发送文章。
- 某基金平台,每晚定时计算用户当日收益情况并推送给用户最新的数据。
- …
这些场景往往都要求我们在某个特定的时间去做某个事情
Java传统定时任务方案–单机定时任务- java.util.Timer ScheduledExecutorService (都无法使用 Cron 表达式指定任务执行的具体时间)
- Spring TaskSpringBoot注解@Scheduled(Spring 自带的定时调度只支持单机,并且提供的功能比较单一)
- 时间轮。时间轮是一个环形的队列(底层一般基于数组实现),队列中的每一个元素(时间格)都可以存放一个定时任务列表。
分布式集群的模式下,如果采用集中式的任务调度方式,会带来一些问题,比如
- 集群部署的定时任务如何保证调度(比如重复执行降低效率)?
- 如何动态地调整定时任务的执行时间? (不重启服务实现)
- 如何实现故障转移?
- 如何对定时任务进行监控?
- 业务量比较大,单机性能如何扩展?
由于集中式的定时任务调度需要解决一系列问题 ,所以在演进的过程中产生一些解决办法。
- 数据库唯一约束,避免定时任务重复执行;
- 使用配置文件、redis、 mysql作为调度的开关;
- 使用分布式锁实现调度的控制;
- 使用分布式任务调度平台(大公司自研或开源项目)TBSchedule、ScheduleX、Elastric-Job、Saturn、Google Cron、XXL-JOB 系统;
调度中心:服务端:是一个web管理后台
执行器:客户端:就是我们希望自动执行的代码,业务逻辑
自研的调度模块,通过RPC远程调用
调度和执行之间的解耦合- 将调度行为抽象形成“调度中心”公共平台,而平台自身并不承担业务逻辑,“调度中心”负责发起调度请求。
- 将任务抽象成分散的 JobHandler ,交由“执行器”统一管理,“执行器”负责接收调度请求并执行对应的 JobHandler中 业务逻辑。
因此,“调度”和“执行”两部分可以相互解耦,提高系统整体稳定性和扩展性。
XXL-Job 是中心化的任务调度平台从调度系统的角度来看,可以分成两类:
- 中心化: 调度中心和执行器分离,调度中心统一调度,通知某个执行器处理任务。
- 去中心化:调度中心和执行器一体化,自己调度自己执行处理任务。
执行器管理是调度中心连接的多个执行器的名称、地址等进行管理。
任务管理是对xx执行器进行任务的具体设置,比如负责人、任务描述、超时时间、失败次数等等
任务管理设置详解 具体可参考官方文档 路由策略:在执行器集群部署时,使用不同的路由策略,比如:
FIRST(第一个):固定选择第一个机器;
RANDOM(随机):随机选择在线的机器;
ROUND(轮询)等等
调度类型:- 无:该类型不会主动触发调度
- CRON:该类型将会通过CRON,触发任务调度;
- 固定速度:该类型将会以固定速度,触发任务调度;按照固定的间隔时间,周期性触发;
- 固定延迟:该类型将会以固定延迟,触发任务调度;按照固定的延迟时间,从上次调度结束后开始计算延迟时间,到达延迟时间后触发下次调度;
Cron表达式是一个具有时间含义的字符串,字符串以56个空格隔开,分为67个域,格式为X X X X X X X。其中X是一个域的占位符。最后一个代表年份的域非必须,可省略。
具体设置方式参考:https://help.aliyun.com/document_detail/64769.html
运行模式- BEAN模式:任务以JobHandler方式维护在执行器端;需要结合 “JobHandler” 属性匹配执行器中任务;
- GLUE模式(Java):任务以源码方式维护在调度中心;该模式的任务实际上是一段继承自IJobHandler的Java类代码并 “groovy” 源码方式维护,它在执行器项目中运行,可使用@Resource/@Autowire注入执行器里中的其他服务;
- Python模式、Shell模式等等
运行模式为 “BEAN模式” 时生效,对应执行器中新开发的JobHandler类“@XxlJob”注解自定义的value值;
阻塞处理策略阻塞处理策略是调度过于密集执行器来不及处理时的处理策略。
- 单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO队列并以串行方式运行;
- 丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;
- 覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务;
运行报表、调度日志、执行器管理都比较常规。
用户管理(权限管理)的功能比较弱,一般公司会进行二次开发。
调度中心集群部署调度中心支持集群部署,提升调度系统容灾和可用性。支持Docker镜像搭建。
即实现多个调度中心,高可用,官方文档建议通过nginx为调度中心集群做负载均衡。
调度中心邮件警报利用spring框架中集成的SMTP邮件服务
配置方法参考:
- https://writing-bugs.blog.csdn.net/article/details/125440957
发件人是在调度中心application.properties中设置好,包括授权码
收件人在任务设置界面设置好
XXL-JOB执行器解析 执行器配置文件//执行器连接调度中心的地址和token要设置好 xxl.job.admin.addresses=http://127.0.0.1:8002/xxl-job-admin xxl.job.accessToken=default_token //执行器名字、端口、ip等 xxl.job.executor.appname=xxl-job-executor-sample xxl.job.executor.address= xxl.job.executor.ip= xxl.job.executor.port=9999 //建议使用自动获取IP,多网卡时可手动设置指定IPXxlJobConfig.class
里面有spring的bean
执行器真正的业务逻辑:SampleXxlJob.class注意使用@Component注解@XxlJob注解。
还可以使用String param = XxlJobHelper.getJobParam()来获取参数(调度中心设置的参数)
注意这里之前注解是@JobHandler,现在都更新为@XxlJob,网上很多旧的教程还在用@JobHandler,官方文档教程也还没完全更新。 项目部署 XXL-JOB调度中心和执行器交互-
进入调度中心
-
新增执行器(上述各种设置)
-
新增任务(上述各种设置)
-
调度中心-执行器管理-填写多个机器地址,逗号分隔
-
任务管理-设置路由策略,比如使用轮询的方式
-
测试
执行器集群部署时,官方要求:
- 执行器回调地址(xxl.job.admin.addresses)需要保持一致;执行器根据该配置进行执行器自动注册等操作。
- 同一个执行器集群内AppName(xxl.job.executor.appname)需要保持一致;调度中心根据该配置动态发现不同集群的在线执行器列表。
利用IDEA实现本地“模拟集群”,即把同一个项目(执行器、实例、服务)同时在多个端口号运行。
通过Run中的Edit Configurations=> Allow multiple instances,修改一下端口号等配置,再点击运行实现本地多实例运行。
DEMO测试目标:将上述项目进行执行器集群部署、每隔两秒定时输出一行信息+调度中心传入的参数(paramtest)。
@XxlJob("demoJobHandler") public void demoJobHandler() throws Exception { String param = XxlJobHelper.getJobParam(); System.out.println("任务执行测试"+"参数:"+param+"time:"+new Date()); }
执行器有两个同样的服务,模拟集群。
调度中心设置2秒执行一次,路由策略为轮询,两个服务轮流2s输出一次,正常
也进行了故障转移的测试。
与quartz进行对比Quartz作为开源作业调度中的佼佼者,是作业调度的首选。但是集群环境中Quartz采用API的方式对任务进行管理,从而可以避免上述问题,但是同样存在以下问题:
- 问题一:调用API的的方式操作任务,不人性化;
- 问题二:需要持久化业务QuartzJobBean到底层数据表中,系统侵入性相当严重。
- 问题三:调度逻辑和QuartzJobBean耦合在同一个项目中,这将导致一个问题,在调度任务数量逐渐增多,同时调度任务逻辑逐渐加重的情况下,此时调度系统的性能将大大受限于业务;
- 问题四:quartz底层以“抢占式”获取DB锁并由抢占成功节点负责运行任务,会导致节点负载悬殊非常大;而XXL-JOB通过执行器实现“协同分配式”运行任务,充分发挥集群优势,负载各节点均衡。
rtz进行对比
Quartz作为开源作业调度中的佼佼者,是作业调度的首选。但是集群环境中Quartz采用API的方式对任务进行管理,从而可以避免上述问题,但是同样存在以下问题:
- 问题一:调用API的的方式操作任务,不人性化;
- 问题二:需要持久化业务QuartzJobBean到底层数据表中,系统侵入性相当严重。
- 问题三:调度逻辑和QuartzJobBean耦合在同一个项目中,这将导致一个问题,在调度任务数量逐渐增多,同时调度任务逻辑逐渐加重的情况下,此时调度系统的性能将大大受限于业务;
- 问题四:quartz底层以“抢占式”获取DB锁并由抢占成功节点负责运行任务,会导致节点负载悬殊非常大;而XXL-JOB通过执行器实现“协同分配式”运行任务,充分发挥集群优势,负载各节点均衡。
XXL-JOB弥补了quartz的上述不足之处
附录:基础知识这包含了学习过程中遇到的相关技术和知识,包括没接触过的和之前学过但需要再重点复习的。
集群、分布式、SOA和微服务。 集群:同一个业务,部署在多个服务器上。代表技术:Nginx集群
分布式:一个业务分拆成多个子业务,部署在不同的服务器上。
分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。
好的设计应该是分布式和集群的结合,先分布式再集群,具体实现就是业务拆分成很多子业务,然后针对每个子业务进行集群部署,这样每个子业务如果出了问题,整个系统完全不会受影响。
SOA:面向服务架构。中间有ESB:企业服务总线。目的是将原先系统间散乱、无规划的网状结构,梳理成规整、可治理的星形结构。
为了解决分布式中出现的复杂的子业务直接的调用关系来提出的。
在SOA基础上进一步解决架构复杂问题。微服务的拆分更细,针对于单一的服务进行更细致化的拆分,更轻量化,并通过更高级、更轻量的rest-api通信取代了ESB。
微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。
Docker技术容器就是将软件打包成标准化单元,以用于开发、交付和部署。
Docker 是世界领先的软件容器平台
Java号称“一次编译,到处运行”,因为java虚拟机解决平台的兼容性问题,所以有java虚拟机的地方就能跑java代码;
Docker是:“一次封装,到处运行”,因为docker解决了应用环境的问题,安装了docker的平台就能跑“docker包”,这样就决绝了“开发环境能跑,一上线就崩”的尴尬。
Docker包含Image(镜像)、Container(容器)、Repository(仓库)
高可用高可用(High availability)的主要目的是为了保障业务的连续性,即在用户眼里,业务永远是正常(或者说基本正常)对外提供服务的。
实现高可用基本思路
- 注重代码质量、严格测试
- 使用集群,减少单点故障
- 超时和重试机制、熔断机制
- 异步调用
- 使用缓存
- 提高硬件质量、定期检测维护等等等
FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
AdminLTE: Bootstrap 管理员仪表盘模板AdminLTE 是一个完全响应管理模板,主要依赖于 Bootstrap 3、jQuery 1.11+ 这两个框架,插件中使用的基本都是Bootstrap和jQquery插件。
SpringBoot Actuator程序监控器Spring Boot Actuator就是一款可以帮助你监控系统数据的框架,其可以监控很多很多的系统数据,它有对应用系统的自省和监控的集成功能,可以查看应用配置的详细信息,比如:
- 显示应用程序员的Health健康信息
- 显示Info应用信息
- 显示HTTP Request跟踪信息
自动配置Autoconfigure
起步依赖Starter
命令行界面CLI
监控器Actuator
MVNrepository官网可以查询各种缺失的依赖:https://mvnrepository.com/