Merge branch 'master' into master

2.5.0
许雪里 8 months ago committed by GitHub
commit 66e0d56724
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -40,6 +40,19 @@ Now, it's already open source, and many companies use it in production environme
XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线开箱即用。
## Sponsor
XXL-JOB is an open source and free project, with its ongoing development made possible entirely by the support of these awesome backers.
XXL-JOB 是一个开源且免费项目,其正在进行的开发完全得益于支持者的支持。开源不易,[前往赞助项目开发](https://www.xuxueli.com/page/donate.html )
<!-- supporter start -->
<h5 style="color: silver;" >金牌赞助方</h5>
<a href="https://www.jetbrains.com/idea/?from=xuxueli.com" title="jetbrains" target="_blank" >
<img width="55px" src="http://www.xuxueli.com/page/static/images/logo_intellij.jpeg" >
</a>
<!-- supporter end -->
## Documentation
- [中文文档](https://www.xuxueli.com/xxl-job/)
- [English Documentation](https://www.xuxueli.com/xxl-job/en/)
@ -119,6 +132,8 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
于2021-12-06XXL-JOB参与"[2021年度OSC中国开源项目评选](https://www.oschina.net/project/top_cn_2021) "评比,在当时已录入的一万多个开源项目中角逐,最终当选"最受欢迎项目"。
于2024-11-06XXL-JOB经 GitCode 官方评审,获得 “G-Star项目毕业认证”。
> 我司大众点评目前已接入XXL-JOB内部别名《Ferrari》Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。
据最新统计, 自2016-01-21接入至2017-12-01期间该系统已调度约100万次表现优异。新接入应用推荐使用最新版本因为经过数十个版本的更新系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升核心功能更加稳定高效。
@ -766,6 +781,41 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
- 640、再造再生健康科技有限公司
- 641、华宝证券
- 642、卓正医疗
- 643、深圳湛信科技
- 644、陕西鑫众为软件有限公司
- 645、深圳市润农科技有限公司
- 646、庚商教育智能科技有限公司
- 647、杭州祎声科技
- 648、四川久远银海软件股份有限公司
- 649、GeeFox极狐低代码
- 650、浙江和仁科技股份有限公司
- 651、宁波聚臻智能科技有限公司
- 652、福建福昕软件开发股份有限公司【福昕】
- 653、广州中长康达信息技术有限公司
- 654、武汉趣改信息科技有限公司
- 655、北京华夏思源科技发展有限公司
- 656、宁波关关通科技有限公司
- 657、青岛吕氏餐饮有限公司
- 658、杭州乐刻网络科技有限公司
- 659、上海红瓦信息科技有限公司
- 660、陕西旅小宝信息科技有限公司
- 661、中科卓恒(大连)科技有限公司
- 662、北京华益精点生物技术有限公司
- 663、马士基中国航运有限公司【马士基】
- 664、陕西美咚网络科技有限公司
- 665、山东新北洋信息技术股份有限公司
- 666、福建中瑞文化发展集团有限公司
- 667、黑龙江省建工集团有限责任公司【黑龙江省建工】
- 668、志信能达安全科技(广州)有限公司
- 669、重庆开源共创科技有限公司
- 670、华泰人寿保险股份有限公司【华泰人寿】
- 671、成都盘古纵横集团
- 672、北京果果乐学科技有限公司
- 673、北京凌云空间科技有限公司
- 674、临工重机股份有限公司
- 675、上海热风时尚管理集团【热风】
- 676、HashKey Exchange
- 677、傲基深圳跨境商务股份有限公司
- ……
> 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
@ -785,10 +835,4 @@ This product is open source and free, and will continue to provide free communit
- Licensed under the GNU General Public License (GPL) v3.
- Copyright (c) 2015-present, xuxueli.
产品开源免费,并且将持续提供免费的社区技术支持。个人或企业内部可自由的接入和使用。如有需要可邮件联系作者免费获取项目授权。
## Donate
No matter how much the donation amount is enough to express your thought, thank you very much [To donate](https://www.xuxueli.com/page/donate.html )
无论捐赠金额多少都足够表达您这份心意,非常感谢 [前往捐赠](https://www.xuxueli.com/page/donate.html )
产品开源免费,并且将持续提供免费的社区技术支持。个人或企业内部可自由的接入和使用。如有需要可 [邮件联系](https://www.xuxueli.com/page/community.html) 作者免费获取项目授权。

@ -17,7 +17,7 @@
### 1.1 概述
XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线开箱即用。
### 1.2 社区交流
### 1.2 社区交流
- [社区交流](https://www.xuxueli.com/page/community.html)
### 1.3 特性
@ -736,6 +736,41 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
- 640、再造再生健康科技有限公司
- 641、华宝证券
- 642、卓正医疗
- 643、深圳湛信科技
- 644、陕西鑫众为软件有限公司
- 645、深圳市润农科技有限公司
- 646、庚商教育智能科技有限公司
- 647、杭州祎声科技
- 648、四川久远银海软件股份有限公司
- 649、GeeFox极狐低代码
- 650、浙江和仁科技股份有限公司
- 651、宁波聚臻智能科技有限公司
- 652、福建福昕软件开发股份有限公司【福昕】
- 653、广州中长康达信息技术有限公司
- 654、武汉趣改信息科技有限公司
- 655、北京华夏思源科技发展有限公司
- 656、宁波关关通科技有限公司
- 657、青岛吕氏餐饮有限公司
- 658、杭州乐刻网络科技有限公司
- 659、上海红瓦信息科技有限公司
- 660、陕西旅小宝信息科技有限公司
- 661、中科卓恒(大连)科技有限公司
- 662、北京华益精点生物技术有限公司
- 663、马士基中国航运有限公司【马士基】
- 664、陕西美咚网络科技有限公司
- 665、山东新北洋信息技术股份有限公司
- 666、福建中瑞文化发展集团有限公司
- 667、黑龙江省建工集团有限责任公司【黑龙江省建工】
- 668、志信能达安全科技(广州)有限公司
- 669、重庆开源共创科技有限公司
- 670、华泰人寿保险股份有限公司【华泰人寿】
- 671、成都盘古纵横集团
- 672、北京果果乐学科技有限公司
- 673、北京凌云空间科技有限公司
- 674、临工重机股份有限公司
- 675、上海热风时尚管理集团【热风】
- 676、HashKey Exchange
- 677、傲基深圳跨境商务股份有限公司
- ……
> 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
@ -756,6 +791,7 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
--- | ---
[https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job) | [Download](https://github.com/xuxueli/xxl-job/releases)
[http://gitee.com/xuxueli0323/xxl-job](http://gitee.com/xuxueli0323/xxl-job) | [Download](http://gitee.com/xuxueli0323/xxl-job/releases)
[https://gitcode.com/xuxueli0323/xxl-job](https://gitcode.com/xuxueli0323/xxl-job) | [Download](https://gitcode.com/xuxueli0323/xxl-job/tags)
#### 中央仓库地址
@ -797,7 +833,7 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
xxl-job-executor-samples执行器Sample示例选择合适的版本执行器可直接使用也可以参考其并将现有项目改造成执行器
xxl-job-executor-sample-springbootSpringboot版本通过Springboot管理执行器推荐这种方式
xxl-job-executor-sample-frameless无框架版本
### 2.3 配置部署“调度中心”
@ -840,8 +876,8 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
### 调度中心日志表数据保存天数 [必填]过期日志自动清理限制大于等于7时生效否则, 如-1关闭自动清理功能
xxl.job.logretentiondays=30
#### 步骤二:部署项目:
如果已经正确进行上述配置,可将项目编译打包部署。
@ -875,8 +911,6 @@ docker pull xuxueli/xxl-job-admin
- 创建容器并运行
```
docker run -p 8080:8080 -v /tmp:/data/applogs --name xxl-job-admin -d xuxueli/xxl-job-admin:{指定版本}
/**
* 如需自定义 mysql 等配置,可通过 "-e PARAMS" 指定,参数格式 PARAMS="--key=value --key2=value2"
* 配置项参考文件:/xxl-job/xxl-job-admin/src/main/resources/application.properties
@ -890,10 +924,10 @@ docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_jo
“执行器”项目xxl-job-executor-sample-springboot (提供多种版本执行器供选择,现以 springboot 版本为例,可直接使用,也可以参考其并将现有项目改造成执行器)
作用:负责接收“调度中心”的调度并执行;可直接部署执行器,也可以将执行器集成到现有业务项目中。
#### 步骤一maven依赖
确认pom文件中引入了 "xxl-job-core" 的maven依赖
#### 步骤二:执行器配置
执行器配置,配置文件地址:
@ -919,7 +953,7 @@ docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_jo
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### 执行器日志文件保存天数 [选填] 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
xxl.job.executor.logretentiondays=30
#### 步骤三:执行器组件配置
@ -951,7 +985,7 @@ public XxlJobSpringExecutor xxlJobExecutor() {
xxl-job-executor-sample-springboot项目编译打包成springboot类型的可执行JAR包命令启动即可
xxl-job-executor-sample-frameless项目编译打包成JAR包命令启动即可
至此“执行器”项目已经部署结束。
@ -959,11 +993,11 @@ public XxlJobSpringExecutor xxlJobExecutor() {
执行器支持集群部署,提升调度系统可用性,同时提升任务处理能力。
执行器集群部署时,几点要求和建议:
- 执行器回调地址xxl.job.admin.addresses需要保持一致执行器根据该配置进行执行器自动注册等操作。
- 执行器回调地址xxl.job.admin.addresses需要保持一致执行器根据该配置进行执行器自动注册等操作。
- 同一个执行器集群内AppNamexxl.job.executor.appname需要保持一致调度中心根据该配置动态发现不同集群的在线执行器列表。
### 2.5 开发第一个任务“Hello World”
### 2.5 开发第一个任务“Hello World”
本示例以新建一个 “GLUE模式(Java)” 运行模式的任务为例。更多有关任务的详细配置,请查看“章节三:任务详解”。
“GLUE模式(Java)”的执行代码托管到调度中心在线维护相比“Bean模式任务”需要在执行器项目开发部署上线更加简便轻量
@ -988,7 +1022,7 @@ public XxlJobSpringExecutor xxlJobExecutor() {
#### 步骤三:触发执行:
请点击任务右侧 “执行” 按钮可手动触发一次任务执行通常情况下通过配置Cron表达式进行任务调度触发
#### 步骤四:查看日志:
#### 步骤四:查看日志:
请点击任务右侧 “日志” 按钮,可前往任务日志界面查看任务日志。
在任务日志界面中,可查看该任务的历史调度记录以及每一次调度的任务调度信息、执行参数和执行信息。运行中的任务点击右侧的“执行日志”按钮,可进入日志控制台查看实时执行日志。
@ -1027,7 +1061,7 @@ public XxlJobSpringExecutor xxlJobExecutor() {
GLUE模式(PHP):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "php" 脚本;
GLUE模式(NodeJS):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "nodejs" 脚本;
GLUE模式(PowerShell):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "PowerShell" 脚本;
- JobHandler运行模式为 "BEAN模式" 时生效对应执行器中新开发的JobHandler类“@JobHandler”注解自定义的value值
- JobHandler运行模式为 "BEAN模式" 时生效对应执行器中新开发的JobHandler类“@XxlJob”注解自定义的value值
- 执行参数:任务执行所需的参数;
高级配置:
@ -1052,11 +1086,11 @@ public XxlJobSpringExecutor xxlJobExecutor() {
覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务;
- 任务超时时间:支持自定义任务超时时间,任务运行超时将会主动中断任务;
- 失败重试次数;支持自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;
### 3.1 BEAN模式类形式
Bean模式任务支持基于类的开发方式每个任务对应一个Java类。
@ -1095,7 +1129,7 @@ Bean模式任务支持基于方法的开发方式每个任务对应一个
2、注解配置为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")"注解value值对应的是调度中心新建任务的JobHandler属性的值。
3、执行日志需要通过 "XxlJobHelper.log" 打印执行日志;
4、任务结果默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果;
```
// 可参考Sample示例执行器中的 "com.xxl.job.executor.service.jobhandler.SampleXxlJob" ,如下:
@XxlJob("demoJobHandler")
@ -1140,7 +1174,7 @@ public void demoJobHandler() throws Exception {
### 3.4 GLUE模式(Shell)
#### 步骤一:调度中心,新建调度任务
#### 步骤一:调度中心,新建调度任务
参考上文“配置属性详细说明”对新建的任务进行参数配置,运行模式选中 "GLUE模式(Shell)"
#### 步骤二:开发任务代码:
@ -1152,7 +1186,7 @@ public void demoJobHandler() throws Exception {
### 3.4 GLUE模式(Python)
#### 步骤一:调度中心,新建调度任务
#### 步骤一:调度中心,新建调度任务
参考上文“配置属性详细说明”对新建的任务进行参数配置,运行模式选中 "GLUE模式(Python)"
#### 步骤二:开发任务代码:
@ -1164,7 +1198,7 @@ public void demoJobHandler() throws Exception {
### 3.5 GLUE模式(NodeJS)
#### 步骤一:调度中心,新建调度任务
#### 步骤一:调度中心,新建调度任务
参考上文“配置属性详细说明”对新建的任务进行参数配置,运行模式选中 "GLUE模式(NodeJS)"
#### 步骤二:开发任务代码:
@ -1330,11 +1364,11 @@ XXL-JOB调度模块基于自研调度组件并支持集群部署调度数据
#### 5.3.2 系统组成
- **调度模块(调度中心)**
负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块;
支持可视化、简单且动态的管理调度信息包括任务新建更新删除GLUE开发和任务报警等所有上述操作都会实时生效同时支持监控调度结果以及执行日志支持执行器Failover。
负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块;
支持可视化、简单且动态的管理调度信息包括任务新建更新删除GLUE开发和任务报警等所有上述操作都会实时生效同时支持监控调度结果以及执行日志支持执行器Failover。
- **执行模块(执行器)**
负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;
接收“调度中心”的执行请求、终止请求和日志请求等。
负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;
接收“调度中心”的执行请求、终止请求和日志请求等。
#### 5.3.3 架构图
@ -1343,7 +1377,7 @@ XXL-JOB调度模块基于自研调度组件并支持集群部署调度数据
### 5.4 调度模块剖析
#### 5.4.1 quartz的不足
Quartz作为开源作业调度中的佼佼者是作业调度的首选。但是集群环境中Quartz采用API的方式对任务进行管理从而可以避免上述问题但是同样存在以下问题
- 问题一调用API的的方式操作任务不人性化
- 问题二需要持久化业务QuartzJobBean到底层数据表中系统侵入性相当严重。
- 问题三调度逻辑和QuartzJobBean耦合在同一个项目中这将导致一个问题在调度任务数量逐渐增多同时调度任务逻辑逐渐加重的情况下此时调度系统的性能将大大受限于业务
@ -1440,7 +1474,7 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
如若需要支撑更多的任务量,可以通过 "调大调度线程数" 、"降低调度中心与执行器ping延迟" 和 "提升机器配置" 几种方式优化。
#### 5.4.12 均衡调度
#### 5.4.12 均衡调度
调度中心在集群部署时会自动进行任务平均分配,触发组件每次获取与线程池数量(调度中心支持自定义调度线程池大小)相关数量的任务,避免大量任务集中在单个调度中心集群节点;
### 5.5 任务 "运行模式" 剖析
@ -1469,7 +1503,7 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
#### 5.5.4 执行器
执行器实际上是一个内嵌的Server默认端口9999配置项xxl.job.executor.port
在项目启动时,执行器会通过“@JobHandler”识别Spring容器中“Bean模式任务”以注解的value属性为key管理起来。
在项目启动时,执行器会通过“@XxlJob”识别Spring容器中“Bean模式任务”以注解的value属性为key管理起来。
“执行器”接收到“调度中心”的调度请求时如果任务类型为“Bean模式”将会匹配Spring容器中的“Bean模式任务”然后调用其execute方法执行任务逻辑。如果任务类型为“GLUE模式”将会加载GLue代码实例化Java对象注入依赖的Spring服务注意Glue代码中注入的Spring服务必须存在与该“执行器”项目的Spring容器中然后调用execute方法执行任务逻辑。
@ -1484,7 +1518,7 @@ XXL-JOB会为每次调度请求生成一个单独的日志文件需要通过
### 5.6 通讯模块剖析
#### 5.6.1 一次完整的任务调度通讯流程
#### 5.6.1 一次完整的任务调度通讯流程
- 1、“调度中心”向“执行器”发送http调度请求: “执行器”中接收请求的服务实际上是一台内嵌Server默认端口9999;
- 2、“执行器”执行任务逻辑
- 3、“执行器”http回调“调度中心”调度结果: “调度中心”中接收回调的服务是针对执行器开放一套API服务;
@ -1492,14 +1526,14 @@ XXL-JOB会为每次调度请求生成一个单独的日志文件需要通过
#### 5.6.2 通讯数据加密
调度中心向执行器发送的调度请求时使用RequestModel和ResponseModel两个对象封装调度请求参数和响应数据, 在进行通讯之前底层会将上述两个对象对象序列化,并进行数据协议以及时间戳检验,从而达到数据加密的功能;
### 5.7 任务注册, 任务自动发现
### 5.7 任务注册, 任务自动发现
自v1.5版本之后, 任务取消了"任务执行机器"属性, 改为通过任务注册和自动发现的方式, 动态获取远程执行器地址并执行。
AppName: 每个执行器机器集群的唯一标示, 任务注册以 "执行器" 为最小粒度进行注册; 每个任务通过其绑定的执行器可感知对应的执行器机器列表;
注册表: 见"xxl_job_registry"表, "执行器" 在进行任务注册时将会周期性维护一条注册记录即机器地址和AppName的绑定关系; "调度中心" 从而可以动态感知每个AppName在线的机器列表;
执行器注册: 任务注册Beat周期默认30s; 执行器以一倍Beat进行执行器注册, 调度中心以一倍Beat进行动态任务发现; 注册信息的失效时间为三倍Beat;
执行器注册摘除:执行器销毁时,将会主动上报调度中心并摘除对应的执行器机器信息,提高心跳注册的实时性;
为保证系统"轻量级"并且降低学习部署成本没有采用Zookeeper作为注册中心采用DB方式进行任务注册发现
@ -1508,7 +1542,7 @@ XXL-JOB会为每次调度请求生成一个单独的日志文件需要通过
当返回值符合 "ReturnT.code == ReturnT.SUCCESS_CODE" 时表示任务执行成功,否则表示任务执行失败,而且可以通过 "ReturnT.msg" 回调错误信息给调度中心;
从而,在任务逻辑中可以方便的控制任务执行结果;
### 5.9 分片广播 & 动态分片
### 5.9 分片广播 & 动态分片
执行器集群部署时,任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发对应集群中所有执行器执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务;
"分片广播" 以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。
@ -1527,7 +1561,7 @@ int shardTotal = XxlJobHelper.getShardTotal();
echo "分片序号 index = $2"
echo "分片总数 total = $3"
```
分片参数属性说明:
index当前分片序号(从0开始),执行器集群列表中当前执行器的序号;
@ -1549,7 +1583,7 @@ echo "分片总数 total = $3"
### 5.11 故障转移 & 失败重试
一次完整任务流程包括"调度(调度中心) + 执行(执行器)"两个阶段。
- "故障转移"发生在调度阶段在执行器集群部署时如果某一台执行器发生故障该策略支持自动进行Failover切换到一台正常的执行器机器并且完成调度请求流程。
- "失败重试"发生在"调度 + 执行"两个阶段,支持通过自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;
@ -1561,7 +1595,7 @@ echo "分片总数 total = $3"
- 1、执行器改为手动注册下线一半机器列表A组线上运行另一半机器列表B组
- 2、等待A组机器任务运行结束并编译上线执行器注册地址替换为A组
- 3、等待B组机器任务运行结束并编译上线执行器注册地址替换为A组+B组
操作结束;
操作结束;
### 5.13 任务执行结果说明
系统根据以下标准判断任务执行结果,可参考之。
@ -1589,15 +1623,15 @@ XXL-JOB是一个跨语言的任务调度平台主要体现在如下几个方
可以通过以下命令快速构建调度中心,并启动运行;
```
mvn clean package
docker build -t xuxueli/xxl-job-admin ./xxl-job-admin
docker build -t xuxueli/xxl-job-admin:{version} ./xxl-job-admin
docker run --name xxl-job-admin -p 8080:8080 -d xuxueli/xxl-job-admin
```
### 5.20 避免任务重复执行
### 5.20 避免任务重复执行
调度密集或者耗时任务可能会导致任务阻塞,集群情况下调度组件小概率情况下会重复触发;
针对上述情况,可以通过结合 "单机路由策略(如:第一台、一致性哈希)" + "阻塞策略(如:单机串行、丢弃后续调度)" 来规避,最终避免任务重复执行。
针对上述情况,可以通过结合 "单机路由策略(如:第一台、一致性哈希)" + "阻塞策略(如:单机串行、丢弃后续调度)" 来规避,最终避免任务重复执行。
### 5.21 命令行任务
### 5.21 命令行任务
原生提供通用命令行任务HandlerBean任务"CommandJobHandler");业务方只需要提供命令行即可;
如任务参数 "pwd" 将会执行命令并输出数据;
@ -1659,7 +1693,7 @@ Header
"msg": null // 错误提示消息
}
```
#### b、执行器注册
```
说明:执行器注册时使用,调度中心会实时感知注册成功的执行器并发起任务调度
@ -1818,7 +1852,7 @@ Header
#### d、查看执行日志
```
说明:终止任务,滚动方式加载
说明:查看任务日志,滚动方式加载
------
@ -1867,35 +1901,35 @@ Header
- 3、底层通讯支持两种方式Servlet方式 + JETTY方式
- 4、支持“任务日志”
- 5、支持“串行执行”并行执行
说明V1.2版本将系统架构按功能拆分为:
- 调度模块(调度中心):负责管理调度信息,按照调度配置发出调度请求;
- 执行模块(执行器):负责接收调度请求并执行任务逻辑;
- 通讯模块:负责调度模块和任务模块之间的信息通讯;
优点:
- 解耦:任务模块提供任务接口,调度模块维护调度信息,业务相互独立;
- 高扩展性;
- 稳定性;
说明V1.2版本将系统架构按功能拆分为:
- 调度模块(调度中心):负责管理调度信息,按照调度配置发出调度请求;
- 执行模块(执行器):负责接收调度请求并执行任务逻辑;
- 通讯模块:负责调度模块和任务模块之间的信息通讯;
优点:
- 解耦:任务模块提供任务接口,调度模块维护调度信息,业务相互独立;
- 高扩展性;
- 稳定性;
### 7.3 版本 V1.3.0,新特性[2016-05-19]
- 1、遗弃“本地任务”模式推荐使用“远程任务”易于系统解耦任务对应的JobHandler统称为“执行器”
- 2、遗弃“servlet”方式底层系统通讯推荐使用JETTY方式调度+回调双向通讯,重构通讯逻辑;
- 3、UI交互优化左侧菜单展开状态优化菜单项选中状态优化任务列表打开表格有压缩优化
- 4、【重要】“执行器”细分为BEAN、GLUE两种开发模式简介见下文
“执行器” 模式简介:
- BEAN模式执行器每个执行器都是Spring的一个Bean实例XXL-JOB通过注解@JobHandler识别和调度执行器
-GLUE模式执行器每个执行器对应一段代码在线Web编辑和维护动态编译生效执行器负责加载GLUE代码和执行
“执行器” 模式简介:
- BEAN模式执行器每个执行器都是Spring的一个Bean实例XXL-JOB通过注解@JobHandler识别和调度执行器
-GLUE模式执行器每个执行器对应一段代码在线Web编辑和维护动态编译生效执行器负责加载GLUE代码和执行
### 7.4 版本 V1.3.1,新特性[2016-05-23]
- 1、更新项目目录结构
- /xxl-job-admin -------------------- 【调度中心】:负责管理调度信息,按照调度配置发出调度请求;
- /xxl-job-core ----------------------- 公共依赖
- /xxl-job-executor-example ------ 【执行器】:负责接收调度请求并执行任务逻辑;
- /db ---------------------------------- 建表脚本
- /doc --------------------------------- 用户手册
- /xxl-job-admin -------------------- 【调度中心】:负责管理调度信息,按照调度配置发出调度请求;
- /xxl-job-core ----------------------- 公共依赖
- /xxl-job-executor-example ------ 【执行器】:负责接收调度请求并执行任务逻辑;
- /db ---------------------------------- 建表脚本
- /doc --------------------------------- 用户手册
- 2、在新的目录结构上升级了用户手册
- 3、优化了一些交互和UI
@ -1909,7 +1943,7 @@ Header
- 2、执行器底层实现代码进行重度重构, 优化底层建表脚本;
- 3、执行器中任务线程分组逻辑优化: 之前根据执行器JobHandler进行线程分组,当多个任务复用Jobhanlder会导致相互阻塞。现改为根据调度中心任务进行任务线程分组,任务与任务执行相互隔离;
- 4、执行器调度通讯方案优化, 通过Hex + HC实现建议RPC通讯协议, 优化了通讯参数的维护和解析流程;
- 5、调度中心, 新建/编辑任务, 界面属性调整:
- 5、调度中心, 新建/编辑任务, 界面属性调整:
- 5.1、任务新增/编辑界面中去除 "任务名JobName"属性 ,该属性改为系统自动生成: 该字段之前主要用于在 "调度中心" 唯一标示一个任务, 现实意义不大, 因此计划淡化掉该字段,改为系统生成UUID,从而简化任务新建的操作;
- 5.2、任务新增/编辑界面中去除 "GLUE模式" 复选框位置调整, 改为贴近"JobHandler"输入框右侧;
- 5.3、任务新增/编辑界面中去除 "报警阈值" 属性;
@ -1924,7 +1958,7 @@ Header
Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段, 地址见分支 [V1.3](https://github.com/xuxueli/xxl-job/tree/v1.3) 。新特性将会在master分支持续更新。
### 7.7 版本 V1.4.1 新特性[2016-09-06]
- 1、项目成功推送maven中央仓库, 中央仓库地址以及依赖如下:
- 1、项目成功推送maven中央仓库, 中央仓库地址以及依赖如下:
```
<!-- http://repo1.maven.org/maven2/com/xuxueli/xxl-job-core/ -->
<dependency>
@ -2052,7 +2086,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
- 11、统一maven依赖版本管理
### 7.20 版本 V1.8.2 特性[2017-09-04]
- 1、项目主页搭建提供中英文文档https://www.xuxueli.com/xxl-job
- 1、项目主页搭建提供中英文文档https://www.xuxueli.com/xxl-job
- 2、JFinal执行器Sample示例项目
- 3、事件触发除了"Cron方式"和"任务依赖方式"触发任务执行之外支持基于事件的触发任务方式。调度中心提供触发任务单次执行的API服务可根据业务事件灵活触发。
- 4、执行器摘除执行器销毁时主动通知调度中心并摘除对应执行器节点提高执行器状态感知的时效性。
@ -2147,7 +2181,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
- 38、GLUE任务脚本字段类型调整改为mediumtext类型提高GLUE长度上限
- 39、任务监控线程Log输出优化运行中任务的监控Log改为debug级别减少非核心日志量
- 40、项目依赖全量升级至较新稳定版本如spring、Jackson、groovy等等
- 41、docker支持调度中心提供 Dockerfile 方便快速构建docker镜像
- 41、docker支持调度中心提供 Dockerfile 方便快速构建docker镜像
### 7.24 版本 V2.0.0 Release Notes[2018-11-04]
- 1、调度中心迁移到 springboot
@ -2159,7 +2193,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
- 7、任务状态规范新增任务默认停止状态任务更新时保持任务状态不变
- 8、IP获取逻辑优化优先遍历网卡来获取可用IP
- 9、任务新增的API服务接口返回任务ID方便调用方实用
- 10、组件化优化移除对 spring 的依赖非spring应用选用 "XxlJobExecutor" 、spring应用选用 "XxlJobSpringExecutor" 作为执行器组件;
- 10、组件化优化移除对 spring 的依赖非spring应用选用 "XxlJobExecutor" 、spring应用选用 "XxlJobSpringExecutor" 作为执行器组件;
- 11、任务RollingLog展示逻辑优化修复超时任务无法查看的问题
- 12、多项UI组件升级到最新版本CodeMirror、Echarts、Jquery 等;
- 13、项目依赖升级 groovy 至较新稳定版本pom清理
@ -2293,7 +2327,7 @@ data: post-data
- 20、修复bootstrap.min.css.map 404问题
- 21、执行器UI交互优化,移除冗余order属性
- 22、执行备注消息长度限制修复数据超长无法存储导致导致回调失败的问题
注意XxlJobSpringExecutor组件个别字段调整“appName” 调整为 “appname” ,升级时该组件时需要注意;
注意XxlJobSpringExecutor组件个别字段调整“appName” 调整为 “appname” ,升级时该组件时需要注意;
### 7.31 版本 v2.3.0 Release Notes[2021-02-09]
- 1、【新增】调度过期策略调度中心错过调度时间的补偿处理策略包括忽略、立即补偿触发一次等
@ -2331,7 +2365,7 @@ public void execute() {
- 24、【修复】执行器注册表字段优化解决执行器注册节点过多导致注册信息存储和更新失败的问题
- 25、【修复】轮训路由策略优化修复小概率下并发问题
- 26、【修复】页面redirect跳转后https变为http问题修复
- 27、【修复】执行器日志清理优化修复小概率下日志文件为空导致清理异常问题
- 27、【修复】执行器日志清理优化修复小概率下日志文件为空导致清理异常问题
### 7.32 版本 v2.3.1 Release Notes[2022-05-21]
- 1、【修复】修复风险漏洞升级问题低版本项目依赖CVE-2021-2471、CVE-2022-22965等。
@ -2341,17 +2375,39 @@ public void execute() {
- 5、【优化】任务线程名优化提升可读性与问题定位效率(ISSUE-2527)。
### 7.33 版本 v2.4.0 Release Notes[2023-03-23]
- 1、【优化】执行器任务Bean扫描逻辑优化解决懒加载注解失效问题
- 1、【优化】执行器任务Bean扫描逻辑优化解决懒加载注解失效问题
- 2、【优化】多个项目依赖升级至较新稳定版本涉及netty、groovy、spring、springboot、mybatis等
- 3、【修复】"CVE-2022-36157" 授权漏洞修复。
- 4、【修复】"CVE-2022-43183" SSRF漏洞修复。
- 3、【修复】漏洞修复包括"CVE-2022-36157" 授权漏洞修复;"CVE-2022-43183" SSRF漏洞修复
### 7.34 版本 v2.4.1 Release Notes[规划中]
### 7.34 版本 v2.4.1 Release Notes[2024-04-17]
- 1、【优化】多个项目依赖升级至较新稳定版本涉及netty、groovy、springboot、mybatis等
- 2、【修复】"CVE-2022-43402" groovy低版本漏洞修复。
- 3、【修复】调度日志页面XSS漏洞修复(ISSUE-3360)。
- 4、[规划中]注册节点,弹框分页展示;解决注册节点过多时无法展示问题;
- 2、【优化】执行器注册节点显示交互调整优化注册节点过多时展示不全体验
- 3、【修复】漏洞修复包括"CVE-2022-43402" groovy低版本漏洞修复"CVE-2024-29025" netty低版本漏洞修复"CVE-2024-3366" freemarker模板注入漏洞修复"CVE-2022-43183" 越权漏洞增强修复;
- 4、【修复】调度日志页面XSS问题修复(ISSUE-3360)。
### 7.35 版本 v2.4.2 Release Notes[2024-11-16]
- 1、【优化】调度中心任务Next计算逻辑调整避免Cron解析失败导致重复执行问题。
- 2、【优化】Cron解析组件代码重构微调健壮性提升
- 3、【优化】修改密码交互调整避免CSRF隐患
- 4、【优化】JdkSerializeTool流关闭逻辑优化
- 5、【优化】任务信息、执行日志API非功能设计完善避免越权隐患
- 6、【修复】漏洞修复包括 "CVE-2024-42681" 子任务越权漏洞修复、"CVE-2023-33779" 任务API越权问题修复
- 7、【升级】多个项目依赖升级至较新稳定版本涉及netty、groovy、gson、springboot、mybatis等
**备注:**
- a、“CVE-2024-38820”漏洞源自spring当前使用spring5.x及springboot2.x软件普遍受该问题影响。 该问题修复需要升级至spring6.x与springboot3.x同时需要升级JDK17如有诉求可自行升级。计划下个大版本升级spring及jdk版本解决该问题。
- b、本次升级数据模型及通讯协议向前兼容v2.4.*可无缝升级。
### 7.36 版本 v2.5.0 Release Notes[规划中]
- 1、【优化】框架基础守护线程异常处理逻辑优化避免极端情况下因Error导致调度终止问题
- 2、【优化】部分系统日志优化提升可读性
- 3、【重构】调度线程任务信息更新逻辑优化避免极端情况下已关闭任务被启动问题
- 5、【重构】执行器注册逻辑重构降低多调度中心地址时并发注册问题注册表“xxl_job_registry”新增唯一索引避免冗余注册信息存储
- 4、[规划中]升级springboot3.x解决2.x老版本漏洞类问题。注意springboot3.x依赖jdk17
- 5、[规划中]安全功能增强通讯加密参数改用加密数据避免AccessToken明文 降低token泄漏风险
- 6、[规划中]登陆态Token声称逻辑优化混淆登陆时间属性降低token泄漏风险
### TODO LIST
- 1、调度隔离调度中心针对不同执行器各自维护不同的调度和远程触发组件。

Binary file not shown.

Binary file not shown.

@ -1,5 +1,5 @@
#
# XXL-JOB v2.4.1-SNAPSHOT
# XXL-JOB
# Copyright (c) 2015-present, xuxueli.
CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci;
@ -54,7 +54,8 @@ CREATE TABLE `xxl_job_log` (
PRIMARY KEY (`id`),
KEY `I_trigger_time` (`trigger_time`),
KEY `I_handle_code` (`handle_code`),
KEY `idx_jobid_jobgroup` (`job_id`,`job_group`)
KEY `I_jobid_jobgroup` (`job_id`,`job_group`),
KEY `I_job_id` (`job_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_log_report` (
@ -86,7 +87,7 @@ CREATE TABLE `xxl_job_registry` (
`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`)
UNIQUE KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_group` (

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 KiB

After

Width:  |  Height:  |  Size: 286 KiB

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>2.4.1-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
@ -17,31 +17,32 @@
</modules>
<properties>
<!-- env -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.test.skip>true</maven.test.skip>
<netty.version>4.1.106.Final</netty.version>
<gson.version>2.10.1</gson.version>
<spring.version>5.3.31</spring.version>
<!-- plugin -->
<maven-source-plugin.version>3.3.1</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.11.1</maven-javadoc-plugin.version>
<maven-gpg-plugin.version>3.2.7</maven-gpg-plugin.version>
<!-- base -->
<slf4j-api.version>2.0.16</slf4j-api.version>
<junit-jupiter.version>5.11.3</junit-jupiter.version>
<javax.annotation-api.version>1.3.2</javax.annotation-api.version>
<!-- net -->
<netty.version>4.1.115.Final</netty.version>
<gson.version>2.11.0</gson.version>
<!-- spring -->
<spring.version>5.3.39</spring.version>
<spring-boot.version>2.7.18</spring-boot.version>
<!-- db -->
<mybatis-spring-boot-starter.version>2.3.2</mybatis-spring-boot-starter.version>
<mysql-connector-j.version>8.3.0</mysql-connector-j.version>
<slf4j-api.version>2.0.11</slf4j-api.version>
<junit-jupiter.version>5.10.1</junit-jupiter.version>
<javax.annotation-api.version>1.3.2</javax.annotation-api.version>
<groovy.version>4.0.18</groovy.version>
<maven-source-plugin.version>3.3.0</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.6.3</maven-javadoc-plugin.version>
<maven-gpg-plugin.version>3.1.0</maven-gpg-plugin.version>
<mysql-connector-j.version>9.1.0</mysql-connector-j.version>
<!-- dynamic language -->
<groovy.version>4.0.24</groovy.version>
</properties>
<build>
@ -76,6 +77,12 @@
<profile>
<id>release</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>xxl-job-core</module>
</modules>
<build>
<plugins>
<!-- Source -->

@ -4,11 +4,15 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>2.4.1-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-admin</artifactId>
<packaging>jar</packaging>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
@ -89,13 +93,14 @@
</execution>
</executions>
</plugin>
<!-- docker -->
<plugin>
<!--<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.13</version>
<configuration>
<!-- made of '[a-z0-9-_.]' -->
&lt;!&ndash; made of '[a-z0-9-_.]' &ndash;&gt;
<imageName>${project.artifactId}:${project.version}</imageName>
<dockerDirectory>${project.basedir}</dockerDirectory>
<resources>
@ -106,7 +111,8 @@
</resource>
</resources>
</configuration>
</plugin>
</plugin>-->
</plugins>
</build>

@ -1,7 +1,7 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.service.LoginService;
import com.xxl.job.admin.service.impl.LoginService;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT;
import org.springframework.beans.propertyeditors.CustomDateEditor;

@ -1,5 +1,6 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLogGlue;
import com.xxl.job.admin.core.util.I18nUtil;
@ -43,7 +44,7 @@ public class JobCodeController {
}
// valid permission
JobInfoController.validPermission(request, jobInfo.getJobGroup());
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
// Glue类型-字典
model.addAttribute("GlueTypeEnum", GlueTypeEnum.values());
@ -55,7 +56,7 @@ public class JobCodeController {
@RequestMapping("/save")
@ResponseBody
public ReturnT<String> save(Model model, int id, String glueSource, String glueRemark) {
public ReturnT<String> save(HttpServletRequest request, int id, String glueSource, String glueRemark) {
// valid
if (glueRemark==null) {
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) );
@ -63,23 +64,26 @@ public class JobCodeController {
if (glueRemark.length()<4 || glueRemark.length()>100) {
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_remark_limit"));
}
XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(id);
if (exists_jobInfo == null) {
XxlJobInfo existsJobInfo = xxlJobInfoDao.loadById(id);
if (existsJobInfo == null) {
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
// valid permission
PermissionInterceptor.validJobGroupPermission(request, existsJobInfo.getJobGroup());
// update new code
exists_jobInfo.setGlueSource(glueSource);
exists_jobInfo.setGlueRemark(glueRemark);
exists_jobInfo.setGlueUpdatetime(new Date());
existsJobInfo.setGlueSource(glueSource);
existsJobInfo.setGlueRemark(glueRemark);
existsJobInfo.setGlueUpdatetime(new Date());
exists_jobInfo.setUpdateTime(new Date());
xxlJobInfoDao.update(exists_jobInfo);
existsJobInfo.setUpdateTime(new Date());
xxlJobInfoDao.update(existsJobInfo);
// log old code
XxlJobLogGlue xxlJobLogGlue = new XxlJobLogGlue();
xxlJobLogGlue.setJobId(exists_jobInfo.getId());
xxlJobLogGlue.setGlueType(exists_jobInfo.getGlueType());
xxlJobLogGlue.setJobId(existsJobInfo.getId());
xxlJobLogGlue.setGlueType(existsJobInfo.getGlueType());
xxlJobLogGlue.setGlueSource(glueSource);
xxlJobLogGlue.setGlueRemark(glueRemark);
@ -88,7 +92,7 @@ public class JobCodeController {
xxlJobLogGlueDao.save(xxlJobLogGlue);
// remove code backup more than 30
xxlJobLogGlueDao.removeOld(exists_jobInfo.getId(), 30);
xxlJobLogGlueDao.removeOld(existsJobInfo.getId(), 30);
return ReturnT.SUCCESS;
}

@ -1,6 +1,6 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.core.cron.CronExpression;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.exception.XxlJobException;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
@ -9,11 +9,8 @@ import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;
import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;
import com.xxl.job.admin.core.thread.JobScheduleHelper;
import com.xxl.job.admin.core.thread.JobTriggerPoolHelper;
import com.xxl.job.admin.core.trigger.TriggerTypeEnum;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.service.LoginService;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
@ -29,7 +26,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;
import java.util.*;
/**
@ -60,7 +56,7 @@ public class JobInfoController {
List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll();
// filter group
List<XxlJobGroup> jobGroupList = filterJobGroupByRole(request, jobGroupList_all);
List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
if (jobGroupList==null || jobGroupList.size()==0) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
}
@ -71,33 +67,6 @@ public class JobInfoController {
return "jobinfo/jobinfo.index";
}
public static List<XxlJobGroup> filterJobGroupByRole(HttpServletRequest request, List<XxlJobGroup> jobGroupList_all){
List<XxlJobGroup> jobGroupList = new ArrayList<>();
if (jobGroupList_all!=null && jobGroupList_all.size()>0) {
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
if (loginUser.getRole() == 1) {
jobGroupList = jobGroupList_all;
} else {
List<String> groupIdStrs = new ArrayList<>();
if (loginUser.getPermission()!=null && loginUser.getPermission().trim().length()>0) {
groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(","));
}
for (XxlJobGroup groupItem:jobGroupList_all) {
if (groupIdStrs.contains(String.valueOf(groupItem.getId()))) {
jobGroupList.add(groupItem);
}
}
}
}
return jobGroupList;
}
public static void validPermission(HttpServletRequest request, int jobGroup) {
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
if (!loginUser.validPermission(jobGroup)) {
throw new RuntimeException(I18nUtil.getString("system_permission_limit") + "[username="+ loginUser.getUsername() +"]");
}
}
@RequestMapping("/pageList")
@ResponseBody
public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int start,
@ -109,14 +78,24 @@ public class JobInfoController {
@RequestMapping("/add")
@ResponseBody
public ReturnT<String> add(XxlJobInfo jobInfo) {
return xxlJobService.add(jobInfo);
public ReturnT<String> add(HttpServletRequest request, XxlJobInfo jobInfo) {
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
// opt
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
return xxlJobService.add(jobInfo, loginUser);
}
@RequestMapping("/update")
@ResponseBody
public ReturnT<String> update(XxlJobInfo jobInfo) {
return xxlJobService.update(jobInfo);
public ReturnT<String> update(HttpServletRequest request, XxlJobInfo jobInfo) {
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
// opt
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
return xxlJobService.update(jobInfo, loginUser);
}
@RequestMapping("/remove")
@ -139,15 +118,11 @@ public class JobInfoController {
@RequestMapping("/trigger")
@ResponseBody
//@PermissionLimit(limit = false)
public ReturnT<String> triggerJob(int id, String executorParam, String addressList) {
// force cover job param
if (executorParam == null) {
executorParam = "";
}
JobTriggerPoolHelper.trigger(id, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList);
return ReturnT.SUCCESS;
public ReturnT<String> triggerJob(HttpServletRequest request, int id, String executorParam, String addressList) {
// login user
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
// trigger
return xxlJobService.trigger(loginUser, id, executorParam, addressList);
}
@RequestMapping("/nextTriggerTime")
@ -170,7 +145,7 @@ public class JobInfoController {
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
logger.error("nextTriggerTime error. scheduleType = {}, scheduleConf= {}", scheduleType, scheduleConf, e);
return new ReturnT<List<String>>(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) + e.getMessage());
}
return new ReturnT<List<String>>(result);

@ -1,5 +1,6 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.complete.XxlJobCompleter;
import com.xxl.job.admin.core.exception.XxlJobException;
import com.xxl.job.admin.core.model.XxlJobGroup;
@ -56,7 +57,7 @@ public class JobLogController {
List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll();
// filter group
List<XxlJobGroup> jobGroupList = JobInfoController.filterJobGroupByRole(request, jobGroupList_all);
List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
if (jobGroupList==null || jobGroupList.size()==0) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
}
@ -73,7 +74,7 @@ public class JobLogController {
model.addAttribute("jobInfo", jobInfo);
// valid permission
JobInfoController.validPermission(request, jobInfo.getJobGroup());
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
}
return "joblog/joblog.index";
@ -94,7 +95,7 @@ public class JobLogController {
int jobGroup, int jobId, int logStatus, String filterTime) {
// valid permission
JobInfoController.validPermission(request, jobGroup); // 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup
PermissionInterceptor.validJobGroupPermission(request, jobGroup); // 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup
// parse param
Date triggerTimeStart = null;
@ -206,8 +207,11 @@ public class JobLogController {
@RequestMapping("/clearLog")
@ResponseBody
public ReturnT<String> clearLog(int jobGroup, int jobId, int type){
public ReturnT<String> clearLog(HttpServletRequest request, int jobGroup, int jobId, int type){
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobGroup);
// opt
Date clearBeforeTime = null;
int clearBeforeNum = 0;
if (type == 1) {

@ -1,12 +1,12 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobUserDao;
import com.xxl.job.admin.service.LoginService;
import com.xxl.job.core.biz.model.ReturnT;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@ -27,7 +27,7 @@ import java.util.Map;
*/
@Controller
@RequestMapping("/user")
public class UserController {
public class JobUserController {
@Resource
private XxlJobUserDao xxlJobUserDao;
@ -112,7 +112,7 @@ public class UserController {
public ReturnT<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) {
// avoid opt login seft
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (loginUser.getUsername().equals(xxlJobUser.getUsername())) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit"));
}
@ -140,7 +140,7 @@ public class UserController {
public ReturnT<String> remove(HttpServletRequest request, int id) {
// avoid opt login seft
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (loginUser.getId() == id) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit"));
}
@ -151,11 +151,14 @@ public class UserController {
@RequestMapping("/updatePwd")
@ResponseBody
public ReturnT<String> updatePwd(HttpServletRequest request, String password){
public ReturnT<String> updatePwd(HttpServletRequest request, String password, String oldPassword){
// valid password
// valid
if (oldPassword==null || oldPassword.trim().length()==0){
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
}
if (password==null || password.trim().length()==0){
return new ReturnT<String>(ReturnT.FAIL.getCode(), "密码不可为空");
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
}
password = password.trim();
if (!(password.length()>=4 && password.length()<=20)) {
@ -163,13 +166,17 @@ public class UserController {
}
// md5 password
String md5OldPassword = DigestUtils.md5DigestAsHex(oldPassword.getBytes());
String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
// update pwd
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
// do write
// valid old pwd
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
XxlJobUser existUser = xxlJobUserDao.loadByUserName(loginUser.getUsername());
if (!md5OldPassword.equals(existUser.getPassword())) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("change_pwd_field_oldpwd") + I18nUtil.getString("system_unvalid"));
}
// write new
existUser.setPassword(md5Password);
xxlJobUserDao.update(existUser);

@ -1,9 +1,10 @@
package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.service.LoginService;
import com.xxl.job.admin.service.impl.LoginService;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
@ -11,6 +12,9 @@ import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
@ -50,10 +54,66 @@ public class PermissionInterceptor implements AsyncHandlerInterceptor {
if (needAdminuser && loginUser.getRole()!=1) {
throw new RuntimeException(I18nUtil.getString("system_permission_limit"));
}
request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);
request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser); // set loginUser, with request
}
return true; // proceed with the next interceptor
}
// -------------------- permission tool --------------------
/**
* get loginUser
*
* @param request
* @return
*/
public static XxlJobUser getLoginUser(HttpServletRequest request){
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); // get loginUser, with request
return loginUser;
}
/**
* valid permission by JobGroup
*
* @param request
* @param jobGroup
*/
public static void validJobGroupPermission(HttpServletRequest request, int jobGroup) {
XxlJobUser loginUser = getLoginUser(request);
if (!loginUser.validPermission(jobGroup)) {
throw new RuntimeException(I18nUtil.getString("system_permission_limit") + "[username="+ loginUser.getUsername() +"]");
}
}
/**
* filter XxlJobGroup by role
*
* @param request
* @param jobGroupList_all
* @return
*/
public static List<XxlJobGroup> filterJobGroupByRole(HttpServletRequest request, List<XxlJobGroup> jobGroupList_all){
List<XxlJobGroup> jobGroupList = new ArrayList<>();
if (jobGroupList_all!=null && jobGroupList_all.size()>0) {
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (loginUser.getRole() == 1) {
jobGroupList = jobGroupList_all;
} else {
List<String> groupIdStrs = new ArrayList<>();
if (loginUser.getPermission()!=null && loginUser.getPermission().trim().length()>0) {
groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(","));
}
for (XxlJobGroup groupItem:jobGroupList_all) {
if (groupIdStrs.contains(String.valueOf(groupItem.getId()))) {
jobGroupList.add(groupItem);
}
}
}
}
return jobGroupList;
}
}

@ -55,6 +55,10 @@ public class XxlJobCompleter {
String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
for (int i = 0; i < childJobIds.length; i++) {
int childJobId = (childJobIds[i]!=null && childJobIds[i].trim().length()>0 && isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1;
if (childJobId == xxlJobLog.getJobId()) {
logger.info("jobid {} is self, ignore it.", childJobId);
continue;
}
if (childJobId > 0) {
JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null);

@ -363,9 +363,9 @@ public final class CronExpression implements Serializable, Cloneable {
// the second immediately following it.
while (difference == 1000) {
newDate = getTimeAfter(lastDate);
if(newDate == null)
if(newDate == null) {
break;
}
difference = newDate.getTime() - lastDate.getTime();
if (difference == 1000) {
@ -668,8 +668,9 @@ public final class CronExpression implements Serializable, Cloneable {
if(c == '-') {
ValueSet vs = getValue(0, s, i+1);
lastdayOffset = vs.value;
if(lastdayOffset > 30)
throw new ParseException("Offset from last day must be <= 30", i+1);
if(lastdayOffset > 30) {
throw new ParseException("Offset from last day must be <= 30", i + 1);
}
i = vs.pos;
}
if(s.length() > i) {
@ -732,8 +733,9 @@ public final class CronExpression implements Serializable, Cloneable {
if (c == 'L') {
if (type == DAY_OF_WEEK) {
if(val < 1 || val > 7)
if(val < 1 || val > 7) {
throw new ParseException("Day-of-Week values must be between 1 and 7", -1);
}
lastdayOfWeek = true;
} else {
throw new ParseException("'L' option is not valid here. (pos=" + i + ")", i);
@ -750,8 +752,9 @@ public final class CronExpression implements Serializable, Cloneable {
} else {
throw new ParseException("'W' option is not valid here. (pos=" + i + ")", i);
}
if(val > 31)
throw new ParseException("The 'W' option does not make sense with values larger than 31 (max number of days in a month)", i);
if(val > 31) {
throw new ParseException("The 'W' option does not make sense with values larger than 31 (max number of days in a month)", i);
}
TreeSet<Integer> set = getSet(type);
set.add(val);
i++;

@ -65,7 +65,7 @@ public class JobCompleteHelper {
// wait for JobTriggerPoolHelper-init
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -92,7 +92,7 @@ public class JobCompleteHelper {
}
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
}
@ -100,7 +100,7 @@ public class JobCompleteHelper {
try {
TimeUnit.SECONDS.sleep(60);
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -127,7 +127,7 @@ public class JobCompleteHelper {
monitorThread.interrupt();
try {
monitorThread.join();
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}

@ -71,7 +71,7 @@ public class JobFailMonitorHelper {
}
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
}
@ -79,7 +79,7 @@ public class JobFailMonitorHelper {
try {
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -102,7 +102,7 @@ public class JobFailMonitorHelper {
monitorThread.interrupt();
try {
monitorThread.join();
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}

@ -87,7 +87,7 @@ public class JobLogReportHelper {
}
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job log report thread error:{}", e);
}
@ -121,7 +121,7 @@ public class JobLogReportHelper {
try {
TimeUnit.MINUTES.sleep(1);
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -144,7 +144,7 @@ public class JobLogReportHelper {
logrThread.interrupt();
try {
logrThread.join();
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}

@ -107,14 +107,14 @@ public class JobRegistryHelper {
XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);
}
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
}
}
try {
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
} catch (InterruptedException e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
}
@ -138,7 +138,7 @@ public class JobRegistryHelper {
registryMonitorThread.interrupt();
try {
registryMonitorThread.join();
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
@ -159,13 +159,19 @@ public class JobRegistryHelper {
registryOrRemoveThreadPool.execute(new Runnable() {
@Override
public void run() {
int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
// 0-fail; 1-save suc; 2-update suc;
int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySaveOrUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
if (ret == 1) {
// fresh (add)
freshGroupRegistryInfo(registryParam);
}
/*int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
if (ret < 1) {
XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
// fresh
freshGroupRegistryInfo(registryParam);
}
}*/
}
});
@ -187,7 +193,7 @@ public class JobRegistryHelper {
public void run() {
int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue());
if (ret > 0) {
// fresh
// fresh (delete)
freshGroupRegistryInfo(registryParam);
}
}

@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@ -44,7 +43,7 @@ public class JobScheduleHelper {
try {
TimeUnit.MILLISECONDS.sleep(5000 - System.currentTimeMillis()%1000 );
} catch (InterruptedException e) {
} catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
@ -150,7 +149,7 @@ public class JobScheduleHelper {
// tx stop
} catch (Exception e) {
} catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread error:{}", e);
}
@ -160,21 +159,21 @@ public class JobScheduleHelper {
if (conn != null) {
try {
conn.commit();
} catch (SQLException e) {
} catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
}
try {
conn.setAutoCommit(connAutoCommit);
} catch (SQLException e) {
} catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
}
try {
conn.close();
} catch (SQLException e) {
} catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
@ -185,7 +184,7 @@ public class JobScheduleHelper {
if (null != preparedStatement) {
try {
preparedStatement.close();
} catch (SQLException e) {
} catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
@ -200,7 +199,7 @@ public class JobScheduleHelper {
try {
// pre-read period: success > scan each second; fail > skip this period;
TimeUnit.MILLISECONDS.sleep((preReadSuc?1000:PRE_READ_MS) - System.currentTimeMillis()%1000);
} catch (InterruptedException e) {
} catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
@ -227,7 +226,7 @@ public class JobScheduleHelper {
// align second
try {
TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000);
} catch (InterruptedException e) {
} catch (Throwable e) {
if (!ringThreadToStop) {
logger.error(e.getMessage(), e);
}
@ -255,7 +254,7 @@ public class JobScheduleHelper {
// clear
ringItemData.clear();
}
} catch (Exception e) {
} catch (Throwable e) {
if (!ringThreadToStop) {
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread error:{}", e);
}
@ -269,17 +268,28 @@ public class JobScheduleHelper {
ringThread.start();
}
private void refreshNextValidTime(XxlJobInfo jobInfo, Date fromTime) throws Exception {
Date nextValidTime = generateNextValidTime(jobInfo, fromTime);
if (nextValidTime != null) {
jobInfo.setTriggerLastTime(jobInfo.getTriggerNextTime());
jobInfo.setTriggerNextTime(nextValidTime.getTime());
} else {
private void refreshNextValidTime(XxlJobInfo jobInfo, Date fromTime) {
try {
Date nextValidTime = generateNextValidTime(jobInfo, fromTime);
if (nextValidTime != null) {
jobInfo.setTriggerLastTime(jobInfo.getTriggerNextTime());
jobInfo.setTriggerNextTime(nextValidTime.getTime());
} else {
// generateNextValidTime fail, stop job
jobInfo.setTriggerStatus(0);
jobInfo.setTriggerLastTime(0);
jobInfo.setTriggerNextTime(0);
logger.error(">>>>>>>>>>> xxl-job, refreshNextValidTime fail for job: jobId={}, scheduleType={}, scheduleConf={}",
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf());
}
} catch (Throwable e) {
// generateNextValidTime error, stop job
jobInfo.setTriggerStatus(0);
jobInfo.setTriggerLastTime(0);
jobInfo.setTriggerNextTime(0);
logger.warn(">>>>>>>>>>> xxl-job, refreshNextValidTime fail for job: jobId={}, scheduleType={}, scheduleConf={}",
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf());
logger.error(">>>>>>>>>>> xxl-job, refreshNextValidTime error for job: jobId={}, scheduleType={}, scheduleConf={}",
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf(), e);
}
}
@ -301,7 +311,7 @@ public class JobScheduleHelper {
scheduleThreadToStop = true;
try {
TimeUnit.SECONDS.sleep(1); // wait
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
if (scheduleThread.getState() != Thread.State.TERMINATED){
@ -309,7 +319,7 @@ public class JobScheduleHelper {
scheduleThread.interrupt();
try {
scheduleThread.join();
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
@ -328,7 +338,7 @@ public class JobScheduleHelper {
if (hasRingData) {
try {
TimeUnit.SECONDS.sleep(8);
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
@ -337,7 +347,7 @@ public class JobScheduleHelper {
ringThreadToStop = true;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
if (ringThread.getState() != Thread.State.TERMINATED){
@ -345,7 +355,7 @@ public class JobScheduleHelper {
ringThread.interrupt();
try {
ringThread.join();
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}

@ -93,7 +93,7 @@ public class JobTriggerPoolHelper {
try {
// do trigger
XxlJobTrigger.trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList);
} catch (Exception e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
} finally {

@ -41,8 +41,21 @@ public interface XxlJobInfoDao {
public int findAllCount();
/**
* find schedule job, limit "trigger_status = 1"
*
* @param maxNextTime
* @param pagesize
* @return
*/
public List<XxlJobInfo> scheduleJobQuery(@Param("maxNextTime") long maxNextTime, @Param("pagesize") int pagesize );
/**
* update schedule job, limit "trigger_status = 1"avoid stopping tasks from being opened
*
* @param xxlJobInfo
* @return
*/
public int scheduleUpdate(XxlJobInfo xxlJobInfo);

@ -21,7 +21,12 @@ public interface XxlJobRegistryDao {
public List<XxlJobRegistry> findAll(@Param("timeout") int timeout,
@Param("nowTime") Date nowTime);
public int registryUpdate(@Param("registryGroup") String registryGroup,
public int registrySaveOrUpdate(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey,
@Param("registryValue") String registryValue,
@Param("updateTime") Date updateTime);
/*public int registryUpdate(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey,
@Param("registryValue") String registryValue,
@Param("updateTime") Date updateTime);
@ -29,7 +34,7 @@ public interface XxlJobRegistryDao {
public int registrySave(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey,
@Param("registryValue") String registryValue,
@Param("updateTime") Date updateTime);
@Param("updateTime") Date updateTime);*/
public int registryDelete(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey,

@ -2,6 +2,7 @@ package com.xxl.job.admin.service;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.core.biz.model.ReturnT;
import java.util.Date;
@ -33,7 +34,7 @@ public interface XxlJobService {
* @param jobInfo
* @return
*/
public ReturnT<String> add(XxlJobInfo jobInfo);
public ReturnT<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser);
/**
* update job
@ -41,7 +42,7 @@ public interface XxlJobService {
* @param jobInfo
* @return
*/
public ReturnT<String> update(XxlJobInfo jobInfo);
public ReturnT<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser);
/**
* remove job
@ -67,6 +68,17 @@ public interface XxlJobService {
*/
public ReturnT<String> stop(int id);
/**
* trigger
*
* @param loginUser
* @param jobId
* @param executorParam
* @param addressList
* @return
*/
public ReturnT<String> trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList);
/**
* dashboard info
*

@ -1,4 +1,4 @@
package com.xxl.job.admin.service;
package com.xxl.job.admin.service.impl;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.CookieUtil;
@ -6,7 +6,7 @@ import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.core.util.JacksonUtil;
import com.xxl.job.admin.dao.XxlJobUserDao;
import com.xxl.job.core.biz.model.ReturnT;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import javax.annotation.Resource;
@ -17,7 +17,7 @@ import java.math.BigInteger;
/**
* @author xuxueli 2019-05-04 22:13:264
*/
@Configuration
@Service
public class LoginService {
public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY";
@ -26,6 +26,8 @@ public class LoginService {
private XxlJobUserDao xxlJobUserDao;
// ---------------------- token tool ----------------------
private String makeToken(XxlJobUser xxlJobUser){
String tokenJson = JacksonUtil.writeValueAsString(xxlJobUser);
String tokenHex = new BigInteger(tokenJson.getBytes()).toString(16);
@ -41,6 +43,8 @@ public class LoginService {
}
// ---------------------- login tool, with cookie and db ----------------------
public ReturnT<String> login(HttpServletRequest request, HttpServletResponse response, String username, String password, boolean ifRemember){
// param

@ -4,10 +4,13 @@ import com.xxl.job.admin.core.cron.CronExpression;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLogReport;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;
import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;
import com.xxl.job.admin.core.thread.JobScheduleHelper;
import com.xxl.job.admin.core.thread.JobTriggerPoolHelper;
import com.xxl.job.admin.core.trigger.TriggerTypeEnum;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.*;
import com.xxl.job.admin.service.XxlJobService;
@ -58,7 +61,7 @@ public class XxlJobServiceImpl implements XxlJobService {
}
@Override
public ReturnT<String> add(XxlJobInfo jobInfo) {
public ReturnT<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser) {
// valid base
XxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup());
@ -128,6 +131,10 @@ public class XxlJobServiceImpl implements XxlJobService {
return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
}
if (!loginUser.validPermission(childJobInfo.getJobGroup())) {
return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
}
} else {
return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
@ -166,7 +173,7 @@ public class XxlJobServiceImpl implements XxlJobService {
}
@Override
public ReturnT<String> update(XxlJobInfo jobInfo) {
public ReturnT<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser) {
// valid base
if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) {
@ -215,11 +222,19 @@ public class XxlJobServiceImpl implements XxlJobService {
String[] childJobIds = jobInfo.getChildJobId().split(",");
for (String childJobIdItem: childJobIds) {
if (childJobIdItem!=null && childJobIdItem.trim().length()>0 && isNumeric(childJobIdItem)) {
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.parseInt(childJobIdItem));
int childJobId = Integer.parseInt(childJobIdItem);
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(childJobId);
if (childJobId == jobInfo.getId()) {
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_childJobId")+"("+childJobId+")"+I18nUtil.getString("system_unvalid")) );
}
if (childJobInfo==null) {
return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
}
if (!loginUser.validPermission(childJobInfo.getJobGroup())) {
return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
}
} else {
return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
@ -345,6 +360,42 @@ public class XxlJobServiceImpl implements XxlJobService {
return ReturnT.SUCCESS;
}
@Override
public ReturnT<String> trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList) {
// permission
if (loginUser == null) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("system_permission_limit"));
}
XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(jobId);
if (xxlJobInfo == null) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
if (!hasPermission(loginUser, xxlJobInfo.getJobGroup())) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("system_permission_limit"));
}
// force cover job param
if (executorParam == null) {
executorParam = "";
}
JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList);
return ReturnT.SUCCESS;
}
private boolean hasPermission(XxlJobUser loginUser, int jobGroup){
if (loginUser.getRole() == 1) {
return true;
}
List<String> groupIdStrs = new ArrayList<>();
if (loginUser.getPermission()!=null && loginUser.getPermission().trim().length()>0) {
groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(","));
}
return groupIdStrs.contains(String.valueOf(jobGroup));
}
@Override
public Map<String, Object> dashboardInfo() {

@ -17,6 +17,7 @@ spring.freemarker.suffix=.ftl
spring.freemarker.charset=UTF-8
spring.freemarker.request-context-attribute=request
spring.freemarker.settings.number_format=0.##########
spring.freemarker.settings.new_builtin_class_resolver=safer
### mybatis
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml

@ -1,6 +1,6 @@
admin_name=Scheduling Center
admin_name_full=Distributed Task Scheduling Platform XXL-JOB
admin_version=2.4.1-SNAPSHOT
admin_version=2.5.0-SNAPSHOT
admin_i18n=en
## system
@ -91,6 +91,7 @@ logout_fail=Logout fail
## change pwd
change_pwd=Change password
change_pwd_suc_to_logout=Change password successful, about to log out login
change_pwd_field_oldpwd=old password
change_pwd_field_newpwd=new password
## dashboard

@ -1,6 +1,6 @@
admin_name=任务调度中心
admin_name_full=分布式任务调度平台XXL-JOB
admin_version=2.4.1-SNAPSHOT
admin_version=2.5.0-SNAPSHOT
admin_i18n=
## system
@ -91,6 +91,7 @@ logout_fail=注销失败
## change pwd
change_pwd=修改密码
change_pwd_suc_to_logout=修改密码成功,即将注销登陆
change_pwd_field_oldpwd=旧密码
change_pwd_field_newpwd=新密码
## dashboard

@ -1,6 +1,6 @@
admin_name=任務調度中心
admin_name_full=分布式任務調度平臺XXL-JOB
admin_version=2.4.1-SNAPSHOT
admin_version=2.5.0-SNAPSHOT
admin_i18n=
## system
@ -91,6 +91,7 @@ logout_fail=登出失敗
## change pwd
change_pwd=修改密碼
change_pwd_suc_to_logout=修改密碼成功,即將登出
change_pwd_field_oldpwd=舊密碼
change_pwd_field_newpwd=新密碼
## dashboard

@ -232,9 +232,12 @@
UPDATE xxl_job_info
SET
trigger_last_time = #{triggerLastTime},
trigger_next_time = #{triggerNextTime},
trigger_status = #{triggerStatus}
trigger_next_time = #{triggerNextTime}
<if test="triggerStatus == 0">
, trigger_status = #{triggerStatus}
</if>
WHERE id = #{id}
AND trigger_status = 1
</update>
</mapper>
</mapper>

@ -178,9 +178,9 @@
<select id="findLogReport" resultType="java.util.Map" >
SELECT
COUNT(handle_code) triggerDayCount,
SUM(CASE WHEN (trigger_code in (0, 200) and handle_code = 0) then 1 else 0 end) as triggerDayCountRunning,
SUM(CASE WHEN handle_code = 200 then 1 else 0 end) as triggerDayCountSuc
COUNT(handle_code) "triggerDayCount",
SUM(CASE WHEN (trigger_code in (0, 200) and handle_code = 0) then 1 else 0 end) as "triggerDayCountRunning",
SUM(CASE WHEN handle_code = 200 then 1 else 0 end) as "triggerDayCountSuc"
FROM xxl_job_log
WHERE trigger_time BETWEEN #{from} and #{to}
</select>

@ -39,6 +39,14 @@
WHERE t.update_time <![CDATA[ > ]]> DATE_ADD(#{nowTime},INTERVAL -#{timeout} SECOND)
</select>
<insert id="registrySaveOrUpdate" >
INSERT INTO xxl_job_registry( `registry_group` , `registry_key` , `registry_value`, `update_time`)
VALUES( #{registryGroup} , #{registryKey} , #{registryValue}, #{updateTime})
ON DUPLICATE KEY UPDATE
`update_time` = #{updateTime}
</insert>
<!--
<update id="registryUpdate" >
UPDATE xxl_job_registry
SET `update_time` = #{updateTime}
@ -51,6 +59,7 @@
INSERT INTO xxl_job_registry( `registry_group` , `registry_key` , `registry_value`, `update_time`)
VALUES( #{registryGroup} , #{registryKey} , #{registryValue}, #{updateTime})
</insert>
-->
<delete id="registryDelete" >
DELETE FROM xxl_job_registry

@ -99,15 +99,23 @@ $(function(){
errorClass : 'help-block',
focusInvalid : true,
rules : {
oldPassword : {
required : true ,
rangelength:[4,20]
},
password : {
required : true ,
rangelength:[4,50]
rangelength:[4,20]
}
},
messages : {
oldPassword : {
required : I18n.system_please_input +I18n.change_pwd_field_oldpwd,
rangelength : "密码长度限制为4~20"
},
password : {
required : '' ,
rangelength : "密码长度限制为4~50"
required : I18n.system_please_input +I18n.change_pwd_field_newpwd,
rangelength : "密码长度限制为4~20"
}
},
highlight : function(element) {

@ -123,7 +123,7 @@ $(function() {
var id = $(this).attr("_id");
var row = tableData['key'+id];
var html = '<div>';
/*var html = '<div>';
if (row.registryList) {
for (var index in row.registryList) {
html += (parseInt(index)+1) + '. <span class="badge bg-green" >' + row.registryList[index] + '</span><br>';
@ -135,8 +135,19 @@ $(function() {
title: I18n.jobinfo_opt_registryinfo ,
btn: [ I18n.system_ok ],
content: html
});
});*/
var html = '<table class="table table-bordered"><tbody>';
if (row.registryList) {
for (var index in row.registryList) {
html += '<tr><th>' + (parseInt(index)+1) + '</th>';
html += '<th><span class="badge bg-green" >' + row.registryList[index] + '</span></th><tr>';
}
}
html += '</tbody></table>';
$('#showRegistryListModal .data').html(html);
$('#showRegistryListModal').modal({backdrop: false, keyboard: false}).modal('show');
});

@ -16,12 +16,12 @@ $(function(){
userName : {
required : true ,
minlength: 4,
maxlength: 18
maxlength: 20
},
password : {
required : true ,
minlength: 4,
maxlength: 18
maxlength: 20
}
},
messages : {
@ -32,7 +32,7 @@ $(function(){
password : {
required : I18n.login_password_empty ,
minlength : I18n.login_password_lt_4
/*,maxlength:"登录密码不应超过18位"*/
/*,maxlength:"登录密码不应超过20位"*/
}
},
highlight : function(element) {

@ -107,9 +107,13 @@
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.change_pwd_field_oldpwd}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="oldPassword" placeholder="${I18n.system_please_input} ${I18n.change_pwd_field_oldpwd}" maxlength="20" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.change_pwd_field_newpwd}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="password" placeholder="${I18n.system_please_input} ${I18n.change_pwd_field_newpwd}" maxlength="18" ></div>
<div class="col-sm-10"><input type="text" class="form-control" name="password" placeholder="${I18n.system_please_input} ${I18n.change_pwd_field_newpwd}" maxlength="20" ></div>
</div>
<hr>
<div class="form-group">

@ -70,6 +70,25 @@
</section>
</div>
<!-- . -->
<div class="modal fade" id="showRegistryListModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >${I18n.jobinfo_opt_registryinfo}</h4>
</div>
<div class="modal-body">
<div class="data" style="word-wrap: break-word;"></div>
</div>
<div class="modal-footer">
<div class="text-center" >
<button type="button" class="btn btn-info ok" data-dismiss="modal" >${I18n.system_ok}</button>
</div>
</div>
</div>
</div>
</div>
<!-- . -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog ">

@ -15,11 +15,11 @@
<div class="login-box-body">
<p class="login-box-msg">${I18n.admin_name}</p>
<div class="form-group has-feedback">
<input type="text" name="userName" class="form-control" placeholder="${I18n.login_username_placeholder}" maxlength="18" >
<input type="text" name="userName" class="form-control" placeholder="${I18n.login_username_placeholder}" maxlength="20" >
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input type="password" name="password" class="form-control" placeholder="${I18n.login_password_placeholder}" maxlength="18" >
<input type="password" name="password" class="form-control" placeholder="${I18n.login_password_placeholder}" maxlength="20" >
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row">

@ -1,6 +1,6 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.service.LoginService;
import com.xxl.job.admin.service.impl.LoginService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;

@ -8,6 +8,7 @@ import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class XxlJobRegistryDaoTest {
@ -17,14 +18,32 @@ public class XxlJobRegistryDaoTest {
@Test
public void test(){
int ret = xxlJobRegistryDao.registryUpdate("g1", "k1", "v1", new Date());
int ret = xxlJobRegistryDao.registrySaveOrUpdate("g1", "k1", "v1", new Date());
/*int ret = xxlJobRegistryDao.registryUpdate("g1", "k1", "v1", new Date());
if (ret < 1) {
ret = xxlJobRegistryDao.registrySave("g1", "k1", "v1", new Date());
}
}*/
List<XxlJobRegistry> list = xxlJobRegistryDao.findAll(1, new Date());
int ret2 = xxlJobRegistryDao.removeDead(Arrays.asList(1));
}
@Test
public void test2() throws InterruptedException {
for (int i = 0; i < 100; i++) {
new Thread(()->{
int ret = xxlJobRegistryDao.registrySaveOrUpdate("g1", "k1", "v1", new Date());
System.out.println(ret);
/*int ret = xxlJobRegistryDao.registryUpdate("g1", "k1", "v1", new Date());
if (ret < 1) {
ret = xxlJobRegistryDao.registrySave("g1", "k1", "v1", new Date());
}*/
}).start();
}
TimeUnit.SECONDS.sleep(10);
}
}

@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>2.4.1-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-core</artifactId>
<packaging>jar</packaging>

@ -89,14 +89,14 @@ public class EmbedServer {
} catch (InterruptedException e) {
logger.info(">>>>>>>>>>> xxl-job remoting server stop.");
} catch (Exception e) {
} catch (Throwable e) {
logger.error(">>>>>>>>>>> xxl-job remoting server error.", e);
} finally {
// stop
try {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
} catch (Exception e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
@ -200,7 +200,7 @@ public class EmbedServer {
default:
return new ReturnT<String>(ReturnT.FAIL_CODE, "invalid request, uri-mapping(" + uri + ") not found.");
}
} catch (Exception e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
return new ReturnT<String>(ReturnT.FAIL_CODE, "request error:" + ThrowableUtil.toString(e));
}

@ -53,12 +53,12 @@ public class ExecutorRegistryThread {
} else {
logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
}
} catch (Exception e) {
} catch (Throwable e) {
logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e);
}
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -69,7 +69,7 @@ public class ExecutorRegistryThread {
if (!toStop) {
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
}
} catch (InterruptedException e) {
} catch (Throwable e) {
if (!toStop) {
logger.warn(">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}", e.getMessage());
}
@ -89,7 +89,7 @@ public class ExecutorRegistryThread {
} else {
logger.info(">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.info(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}", registryParam, e);
}
@ -97,7 +97,7 @@ public class ExecutorRegistryThread {
}
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -119,7 +119,7 @@ public class ExecutorRegistryThread {
registryThread.interrupt();
try {
registryThread.join();
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}

@ -81,7 +81,7 @@ public class JobLogFileCleanThread {
}
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -90,7 +90,7 @@ public class JobLogFileCleanThread {
try {
TimeUnit.DAYS.sleep(1);
} catch (InterruptedException e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}

@ -36,7 +36,7 @@ public class JobThread extends Thread{
private String stopReason;
private boolean running = false; // if running job
private int idleTimes = 0; // idel times
private int idleTimes = 0; // idle times
public JobThread(int jobId, IJobHandler handler) {
@ -185,7 +185,7 @@ public class JobThread extends Thread{
} else {
if (idleTimes > 30) {
if(triggerQueue.size() == 0) { // avoid concurrent trigger causes jobId-lost
XxlJobExecutor.removeJobThread(jobId, "excutor idel times over limit.");
XxlJobExecutor.removeJobThread(jobId, "excutor idle times over limit.");
}
}
}

@ -76,7 +76,7 @@ public class TriggerCallbackThread {
doCallback(callbackParamList);
}
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -90,7 +90,7 @@ public class TriggerCallbackThread {
if (callbackParamList!=null && callbackParamList.size()>0) {
doCallback(callbackParamList);
}
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -111,7 +111,7 @@ public class TriggerCallbackThread {
while(!toStop){
try {
retryFailCallbackFile();
} catch (Exception e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -119,7 +119,7 @@ public class TriggerCallbackThread {
}
try {
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
} catch (InterruptedException e) {
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
@ -139,7 +139,7 @@ public class TriggerCallbackThread {
triggerCallbackThread.interrupt();
try {
triggerCallbackThread.join();
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
@ -149,7 +149,7 @@ public class TriggerCallbackThread {
triggerRetryCallbackThread.interrupt();
try {
triggerRetryCallbackThread.join();
} catch (InterruptedException e) {
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
@ -173,7 +173,7 @@ public class TriggerCallbackThread {
} else {
callbackLog(callbackParamList, "<br>----------- xxl-job job callback fail, callbackResult:" + callbackResult);
}
} catch (Exception e) {
} catch (Throwable e) {
callbackLog(callbackParamList, "<br>----------- xxl-job job callback error, errorMsg:" + e.getMessage());
}
}

@ -51,16 +51,18 @@ public class JdkSerializeTool {
* @return
*/
public static <T> Object deserialize(byte[] bytes, Class<T> clazz) {
ObjectInputStream ois = null;
ByteArrayInputStream bais = null;
try {
// 反序列化
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
try {
ois.close();
bais.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);

@ -1,11 +1,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>2.4.1-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-samples</artifactId>
<packaging>pom</packaging>
@ -15,4 +14,8 @@
<module>xxl-job-executor-sample-springboot</module>
</modules>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
</project>

@ -6,7 +6,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-executor-samples</artifactId>
<version>2.4.1-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-sample-frameless</artifactId>
<packaging>jar</packaging>
@ -21,7 +21,7 @@
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<artifactId>slf4j-reload4j</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<!-- junit -->

@ -6,7 +6,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-executor-samples</artifactId>
<version>2.4.1-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-sample-springboot</artifactId>
<packaging>jar</packaging>
@ -66,6 +66,9 @@
</goals>
</execution>
</executions>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>

Loading…
Cancel
Save