Merge pull request #1 from xuxueli/master

update from origin
v1.8.2
Mrwb 7 years ago committed by GitHub
commit 3fda544884

@ -0,0 +1,11 @@
Please answer some questions before submitting your issue. Thanks!
### Which version of XXL-JOB do you using?
### Expected behavior
### Actual behavior
### Steps to reproduce the behavior
### Other information

@ -0,0 +1,14 @@
**What kind of change does this PR introduce?** (check at least one)
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:
**The description of the PR:**
**Other information:**

@ -1,21 +1,48 @@
# 分布式任务调度平台XXL-JOB
[![Build Status](https://travis-ci.org/xuxueli/xxl-job.svg?branch=master)](https://travis-ci.org/xuxueli/xxl-job)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.xuxueli/xxl-job/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.xuxueli/xxl-job/)
[![GitHub release](https://img.shields.io/github/release/xuxueli/xxl-job.svg)](https://github.com/xuxueli/xxl-job/releases)
[![License](https://img.shields.io/badge/license-GPLv3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0.html)
[![Gitter](https://badges.gitter.im/xuxueli/xxl-job.svg)](https://gitter.im/xuxueli/xxl-job?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
<p align="center">
<a href="http://www.xuxueli.com/">
<img src="https://raw.githubusercontent.com/xuxueli/xxl-job/master/doc/images/xxl-logo.jpg" width="150">
</a>
<h3 align="center">XXL-JOB</h3>
<p align="center">
XXL-JOB, a lightweight distributed task scheduling framework.
<br>
<a href="http://www.xuxueli.com/"><strong>-- Browse xuxueli's website. --</strong></a>
<br>
<br>
<a href="https://travis-ci.org/xuxueli/xxl-job">
<img src="https://travis-ci.org/xuxueli/xxl-job.svg?branch=master" >
</a>
<a href="https://maven-badges.herokuapp.com/maven-central/com.xuxueli/xxl-job/">
<img src="https://maven-badges.herokuapp.com/maven-central/com.xuxueli/xxl-job/badge.svg" >
</a>
<a href="https://github.com/xuxueli/xxl-job/releases">
<img src="https://img.shields.io/github/release/xuxueli/xxl-job.svg" >
</a>
<a href="http://www.gnu.org/licenses/gpl-3.0.html">
<img src="https://img.shields.io/badge/license-GPLv3-blue.svg" >
</a>
<a href="https://gitter.im/xuxueli/xxl-job?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
<img src="https://badges.gitter.im/xuxueli/xxl-job.svg" >
</a>
</p>
</p>
## Intro
XXL-JOB is a lightweight distributed task scheduling framework.
It's core design goal is to develop quickly and learn simple, lightweight, and easy to expand.
Now, it's already open source, and many companies use it in production environments, real "out-of-the-box".
XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线开箱即用。
![输入图片说明](https://raw.githubusercontent.com/xuxueli/xxl-job/master/doc/images/xxl-logo.jpg "在这里输入图片标题")
### 文档
## Documentation
- [中文文档](https://github.com/xuxueli/xxl-job/blob/master/doc/XXL-JOB官方文档.md)
- [Englis Documentation](https://github.com/xuxueli/xxl-job/blob/master/doc/XXL-JOB-Englis-Documentation.md)
- 官方文档:[XXL-JOB官方文档](https://github.com/xuxueli/xxl-job/blob/master/doc/XXL-JOB官方文档.md)
### 特性
## Features
- 1、简单支持通过Web页面对任务进行CRUD操作操作简单一分钟上手
- 2、动态支持动态修改任务状态、暂停/恢复任务,以及终止运行中任务,即时生效;
- 3、调度中心HA中心式调度采用中心式设计“调度中心”基于集群Quartz实现可保证调度中心HA
@ -41,11 +68,8 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
- 23、分片广播任务执行器集群部署时任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发对应集群中所有执行器执行一次任务,同时传递分片参数;可根据分片参数开发分片任务;
- 24、动态分片分片广播任务以执行器为维度进行分片支持动态扩容执行器集群从而动态增加分片数量协同进行业务处理在进行大数据量业务操作时可显著提升任务处理能力和速度。
### 架构图
![输入图片说明](https://static.oschina.net/uploads/img/201707/17190028_aEE2.png "在这里输入图片标题")
### 发展
## Development
于2015年中我在github上创建XXL-JOB项目仓库并提交第一个commit随之进行系统结构设计UI选型交互设计……
于2015-11月XXL-JOB终于RELEASE了第一个大版本V1.0 随后我将之发布到OSCHINAXXL-JOB在OSCHINA上获得了@红薯的热门推荐同期分别达到了OSCHINA的“热门动弹”排行第一和git.oschina的开源软件月热度排行第一在此特别感谢红薯感谢大家的关注和支持。
@ -56,7 +80,7 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
于2017-05-13在上海举办的 "[第62期开源中国源创会](https://www.oschina.net/event/2236961)" 的 "放码过来" 环节我登台对XXL-JOB做了演讲台下五百位在场观众反响热烈[图文回顾](https://www.oschina.net/question/2686220_2242120) )。
#### 我司大众点评目前已接入XXL-JOB内部别名《Ferrari》Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。
** 我司大众点评目前已接入XXL-JOB内部别名《Ferrari》Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。**
据最新统计, 自2016-01-21接入至2017-07-07期间该系统已调度约60万余次表现优异。新接入应用推荐使用最新版本因为经过数个大版本的更新系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升核心功能更加稳定高效。
至今XXL-JOB已接入多家公司的线上产品线接入场景如电商业务O2O业务和大数据作业等截止2016-07-19为止XXL-JOB已接入的公司包括不限于
@ -94,60 +118,43 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
- 31、四川互宜达科技有限公司
- 32、钱包行云北京科技有限公司
- 33、重庆欣才集团
- 34、咪咕互动娱乐有限公司中国移动
- 35、北京诺亦腾科技有限公司
- 36、增长引擎(北京)信息技术有限公司
- ……
欢迎大家的关注和使用XXL-JOB也将拥抱变化持续发展。
### 下载
#### 源码仓库地址 (将会在两个git仓库同步发布最新代码)
源码仓库地址 | Release Download
--- | ---
[https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job) | [Download](https://github.com/xuxueli/xxl-job/releases)
[http://git.oschina.net/xuxueli0323/xxl-job](http://git.oschina.net/xuxueli0323/xxl-job) | [Download](http://git.oschina.net/xuxueli0323/xxl-job/releases)
#### 中央仓库地址 (最新Release版本1.8.0)
```
<!-- http://repo1.maven.org/maven2/com/xuxueli/xxl-job-core/ -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>1.8.0</version>
</dependency>
```
#### 博客地址 (将会在两个博客同步更新文档)
- [oschina地址](http://my.oschina.net/xuxueli/blog/690978)
- [cnblogs地址](http://www.cnblogs.com/xuxueli/p/5021979.html)
## Communication
#### 技术交流群 (仅作技术交流)
- 腾讯QQ群6399758605
- 腾讯QQ群5138274130 群即将满请加群6
- 腾讯QQ群4464762661 群即将满请加群6
- 腾讯QQ群3242151780 群即将满请加群6
- 腾讯QQ群2438249535 群即将满请加群6
- 腾讯QQ群1367260654 群即将满请加群6
- [Gitter](https://gitter.im/xuxueli/xxl-job)
- 群5138274130 [![image](http://pub.idqqimg.com/wpa/images/group.png)](http://shang.qq.com/wpa/qunwpa?idkey=a3f3aea7e5943e7a24e9726495747ddc19bccd3592d7a70ecb5a97b616062241 )
- 群4464762661 群即将满请加群5
- 群3242151780 群即将满请加群5
- 群2438249535 群即将满请加群5
- 群1367260654 群即将满请加群5
## Issue
如有问题可在 [Github Issues](https://github.com/xuxueli/xxl-job/issues/) 上提问,也可以加入上文技术交流群;
### 报告问题
XXL-JOB托管在Github上如有问题可在 [ISSUES](https://github.com/xuxueli/xxl-job/issues/) 上提问,也可以加入上文技术交流群;
## User Registration
登记仅为了产品推广,产品开源免费。
请接入使用的公司或个人进行用户登记 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 。
### 接入登记(登记仅为了推广,产品开源免费)
更多接入公司欢迎在github [登记](https://github.com/xuxueli/xxl-job/issues/1 )
### 开源协议
## Copyright and license
产品开源免费,并且将持续提供免费的社区技术支持。个人或企业内部可自由的接入和使用。
XXL-JOB采用GPLv3协议目的是为了保证用户的自由使用权利。协议可避免专利申请的特殊危险 "the GPL assures that patents cannot be used to render the program non-free.摘自GPLv3"。
Copyright (c) 2015-present, xuxueli.
---
### 支持的话可以扫一扫,支持 [XXL系列](https://github.com/xuxueli) 的建设:)
## Donate
支持的话可以扫一扫,请作者喝杯咖啡吧:)
微信:![输入图片说明](https://static.oschina.net/uploads/img/201707/07214300_qhxT.png "在这里输入图片标题")
支付宝:![输入图片说明](http://images2015.cnblogs.com/blog/554415/201605/554415-20160513183306234-1939652116.png "在这里输入图片标题")

@ -0,0 +1,11 @@
# 《A lightweight distributed task scheduling framework. "XXL-JOB"》
[![Build Status](https://travis-ci.org/xuxueli/xxl-job.svg?branch=master)](https://travis-ci.org/xuxueli/xxl-job)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.xuxueli/xxl-job/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.xuxueli/xxl-job/)
[![GitHub release](https://img.shields.io/github/release/xuxueli/xxl-job.svg)](https://github.com/xuxueli/xxl-job/releases)
[![License](https://img.shields.io/badge/license-GPLv3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0.html)
[![Gitter](https://badges.gitter.im/xuxueli/xxl-job.svg)](https://gitter.im/xuxueli/xxl-job?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
## 1、Intro
### 1.1 overview

@ -86,13 +86,16 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
- 31、四川互宜达科技有限公司
- 32、钱包行云北京科技有限公司
- 33、重庆欣才集团
- 34、咪咕互动娱乐有限公司中国移动
- 35、北京诺亦腾科技有限公司
- 36、增长引擎(北京)信息技术有限公司
- ……
欢迎大家的关注和使用XXL-JOB也将拥抱变化持续发展。
### 1.4 下载
#### 源码仓库地址 (将会在两个git仓库同步发布最新代码)
#### 源码仓库地址
源码仓库地址 | Release Download
--- | ---
@ -100,35 +103,36 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
[http://git.oschina.net/xuxueli0323/xxl-job](http://git.oschina.net/xuxueli0323/xxl-job) | [Download](http://git.oschina.net/xuxueli0323/xxl-job/releases)
#### 中央仓库地址 (最新Release版本1.8.0)
#### 中央仓库地址
```
<!-- http://repo1.maven.org/maven2/com/xuxueli/xxl-job-core/ -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>1.8.0</version>
<version>1.8.1</version>
</dependency>
```
#### 博客地址 (将会在两个博客同步更新文档)
#### 博客地址
- [oschina地址](http://my.oschina.net/xuxueli/blog/690978)
- [cnblogs地址](http://www.cnblogs.com/xuxueli/p/5021979.html)
#### 技术交流群 (仅作技术交流)
#### 技术交流
- 群5138274130 [![image](http://pub.idqqimg.com/wpa/images/group.png)](http://shang.qq.com/wpa/qunwpa?idkey=a3f3aea7e5943e7a24e9726495747ddc19bccd3592d7a70ecb5a97b616062241 )
- 群4464762661 群即将满请加群5
- 群3242151780 群即将满请加群5
- 群2438249535 群即将满请加群5
- 群1367260654 群即将满请加群5
- 腾讯QQ群6399758605
- 腾讯QQ群5138274130 群即将满请加群6
- 腾讯QQ群4464762661 群即将满请加群6
- 腾讯QQ群3242151780 群即将满请加群6
- 腾讯QQ群2438249535 群即将满请加群6
- 腾讯QQ群1367260654 群即将满请加群6
### 1.5 环境
- JDK1.7+
- Servlet/JSP Spec3.1/2.3
- Tomcat8.5.x/Jetty9.2
- Spring-boot1.3.8/Spring4.x
- Tomcat8.5.x/Jetty9.2.x
- Spring-boot1.5.x/Spring4.x
- Mysql5.6+
- Maven3+
@ -136,18 +140,18 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
## 二、快速入门
### 2.1 初始化“调度数据库”
请下载项目源码并解压,获取 "调度数据库初始化SQL脚本" 并执行即可。正常情况下应该生成16张表脚本文件位置为:
请下载项目源码并解压,获取 "调度数据库初始化SQL脚本" 并执行即可正常情况下应该生成16张表。
源码解压根目录\xxl-job\db\tables_xxl_job.sql
"调度数据库初始化SQL脚本" 位置为:
/xxl-job/db/tables_xxl_job.sql
调度中心支持集群部署集群情况下各节点务必连接同一个mysql实例;
如果mysql做主从,调度中心集群节点务必强制走主库;
### 2.2 编译源码
解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可源码结构如下图所示
![输入图片说明](https://static.oschina.net/uploads/img/201705/11214348_aGgr.png "在这里输入图片标题")
解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可源码结构如下
xxl-job-admin调度中心
xxl-job-core公共依赖
@ -160,14 +164,16 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
作用:统一管理任务调度平台上调度任务,负责触发调度执行。
#### 步骤一:调度中心配置:
配置文件以及配置属性如下图所示。
调度中心配置文件地址:
![输入图片说明](https://static.oschina.net/uploads/img/201705/11214752_Ifvp.png "在这里输入图片标题")
/xxl-job/xxl-job-admin/src/main/resources/xxl-job-admin.properties
调度中心配置内容说明:
### 调度中心JDBC链接链接地址请保持和 2.1章节 所创建的调度数据库的地址一致
xxl.job.db.driverClass=com.mysql.jdbc.Driver
xxl.job.db.url=jdbc:mysql://localhost:3306/xxl-job?useUnicode=true&amp;characterEncoding=UTF-8
xxl.job.db.url=jdbc:mysql://localhost:3306/xxl-job?useUnicode=true&characterEncoding=UTF-8
xxl.job.db.user=root
xxl.job.db.password=root_pwd
@ -179,9 +185,12 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
xxl.job.mail.sendFrom=ovono802302@163.com
xxl.job.mail.sendNick=《任务调度平台XXL-JOB》
# 登录账号
### 登录账号
xxl.job.login.username=admin
xxl.job.login.password=123456
### 调度中心通讯TOKEN非空时启用
xxl.job.accessToken=
#### 步骤二:部署项目:
如果已经正确进行上述配置可将项目编译打war包并部署到tomcat中。
@ -191,6 +200,13 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
至此“调度中心”项目已经部署成功。
#### 步骤三:调度中心集群(可选):
调度中心支持集群部署,提升调度系统可用性。
集群部署唯一要求为保证每个集群节点配置db和登陆账号等保持一致。调度中心通过db配置区分不同集群。
调度中心在集群部署时可通过nginx负载均衡此时可以为集群分配一个域名。该域名一方面可以用于访问另一方面也可以用于配置执行器回调地址。
### 2.4 配置部署“执行器项目”
“执行器”项目xxl-job-executor-example (如新建执行器项目可参考该Example执行器项目的配置步骤)
@ -200,10 +216,11 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
确认pom文件中引入了 "xxl-job-core" 的maven依赖
#### 步骤二:执行器配置
配置文件以及配置属性如下图所示。
执行器配置配置文件地址:
![输入图片说明](https://static.oschina.net/uploads/img/201705/11214800_7G3o.png "在这里输入图片标题")
/xxl-job/xxl-job-executor-example/src/main/resources/xxl-job-executor.properties
执行器配置配置内容说明:
### xxl-job admin address list调度中心部署跟地址如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调"。
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
@ -215,6 +232,9 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
### xxl-job log path执行器运行日志文件存储的磁盘位置
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
### xxl-job, access token执行器通讯TOKEN非空时启用
xxl.job.accessToken=
#### 步骤三:执行器组件配置
@ -225,7 +245,7 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
1、JobHandler 扫描路径自动扫描容器中JobHandler
2、执行器Excutor配置执行器核心配置
#### 步骤四:部署项目:
#### 步骤四:部署执行器项目:
如果已经正确进行上述配置可将执行器项目编译打部署系统提供两个执行器example项目选择其中一个即可各自的部署方式如下。
xxl-job-executor-example项目编译打包成WAR包并部署到tomcat中。
@ -233,6 +253,11 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
至此“执行器”项目已经部署结束。
#### 步骤五:执行器集群(可选):
执行器支持集群部署,提升调度系统可用性,同时提升任务处理能力。
集群部署唯一要求为:保证集群中每个执行器的配置项 "xxl.job.admin.addresses/调度中心地址" 保持一致,执行器根据该配置进行执行器自动注册等操作。
### 2.5 开发第一个任务“Hello World”
本示例以新建一个 “GLUE模式(Java)” 运行模式的任务为例。更多有关任务的详细配置,请查看“章节三:任务详解”。
@ -706,6 +731,16 @@ XXL-JOB会为每次调度请求生成一个单独的日志文件需要通过
- 1、分片任务场景10个执行器的集群来处理10w条数据每台机器只需要处理1w条数据耗时降低10倍
- 2、广播任务场景广播执行器机器运行shell脚本、广播集群节点进行缓存更新等
#### 5、10 访问令牌AccessToken
为提升系统安全性调度中心和执行器进行安全性校验双方AccessToken匹配才允许通讯
调度中心和执行器,可通过配置项 "xxl.job.accessToken" 进行AccessToken的设置。
调度中心和执行器,如果需要正常通讯,只有两种设置;
- 设置一调度中心和执行器均不设置AccessToken关闭安全性校验
- 设置二调度中心和执行器设置了相同的AccessToken
## 六、版本更新日志
#### 6.1 版本 V1.1.x新特性[2015-12-05]
@ -896,41 +931,49 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
- 10、执行日志支持根据运行 "状态" 筛选日志;
- 11、调度中心任务注册检测逻辑优化
#### 6.18 版本 V1.8.1 特性[快照版本]
#### 6.18 版本 V1.8.1 特性[2017-07-30]
- 1、分片广播任务执行器集群部署时任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发集群中所有执行器执行一次任务,可根据分片参数处理分片任务;
- 2、动态分片分片广播任务以执行器为维度进行分片支持动态扩容执行器集群从而动态增加分片数量协同进行业务处理在进行大数据量业务操作时可显著提升任务处理能力和速度。
- 3、执行器JobHandler禁止命名冲突
- 4、执行器集群地址列表进行自然排序
- 5、调度中心DAO层代码精简优化并且新增测试用例覆盖
- 6、调度中心API服务改为自研RPC形式统一底层通讯模型
- 7、新增调度中心API服务测试Demo方便在调度中心API扩展和测试
- 8、任务列表页交互优化更换执行器分组时自动刷新任务列表新建任务时默认定位在当前执行器位置
- 9、访问令牌accessToken为提升系统安全性调度中心和执行器进行安全性校验双方AccessToken匹配才允许通讯
- 10、springboot版本执行器升级至1.5.6.RELEASE版本
- 11、统一maven依赖版本管理
#### TODO LIST
- 1、任务权限管理执行器为粒度分配权限核心操作校验权限
- 2、任务分片路由分片采用一致性Hash算法计算出尽量稳定的分片顺序即使注册机器存在波动也不会引起分批分片顺序大的波动
- 3、失败重试优化目前失败重试逻辑为在本次调度请求失败后重新执行一次请求逻辑。优化点为针对调度和执行失败时均做失败重试重试时重新触发一次完整调度这将可能导致失败是调度死循环考虑中。
- 2、任务分片路由分片采用一致性Hash算法计算出尽量稳定的分片顺序即使注册机器存在波动也不会引起分批分片顺序大的波动目前采用IP自然排序可以满足需求待定
- 3、失败重试优化目前失败重试逻辑为在本次调度请求失败后重新执行一次请求逻辑。优化点为针对调度和执行失败时均做失败重试重试时重新触发一次完整调度这将可能导致失败是调度死循环待定
- 4、回调失败写文件查看日志时读文件确认重启后回调确认
- 5、任务依赖流程图子任务+会签任务,各节点日志;
- 6、调度任务优先级
- 7、移除quartz依赖重写调度模块新增或恢复任务时将下次执行记录插入delayqueue调度中心集群竞争分布式锁成功节点批量加载到期delayqueue数据批量执行。
- 8、任务执行结果回调失败后重试:待定,防止回调死循环
- 9、springboot 和 docker镜像并且推送docker镜像到中央仓库更进一步实现产品开箱即用
- 10、安全校验:调度中心和执行器约定公共密匙,只有密匙一致才允许相互通讯;
- 8、springboot 和 docker镜像并且推送docker镜像到中央仓库更进一步实现产品开箱即用
- 9、国际化:调度中心界面 + 官方文档,新增英文版本。
- 10、执行器摘除:执行器销毁时,主动通知调度中心并摘除对应执行器节点,提高执行器状态感知的时效性。
## 七、其他
#### 7.1 报告问题
XXL-JOB托管在Github上如有问题可在 [ISSUES](https://github.com/xuxueli/xxl-job/issues/) 上提问,也可以加入上文技术交流群;
#### 7.2 接入登记(登记仅为了推广,产品开源免费)
更多接入公司欢迎在github [登记](https://github.com/xuxueli/xxl-job/issues/1 )
#### 7.2 用户接入登记
登记仅为了产品推广,产品开源免费。
请接入使用的公司或个人进行用户登记 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 。
#### 7.3 开源协议
#### 7.3 开源协议和版权
产品开源免费,并且将持续提供免费的社区技术支持。个人或企业内部可自由的接入和使用。
XXL-JOB采用GPLv3协议目的是为了保证用户的自由使用权利。协议可避免专利申请的特殊危险 "the GPL assures that patents cannot be used to render the program non-free.摘自GPLv3"。
Copyright (c) 2015-present, xuxueli.
---
#### 支持的话可以扫一扫,支持 [XXL系列](https://github.com/xuxueli) 的建设:)
#### 捐赠
支持的话可以扫一扫,请作者喝杯咖啡吧:)
微信:![输入图片说明](https://static.oschina.net/uploads/img/201707/07214300_qhxT.png "在这里输入图片标题")
支付宝:![输入图片说明](http://images2015.cnblogs.com/blog/554415/201605/554415-20160513183306234-1939652116.png "在这里输入图片标题")

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.8.1-SNAPSHOT</version>
<version>1.8.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
@ -17,6 +17,37 @@
<module>xxl-job-executor-springboot-example</module>
</modules>
<properties>
<javax.servlet-api.version>3.0.1</javax.servlet-api.version>
<jsp-api.version>2.2</jsp-api.version>
<spring.version>3.2.18.RELEASE</spring.version>
<jackson-mapper-asl.version>1.9.13</jackson-mapper-asl.version>
<aspectjweaver.version>1.8.7</aspectjweaver.version>
<slf4j-api.version>1.7.25</slf4j-api.version>
<freemarker.version>2.3.20</freemarker.version>
<junit.version>4.11</junit.version>
<jetty-server.version>9.2.22.v20170606</jetty-server.version>
<hessian.version>4.0.38</hessian.version>
<httpclient.version>4.3.6</httpclient.version>
<commons-exec.version>1.3</commons-exec.version>
<commons-beanutils.version>1.9.2</commons-beanutils.version>
<commons-lang.version>2.6</commons-lang.version>
<c3p0.version>0.9.5.2</c3p0.version>
<mysql-connector-java.version>5.1.29</mysql-connector-java.version>
<mybatis-spring.version>1.2.2</mybatis-spring.version>
<mybatis.version>3.2.8</mybatis.version>
<groovy-all.version>2.4.5</groovy-all.version>
<mail.version>1.4.6</mail.version>
<quartz.version>2.3.0</quartz.version>
<spring-boot.version>1.5.6.RELEASE</spring-boot.version>
</properties>
<build>
<plugins>
<plugin>

@ -4,15 +4,11 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.8.1-SNAPSHOT</version>
<version>1.8.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-admin</artifactId>
<packaging>war</packaging>
<properties>
<spring.version>3.2.17.RELEASE</spring.version>
</properties>
<dependencies>
<!-- springframe start -->
<dependency>
@ -40,59 +36,59 @@
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
<version>${aspectjweaver.version}</version>
</dependency>
<!-- jackson (support spring json) -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
<version>${jackson-mapper-asl.version}</version>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
<version>${slf4j-api.version}</version>
</dependency>
<!-- freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
<version>${freemarker.version}</version>
</dependency>
<!-- commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
<version>${commons-beanutils.version}</version>
</dependency>
<!-- commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
<version>${commons-lang.version}</version>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<version>${javax.servlet-api.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<version>${jsp-api.version}</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
@ -100,45 +96,46 @@
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
<version>${c3p0.version}</version>
</dependency>
<!-- mysql-connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
<version>${mybatis-spring.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!-- mysql-connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
<version>${mybatis.version}</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
<version>${httpclient.version}</version>
</dependency>
<!-- javax.mail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.6</version>
<version>${mail.version}</version>
</dependency>
<!-- quartz quartz-2.2.3/c3p0-0.9.1.1/slf4j-api-1.6.6 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
<version>${quartz.version}</version>
</dependency>
<!-- xxl-job-core -->

@ -1,6 +1,7 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.rpc.codec.RpcRequest;
import com.xxl.job.core.rpc.codec.RpcResponse;
import com.xxl.job.core.rpc.netcom.NetComServerFactory;
@ -46,7 +47,7 @@ public class JobApiController {
}
}
@RequestMapping("/api")
@RequestMapping(AdminBiz.MAPPING)
@PermessionLimit(limit=false)
public void api(HttpServletRequest request, HttpServletResponse response) throws IOException {

@ -33,7 +33,7 @@ public class JobInfoController {
private XxlJobService xxlJobService;
@RequestMapping
public String index(Model model) {
public String index(Model model, @RequestParam(required = false, defaultValue = "-1") int jobGroup) {
// 枚举-字典
model.addAttribute("ExecutorRouteStrategyEnum", ExecutorRouteStrategyEnum.values()); // 路由策略-列表
@ -44,6 +44,8 @@ public class JobInfoController {
// 任务组
List<XxlJobGroup> jobGroupList = xxlJobGroupDao.findAll();
model.addAttribute("JobGroupList", jobGroupList);
model.addAttribute("jobGroup", jobGroup);
return "jobinfo/jobinfo.index";
}

@ -3,6 +3,7 @@ package com.xxl.job.admin.controller;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogDao;
@ -12,6 +13,8 @@ import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@ -32,6 +35,7 @@ import java.util.Map;
@Controller
@RequestMapping("/joblog")
public class JobLogController {
private static Logger logger = LoggerFactory.getLogger(JobLogController.class);
@Resource
private XxlJobGroupDao xxlJobGroupDao;
@ -116,7 +120,7 @@ public class JobLogController {
@ResponseBody
public ReturnT<LogResult> logDetailCat(String executorAddress, long triggerTime, int logId, int fromLineNum){
try {
ExecutorBiz executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, executorAddress).getObject();
ExecutorBiz executorBiz = XxlJobDynamicScheduler.getExecutorBiz(executorAddress);
ReturnT<LogResult> logResult = executorBiz.log(triggerTime, logId, fromLineNum);
// is end
@ -129,7 +133,7 @@ public class JobLogController {
return logResult;
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
return new ReturnT<LogResult>(ReturnT.FAIL_CODE, e.getMessage());
}
}
@ -148,14 +152,14 @@ public class JobLogController {
}
// request of kill
ExecutorBiz executorBiz = null;
ReturnT<String> runResult = null;
try {
executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, log.getExecutorAddress()).getObject();
ExecutorBiz executorBiz = XxlJobDynamicScheduler.getExecutorBiz(log.getExecutorAddress());
runResult = executorBiz.kill(jobInfo.getId());
} catch (Exception e) {
e.printStackTrace();
return new ReturnT<String>(500, e.getMessage());
logger.error(e.getMessage(), e);
runResult = new ReturnT<String>(500, e.getMessage());
}
ReturnT<String> runResult = executorBiz.kill(jobInfo.getId());
if (ReturnT.SUCCESS_CODE == runResult.getCode()) {
log.setHandleCode(ReturnT.FAIL_CODE);

@ -1,9 +1,7 @@
package com.xxl.job.admin.core.route;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -24,30 +22,4 @@ public abstract class ExecutorRouter {
*/
public abstract ReturnT<String> routeRun(TriggerParam triggerParam, ArrayList<String> addressList);
/**
* run executor
* @param triggerParam
* @param address
* @return ReturnT.content: final address
*/
public static ReturnT<String> runExecutor(TriggerParam triggerParam, String address){
ReturnT<String> runResult = null;
try {
ExecutorBiz executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address).getObject();
runResult = executorBiz.run(triggerParam);
} catch (Exception e) {
logger.error("", e);
runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
}
StringBuffer runResultSB = new StringBuffer("触发调度:");
runResultSB.append("<br>address").append(address);
runResultSB.append("<br>code").append(runResult.getCode());
runResultSB.append("<br>msg").append(runResult.getMsg());
runResult.setMsg(runResultSB.toString());
runResult.setContent(address);
return runResult;
}
}

@ -1,10 +1,11 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import java.util.ArrayList;
@ -25,7 +26,7 @@ public class ExecutorRouteBusyover extends ExecutorRouter {
// beat
ReturnT<String> idleBeatResult = null;
try {
ExecutorBiz executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address).getObject();
ExecutorBiz executorBiz = XxlJobDynamicScheduler.getExecutorBiz(address);
idleBeatResult = executorBiz.idleBeat(triggerParam.getJobId());
} catch (Exception e) {
logger.error(e.getMessage(), e);
@ -40,7 +41,7 @@ public class ExecutorRouteBusyover extends ExecutorRouter {
// beat success
if (idleBeatResult.getCode() == ReturnT.SUCCESS_CODE) {
ReturnT<String> runResult = runExecutor(triggerParam, address);
ReturnT<String> runResult = XxlJobTrigger.runExecutor(triggerParam, address);
idleBeatResultSB.append("<br><br>").append(runResult.getMsg());
// result

@ -1,6 +1,7 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
@ -82,7 +83,7 @@ public class ExecutorRouteConsistentHash extends ExecutorRouter {
String address = route(triggerParam.getJobId(), addressList);
// run executor
ReturnT<String> runResult = runExecutor(triggerParam, address);
ReturnT<String> runResult = XxlJobTrigger.runExecutor(triggerParam, address);
runResult.setContent(address);
return runResult;
}

@ -1,10 +1,11 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import java.util.ArrayList;
@ -25,7 +26,7 @@ public class ExecutorRouteFailover extends ExecutorRouter {
// beat
ReturnT<String> beatResult = null;
try {
ExecutorBiz executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address).getObject();
ExecutorBiz executorBiz = XxlJobDynamicScheduler.getExecutorBiz(address);
beatResult = executorBiz.beat();
} catch (Exception e) {
logger.error(e.getMessage(), e);
@ -40,7 +41,7 @@ public class ExecutorRouteFailover extends ExecutorRouter {
// beat success
if (beatResult.getCode() == ReturnT.SUCCESS_CODE) {
ReturnT<String> runResult = runExecutor(triggerParam, address);
ReturnT<String> runResult = XxlJobTrigger.runExecutor(triggerParam, address);
beatResultSB.append("<br><br>").append(runResult.getMsg());
// result

@ -1,7 +1,7 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
@ -23,7 +23,7 @@ public class ExecutorRouteFirst extends ExecutorRouter {
String address = route(triggerParam.getJobId(), addressList);
// run executor
ReturnT<String> runResult = runExecutor(triggerParam, address);
ReturnT<String> runResult = XxlJobTrigger.runExecutor(triggerParam, address);
runResult.setContent(address);
return runResult;
}

@ -1,6 +1,7 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
@ -62,7 +63,7 @@ public class ExecutorRouteLFU extends ExecutorRouter {
String address = route(triggerParam.getJobId(), addressList);
// run executor
ReturnT<String> runResult = runExecutor(triggerParam, address);
ReturnT<String> runResult = XxlJobTrigger.runExecutor(triggerParam, address);
runResult.setContent(address);
return runResult;
}

@ -1,6 +1,7 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
@ -61,7 +62,7 @@ public class ExecutorRouteLRU extends ExecutorRouter {
String address = route(triggerParam.getJobId(), addressList);
// run executor
ReturnT<String> runResult = runExecutor(triggerParam, address);
ReturnT<String> runResult = XxlJobTrigger.runExecutor(triggerParam, address);
runResult.setContent(address);
return runResult;
}

@ -1,7 +1,7 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
@ -22,7 +22,7 @@ public class ExecutorRouteLast extends ExecutorRouter {
String address = route(triggerParam.getJobId(), addressList);
// run executor
ReturnT<String> runResult = runExecutor(triggerParam, address);
ReturnT<String> runResult = XxlJobTrigger.runExecutor(triggerParam, address);
runResult.setContent(address);
return runResult;
}

@ -1,6 +1,7 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
@ -25,7 +26,7 @@ public class ExecutorRouteRandom extends ExecutorRouter {
String address = route(triggerParam.getJobId(), addressList);
// run executor
ReturnT<String> runResult = runExecutor(triggerParam, address);
ReturnT<String> runResult = XxlJobTrigger.runExecutor(triggerParam, address);
runResult.setContent(address);
return runResult;
}

@ -1,6 +1,7 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
@ -41,7 +42,7 @@ public class ExecutorRouteRound extends ExecutorRouter {
String address = route(triggerParam.getJobId(), addressList);
// run executor
ReturnT<String> runResult = runExecutor(triggerParam, address);
ReturnT<String> runResult = XxlJobTrigger.runExecutor(triggerParam, address);
runResult.setContent(address);
return runResult;
}

@ -9,62 +9,52 @@ import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogDao;
import com.xxl.job.admin.dao.XxlJobRegistryDao;
import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import com.xxl.job.core.rpc.netcom.NetComServerFactory;
import org.quartz.*;
import org.quartz.Trigger.TriggerState;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import java.util.*;
import java.util.Date;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
/**
* base quartz scheduler util
* @author xuxueli 2015-12-19 16:13:53
*/
public final class XxlJobDynamicScheduler implements ApplicationContextAware, InitializingBean {
public final class XxlJobDynamicScheduler implements ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(XxlJobDynamicScheduler.class);
// Scheduler
// ---------------------- param ----------------------
// scheduler
private static Scheduler scheduler;
public void setScheduler(Scheduler scheduler) {
XxlJobDynamicScheduler.scheduler = scheduler;
}
// init
public void init() throws Exception {
// admin registry monitor run
JobRegistryMonitorHelper.getInstance().start();
// admin monitor run
JobFailMonitorHelper.getInstance().start();
// rpc-service, base on spring-mvc
NetComServerFactory.putService(AdminBiz.class, XxlJobDynamicScheduler.adminBiz);
// accessToken
private static String accessToken;
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
// destroy
public void destroy(){
// admin registry stop
JobRegistryMonitorHelper.getInstance().toStop();
// admin monitor stop
JobFailMonitorHelper.getInstance().toStop();
}
// xxlJobLogDao、xxlJobInfoDao
// dao
public static XxlJobLogDao xxlJobLogDao;
public static XxlJobInfoDao xxlJobInfoDao;
public static XxlJobRegistryDao xxlJobRegistryDao;
public static XxlJobGroupDao xxlJobGroupDao;
public static AdminBiz adminBiz;
// ---------------------- applicationContext ----------------------
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
XxlJobDynamicScheduler.xxlJobLogDao = applicationContext.getBean(XxlJobLogDao.class);
@ -73,48 +63,60 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware, In
XxlJobDynamicScheduler.xxlJobGroupDao = applicationContext.getBean(XxlJobGroupDao.class);
XxlJobDynamicScheduler.adminBiz = applicationContext.getBean(AdminBiz.class);
}
@Override
public void afterPropertiesSet() throws Exception {
// ---------------------- init + destroy ----------------------
public void init() throws Exception {
// admin registry monitor run
JobRegistryMonitorHelper.getInstance().start();
// admin monitor run
JobFailMonitorHelper.getInstance().start();
// admin-server(spring-mvc)
NetComServerFactory.putService(AdminBiz.class, XxlJobDynamicScheduler.adminBiz);
NetComServerFactory.setAccessToken(accessToken);
// valid
Assert.notNull(scheduler, "quartz scheduler is null");
logger.info(">>>>>>>>> init quartz scheduler success.[{}]", scheduler);
}
// getJobKeys
@Deprecated
public static List<Map<String, Object>> getJobList(){
List<Map<String, Object>> jobList = new ArrayList<Map<String,Object>>();
try {
if (scheduler.getJobGroupNames()==null || scheduler.getJobGroupNames().size()==0) {
return null;
}
String groupName = scheduler.getJobGroupNames().get(0);
Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName));
if (jobKeys!=null && jobKeys.size()>0) {
for (JobKey jobKey : jobKeys) {
TriggerKey triggerKey = TriggerKey.triggerKey(jobKey.getName(), Scheduler.DEFAULT_GROUP);
Trigger trigger = scheduler.getTrigger(triggerKey);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
TriggerState triggerState = scheduler.getTriggerState(triggerKey);
Map<String, Object> jobMap = new HashMap<String, Object>();
jobMap.put("TriggerKey", triggerKey);
jobMap.put("Trigger", trigger);
jobMap.put("JobDetail", jobDetail);
jobMap.put("TriggerState", triggerState);
jobList.add(jobMap);
}
}
} catch (SchedulerException e) {
e.printStackTrace();
return null;
}
return jobList;
}
// fill job info
public void destroy(){
// admin registry stop
JobRegistryMonitorHelper.getInstance().toStop();
// admin monitor stop
JobFailMonitorHelper.getInstance().toStop();
}
// ---------------------- executor-client ----------------------
private static ConcurrentHashMap<String, ExecutorBiz> executorBizRepository = new ConcurrentHashMap<String, ExecutorBiz>();
public static ExecutorBiz getExecutorBiz(String address) throws Exception {
// valid
if (address==null || address.trim().length()==0) {
return null;
}
// load-cache
address = address.trim();
ExecutorBiz executorBiz = executorBizRepository.get(address);
if (executorBiz != null) {
return executorBiz;
}
// set-cache
executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address, accessToken).getObject();
executorBizRepository.put(address, executorBiz);
return executorBiz;
}
// ---------------------- schedule util ----------------------
/**
* fill job info
*
* @param jobInfo
*/
public static void fillJobInfo(XxlJobInfo jobInfo) {
// TriggerKey : name + group
String group = String.valueOf(jobInfo.getJobGroup());
@ -145,14 +147,28 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware, In
}
}
// check if exists
/**
* check if exists
*
* @param jobName
* @param jobGroup
* @return
* @throws SchedulerException
*/
public static boolean checkExists(String jobName, String jobGroup) throws SchedulerException{
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
return scheduler.checkExists(triggerKey);
}
// addJob 新增
@SuppressWarnings("unchecked")
/**
* addJob
*
* @param jobName
* @param jobGroup
* @param cronExpression
* @return
* @throws SchedulerException
*/
public static boolean addJob(String jobName, String jobGroup, String cronExpression) throws SchedulerException {
// TriggerKey : name + group
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
@ -185,7 +201,15 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware, In
return true;
}
// reschedule
/**
* rescheduleJob
*
* @param jobGroup
* @param jobName
* @param cronExpression
* @return
* @throws SchedulerException
*/
public static boolean rescheduleJob(String jobGroup, String jobName, String cronExpression) throws SchedulerException {
// TriggerKey valid if_exists
@ -234,7 +258,14 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware, In
return true;
}
// unscheduleJob
/**
* unscheduleJob
*
* @param jobName
* @param jobGroup
* @return
* @throws SchedulerException
*/
public static boolean removeJob(String jobName, String jobGroup) throws SchedulerException {
// TriggerKey : name + group
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
@ -246,7 +277,14 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware, In
return true;
}
// Pause
/**
* pause
*
* @param jobName
* @param jobGroup
* @return
* @throws SchedulerException
*/
public static boolean pauseJob(String jobName, String jobGroup) throws SchedulerException {
// TriggerKey : name + group
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
@ -262,7 +300,14 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware, In
return result;
}
// resume
/**
* resume
*
* @param jobName
* @param jobGroup
* @return
* @throws SchedulerException
*/
public static boolean resumeJob(String jobName, String jobGroup) throws SchedulerException {
// TriggerKey : name + group
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
@ -278,7 +323,14 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware, In
return result;
}
// run
/**
* run
*
* @param jobName
* @param jobGroup
* @return
* @throws SchedulerException
*/
public static boolean triggerJob(String jobName, String jobGroup) throws SchedulerException {
// TriggerKey : name + group
JobKey jobKey = new JobKey(jobName, jobGroup);
@ -294,5 +346,41 @@ public final class XxlJobDynamicScheduler implements ApplicationContextAware, In
return result;
}
/**
* finaAllJobList
*
* @return
*//*
@Deprecated
public static List<Map<String, Object>> finaAllJobList(){
List<Map<String, Object>> jobList = new ArrayList<Map<String,Object>>();
try {
if (scheduler.getJobGroupNames()==null || scheduler.getJobGroupNames().size()==0) {
return null;
}
String groupName = scheduler.getJobGroupNames().get(0);
Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName));
if (jobKeys!=null && jobKeys.size()>0) {
for (JobKey jobKey : jobKeys) {
TriggerKey triggerKey = TriggerKey.triggerKey(jobKey.getName(), Scheduler.DEFAULT_GROUP);
Trigger trigger = scheduler.getTrigger(triggerKey);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
TriggerState triggerState = scheduler.getTriggerState(triggerKey);
Map<String, Object> jobMap = new HashMap<String, Object>();
jobMap.put("TriggerKey", triggerKey);
jobMap.put("Trigger", trigger);
jobMap.put("JobDetail", jobDetail);
jobMap.put("TriggerState", triggerState);
jobList.add(jobMap);
}
}
} catch (SchedulerException e) {
e.printStackTrace();
return null;
}
return jobList;
}*/
}

@ -30,7 +30,7 @@ public class JobFailMonitorHelper {
private LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(0xfff8);
private Thread monitorThread;
private boolean toStop = false;
private volatile boolean toStop = false;
public void start(){
monitorThread = new Thread(new Runnable() {

@ -28,7 +28,7 @@ public class JobRegistryMonitorHelper {
}
private Thread registryThread;
private boolean toStop = false;
private volatile boolean toStop = false;
public void start(){
registryThread = new Thread(new Runnable() {
@Override

@ -5,9 +5,9 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.xxl.job.admin.core.thread.JobFailMonitorHelper;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
@ -90,12 +90,12 @@ public class XxlJobTrigger {
triggerParam.setBroadcastTotal(addressList.size()); // update02
// 4.2、trigger-run (route run / trigger remote executor)
triggerResult = ExecutorRouter.runExecutor(triggerParam, address); // update03
triggerResult = runExecutor(triggerParam, address); // update03
triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
// 4.3、trigger (fail retry)
if (triggerResult.getCode()!=ReturnT.SUCCESS_CODE && failStrategy == ExecutorFailStrategyEnum.FAIL_RETRY) {
triggerResult = ExecutorRouter.runExecutor(triggerParam, address); // update04
triggerResult = runExecutor(triggerParam, address); // update04
triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>失败重试<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
}
}
@ -179,4 +179,30 @@ public class XxlJobTrigger {
logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
}
/**
* run executor
* @param triggerParam
* @param address
* @return ReturnT.content: final address
*/
public static ReturnT<String> runExecutor(TriggerParam triggerParam, String address){
ReturnT<String> runResult = null;
try {
ExecutorBiz executorBiz = XxlJobDynamicScheduler.getExecutorBiz(address);
runResult = executorBiz.run(triggerParam);
} catch (Exception e) {
logger.error(e.getMessage(), e);
runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
}
StringBuffer runResultSB = new StringBuffer("触发调度:");
runResultSB.append("<br>address").append(address);
runResultSB.append("<br>code").append(runResult.getCode());
runResultSB.append("<br>msg").append(runResult.getMsg());
runResult.setMsg(runResultSB.toString());
runResult.setContent(address);
return runResult;
}
}

@ -65,6 +65,7 @@
<bean id="xxlJobDynamicScheduler" class="com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler" init-method="init" destroy-method="destroy" >
<!-- (轻易不要变更“调度器名称”, 任务创建时会绑定该“调度器名称”) -->
<property name="scheduler" ref="quartzScheduler"/>
<property name="accessToken" value="${xxl.job.accessToken}" />
</bean>
</beans>

@ -12,6 +12,9 @@ xxl.job.mail.password=asdfzxcv
xxl.job.mail.sendFrom=ovono802302@163.com
xxl.job.mail.sendNick=《任务调度平台XXL-JOB》
# xxl-job login
### xxl-job login
xxl.job.login.username=admin
xxl.job.login.password=123456
xxl.job.login.password=123456
### xxl-job, access token
xxl.job.accessToken=

@ -175,11 +175,12 @@
<#macro commonFooter >
<footer class="main-footer">
Powered by <b>XXL-JOB</b> 1.8.1()
Powered by <b>XXL-JOB</b> 1.8.2
<div class="pull-right hidden-xs">
<strong>Copyright &copy; 2015-${.now?string('yyyy')} &nbsp;
<a href="https://github.com/xuxueli/xxl-job" target="_blank" >github</a>&nbsp;
<a href="http://my.oschina.net/xuxueli/blog/690978" target="_blank" >oschina</a>
<a href="http://www.xuxueli.com/" target="_blank" >xuxueli</a>
&nbsp;
<a href="https://github.com/xuxueli/xxl-job" target="_blank" >github</a>
</strong><!-- All rights reserved. -->
</div>
</footer>

@ -31,33 +31,14 @@
<h4>XXL-JOB</h4>
<br>
<p>
<a target="_blank" href="https://github.com/xuxueli/xxl-job">github</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a target="_blank" href="http://www.xuxueli.com/">xuxueli</a>
<br><br>
<a target="_blank" href="https://github.com/xuxueli/xxl-job">github</a>&nbsp;&nbsp;&nbsp;&nbsp;
<iframe src="https://ghbtns.com/github-btn.html?user=xuxueli&repo=xxl-job&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px" style="margin-bottom:-5px;"></iframe>
<br><br>
<a target="_blank" href="http://my.oschina.net/xuxueli/blog/690978">oschina</a>
<br><br>
<a >5138274130</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=a3f3aea7e5943e7a24e9726495747ddc19bccd3592d7a70ecb5a97b616062241">
<img border="0" src="//pub.idqqimg.com/wpa/images/group.png" alt="《xxl-javaer》五群" title="《xxl-javaer》五群">
</a>
<br><br>
<a >4464762661</a>&nbsp;&nbsp;&nbsp;&nbsp;
5
<a target="_blank" href="http://my.oschina.net/xuxueli/blog/690978">oschina</a>
<br><br>
<a >3242151780</a>&nbsp;&nbsp;&nbsp;&nbsp;
5
<br><br>
<a >2438249535</a>&nbsp;&nbsp;&nbsp;&nbsp;
5
<br><br>
<a >1367260654</a>&nbsp;&nbsp;&nbsp;&nbsp;
5
</p>
<p></p>
</div>

@ -37,7 +37,7 @@
<span class="input-group-addon"></span>
<select class="form-control" id="jobGroup" >
<#list JobGroupList as group>
<option value="${group.id}" >${group.title}</option>
<option value="${group.id}" <#if jobGroup==group.id>selected</#if> >${group.title}</option>
</#list>
</select>
</div>
@ -110,7 +110,7 @@
<div class="col-sm-4">
<select class="form-control" name="jobGroup" >
<#list JobGroupList as group>
<option value="${group.id}" >${group.title}</option>
<option value="${group.id}" <#if jobGroup==group.id>selected</#if> >${group.title}</option>
</#list>
</select>
</div>

@ -167,6 +167,13 @@ $(function() {
jobTable.fnDraw();
});
// jobGroup change
$('#jobGroup').on('change', function(){
//reload
var jobGroup = $('#jobGroup').val();
window.location.href = base_url + "/jobinfo?jobGroup=" + jobGroup;
});
// job operate
$("#job_list").on('click', '.job_operate',function() {
var typeName;

@ -0,0 +1,32 @@
package com.xxl.job.dao.impl;
import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.model.RegistryParam;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.RegistryConfig;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import org.junit.Assert;
import org.junit.Test;
/**
* admin-api client, test
* @author xuxueli 2017-07-28 22:14:52
*/
public class AdminBizTest {
@Test
public void registryTest() throws Exception {
// admin-client
String addressUrl = "http://127.0.0.1:8080/xxl-job-admin".concat(AdminBiz.MAPPING);
String accessToken = null;
AdminBiz adminBiz = (AdminBiz) new NetComClientProxy(AdminBiz.class, addressUrl, accessToken).getObject();
// test executor registry
RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(), "xxl-job-executor-example", "127.0.0.1:9999");
ReturnT<String> returnT = adminBiz.registry(registryParam);
Assert.assertTrue(returnT.getCode() == ReturnT.SUCCESS_CODE);
}
}

@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.8.1-SNAPSHOT</version>
<version>1.8.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-core</artifactId>
<packaging>jar</packaging>
@ -13,56 +13,52 @@
<description>A lightweight distributed task scheduling framework.</description>
<url>http://www.xuxueli.com/</url>
<properties>
<spring.version>3.2.17.RELEASE</spring.version>
</properties>
<dependencies>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<version>${javax.servlet-api.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<version>${jsp-api.version}</version>
</dependency>
<!-- jetty -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.2.21.v20170120</version>
<version>${jetty-server.version}</version>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
<version>${slf4j-api.version}</version>
</dependency>
<!-- hessian -->
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.38</version>
<version>${hessian.version}</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
<version>${jackson-mapper-asl.version}</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
<version>${httpclient.version}</version>
</dependency>
<!-- spring-context -->
@ -76,14 +72,14 @@
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.5</version>
<version>${groovy-all.version}</version>
</dependency>
<!-- commons-exec -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
<version>${commons-exec.version}</version>
</dependency>
</dependencies>

@ -11,6 +11,8 @@ import java.util.List;
*/
public interface AdminBiz {
public static final String MAPPING = "/api";
/**
* callback
*

@ -1,36 +1,38 @@
package com.xxl.job.core.executor;
import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.impl.ExecutorBizImpl;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHander;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import com.xxl.job.core.rpc.netcom.NetComServerFactory;
import com.xxl.job.core.thread.ExecutorRegistryThread;
import com.xxl.job.core.thread.JobThread;
import com.xxl.job.core.thread.TriggerCallbackThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by xuxueli on 2016/3/2 21:14.
*/
public class XxlJobExecutor implements ApplicationContextAware, ApplicationListener {
public class XxlJobExecutor implements ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(XxlJobExecutor.class);
// ---------------------- param ----------------------
private String ip;
private int port = 9999;
private String appName;
public static String adminAddresses;
public static String logPath;
private String adminAddresses;
private String accessToken;
private String logPath;
public void setIp(String ip) {
this.ip = ip;
@ -44,48 +46,98 @@ public class XxlJobExecutor implements ApplicationContextAware, ApplicationListe
public void setAdminAddresses(String adminAddresses) {
this.adminAddresses = adminAddresses;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public void setLogPath(String logPath) {
this.logPath = logPath;
}
// ---------------------------------- job server ------------------------------------
private NetComServerFactory serverFactory = new NetComServerFactory();
public void start() throws Exception {
// executor start
NetComServerFactory.putService(ExecutorBiz.class, new ExecutorBizImpl()); // rpc-service, base on jetty
serverFactory.start(port, ip, appName);
// trigger callback thread start
TriggerCallbackThread.getInstance().start();
// ---------------------- applicationContext ----------------------
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void destroy(){
// 1、executor registry thread stop
ExecutorRegistryThread.getInstance().toStop();
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
// ---------------------- start + stop ----------------------
public void start() throws Exception {
// init admin-client
initAdminBizList(adminAddresses, accessToken);
// init executor-jobHandlerRepository
initJobHandlerRepository(applicationContext);
// 2、executor stop
serverFactory.destroy();
// init logpath
if (logPath!=null && logPath.trim().length()>0) {
XxlJobFileAppender.logPath = logPath;
}
// 3、job thread repository destory
// init executor-server
initExecutorServer(port, ip, appName, accessToken);
}
public void destroy(){
// destory JobThreadRepository
if (JobThreadRepository.size() > 0) {
for (Map.Entry<Integer, JobThread> item: JobThreadRepository.entrySet()) {
JobThread jobThread = item.getValue();
jobThread.toStop("Web容器销毁终止");
jobThread.interrupt();
removeJobThread(item.getKey(), "Web容器销毁终止");
}
JobThreadRepository.clear();
}
// 4、trigger callback thread stop
TriggerCallbackThread.getInstance().toStop();
// destory executor-server
stopExecutorServer();
}
// ---------------------- admin-client ----------------------
private static List<AdminBiz> adminBizList;
private static void initAdminBizList(String adminAddresses, String accessToken) throws Exception {
if (adminAddresses!=null && adminAddresses.trim().length()>0) {
for (String address: adminAddresses.trim().split(",")) {
if (address!=null && address.trim().length()>0) {
String addressUrl = address.concat(AdminBiz.MAPPING);
AdminBiz adminBiz = (AdminBiz) new NetComClientProxy(AdminBiz.class, addressUrl, accessToken).getObject();
if (adminBizList == null) {
adminBizList = new ArrayList<AdminBiz>();
}
adminBizList.add(adminBiz);
}
}
}
}
public static List<AdminBiz> getAdminBizList(){
return adminBizList;
}
// ---------------------- executor-server(jetty) ----------------------
private NetComServerFactory serverFactory = new NetComServerFactory();
private void initExecutorServer(int port, String ip, String appName, String accessToken) throws Exception {
NetComServerFactory.putService(ExecutorBiz.class, new ExecutorBizImpl()); // rpc-service, base on jetty
NetComServerFactory.setAccessToken(accessToken);
serverFactory.start(port, ip, appName); // jetty + registry
}
private void stopExecutorServer() {
serverFactory.destroy(); // jetty + registry + callback
}
// ---------------------------------- init job handler ------------------------------------
public static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
XxlJobExecutor.applicationContext = applicationContext;
// ---------------------- job handler repository ----------------------
private static ConcurrentHashMap<String, IJobHandler> jobHandlerRepository = new ConcurrentHashMap<String, IJobHandler>();
public static IJobHandler registJobHandler(String name, IJobHandler jobHandler){
logger.info("xxl-job register jobhandler success, name:{}, jobHandler:{}", name, jobHandler);
return jobHandlerRepository.put(name, jobHandler);
}
public static IJobHandler loadJobHandler(String name){
return jobHandlerRepository.get(name);
}
private static void initJobHandlerRepository(ApplicationContext applicationContext){
// init job handler action
Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHander.class);
@ -101,27 +153,10 @@ public class XxlJobExecutor implements ApplicationContextAware, ApplicationListe
}
}
}
}
// ---------------------------------- destory job executor ------------------------------------
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
if(applicationEvent instanceof ContextClosedEvent){
// TODO
}
}
// ---------------------------------- job handler repository
private static ConcurrentHashMap<String, IJobHandler> jobHandlerRepository = new ConcurrentHashMap<String, IJobHandler>();
public static IJobHandler registJobHandler(String name, IJobHandler jobHandler){
logger.info("xxl-job register jobhandler success, name:{}, jobHandler:{}", name, jobHandler);
return jobHandlerRepository.put(name, jobHandler);
}
public static IJobHandler loadJobHandler(String name){
return jobHandlerRepository.get(name);
}
// ---------------------------------- job thread repository
// ---------------------- job thread repository ----------------------
private static ConcurrentHashMap<Integer, JobThread> JobThreadRepository = new ConcurrentHashMap<Integer, JobThread>();
public static JobThread registJobThread(int jobId, IJobHandler handler, String removeOldReason){
JobThread newJobThread = new JobThread(jobId, handler);

@ -52,21 +52,21 @@ public class GlueFactory {
try {
Resource resource = AnnotationUtils.getAnnotation(field, Resource.class);
if (resource.name()!=null && resource.name().length()>0){
fieldBean = XxlJobExecutor.applicationContext.getBean(resource.name());
fieldBean = XxlJobExecutor.getApplicationContext().getBean(resource.name());
} else {
fieldBean = XxlJobExecutor.applicationContext.getBean(field.getName());
fieldBean = XxlJobExecutor.getApplicationContext().getBean(field.getName());
}
} catch (Exception e) {
}
if (fieldBean==null ) {
fieldBean = XxlJobExecutor.applicationContext.getBean(field.getType());
fieldBean = XxlJobExecutor.getApplicationContext().getBean(field.getType());
}
} else if (AnnotationUtils.getAnnotation(field, Autowired.class) != null) {
Qualifier qualifier = AnnotationUtils.getAnnotation(field, Qualifier.class);
if (qualifier!=null && qualifier.value()!=null && qualifier.value().length()>0) {
fieldBean = XxlJobExecutor.applicationContext.getBean(qualifier.value());
fieldBean = XxlJobExecutor.getApplicationContext().getBean(qualifier.value());
} else {
fieldBean = XxlJobExecutor.applicationContext.getBean(field.getType());
fieldBean = XxlJobExecutor.getApplicationContext().getBean(field.getType());
}
}

@ -1,7 +1,6 @@
package com.xxl.job.core.handler.impl;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.log.XxlJobFileAppender;
@ -37,17 +36,17 @@ public class ScriptJobHandler extends IJobHandler {
String scriptFileName = null;
if (GlueTypeEnum.GLUE_SHELL == glueType) {
cmd = "bash";
scriptFileName = XxlJobExecutor.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".sh");
scriptFileName = XxlJobFileAppender.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".sh");
} else if (GlueTypeEnum.GLUE_PYTHON == glueType) {
cmd = "python";
scriptFileName = XxlJobExecutor.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".py");
scriptFileName = XxlJobFileAppender.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".py");
}
// make script file
ScriptUtil.markScriptFile(scriptFileName, gluesource);
// log file
String logFileName = XxlJobExecutor.logPath.concat(XxlJobFileAppender.contextHolder.get());
String logFileName = XxlJobFileAppender.logPath.concat(XxlJobFileAppender.contextHolder.get());
// invoke
XxlJobLogger.log("----------- script file:"+ scriptFileName +" -----------");

@ -1,7 +1,6 @@
package com.xxl.job.core.log;
import com.xxl.job.core.biz.model.LogResult;
import com.xxl.job.core.executor.XxlJobExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -20,7 +19,8 @@ public class XxlJobFileAppender {
//public static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static final InheritableThreadLocal<String> contextHolder = new InheritableThreadLocal<String>();
public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public static String logPath = "/data/applogs/xxl-job/jobhandler/";
/**
* log filename: yyyy-MM-dd/9999.log
*
@ -31,7 +31,7 @@ public class XxlJobFileAppender {
public static String makeLogFileName(Date triggerDate, int logId) {
// filePath/
File filePathDir = new File(XxlJobExecutor.logPath);
File filePathDir = new File(logPath);
if (!filePathDir.exists()) {
filePathDir.mkdirs();
}
@ -66,7 +66,7 @@ public class XxlJobFileAppender {
if (logFileName==null || logFileName.trim().length()==0) {
return;
}
File logFile = new File(XxlJobExecutor.logPath, logFileName);
File logFile = new File(logPath, logFileName);
if (!logFile.exists()) {
try {
@ -111,7 +111,7 @@ public class XxlJobFileAppender {
if (logFileName==null || logFileName.trim().length()==0) {
return new LogResult(fromLineNum, 0, "readLog fail, logFile not found", true);
}
File logFile = new File(XxlJobExecutor.logPath, logFileName);
File logFile = new File(logPath, logFileName);
if (!logFile.exists()) {
return new LogResult(fromLineNum, 0, "readLog fail, logFile not exists", true);

@ -12,12 +12,14 @@ public class RpcRequest implements Serializable{
private String serverAddress;
private long createMillisTime;
private String accessToken;
private String className;
private String methodName;
private Class<?>[] parameterTypes;
private Object[] parameters;
public String getServerAddress() {
return serverAddress;
}
@ -29,41 +31,62 @@ public class RpcRequest implements Serializable{
public long getCreateMillisTime() {
return createMillisTime;
}
public void setCreateMillisTime(long createMillisTime) {
this.createMillisTime = createMillisTime;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Class<?>[] getParameterTypes() {
return parameterTypes;
}
public void setParameterTypes(Class<?>[] parameterTypes) {
this.parameterTypes = parameterTypes;
}
public Object[] getParameters() {
return parameters;
}
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
@Override
public String toString() {
return "NettyRequest [serverAddress=" + serverAddress + ", createMillisTime="
+ createMillisTime + ", className=" + className
+ ", methodName=" + methodName + ", parameterTypes="
+ Arrays.toString(parameterTypes) + ", parameters="
+ Arrays.toString(parameters) + "]";
return "RpcRequest{" +
"serverAddress='" + serverAddress + '\'' +
", createMillisTime=" + createMillisTime +
", accessToken='" + accessToken + '\'' +
", className='" + className + '\'' +
", methodName='" + methodName + '\'' +
", parameterTypes=" + Arrays.toString(parameterTypes) +
", parameters=" + Arrays.toString(parameters) +
'}';
}
}

@ -20,11 +20,13 @@ public class NetComClientProxy implements FactoryBean<Object> {
// ---------------------- config ----------------------
private Class<?> iface;
String serverAddress;
JettyClient client = new JettyClient();
public NetComClientProxy(Class<?> iface, String serverAddress) {
private String serverAddress;
private String accessToken;
private JettyClient client = new JettyClient();
public NetComClientProxy(Class<?> iface, String serverAddress, String accessToken) {
this.iface = iface;
this.serverAddress = serverAddress;
this.accessToken = accessToken;
}
@Override
@ -39,6 +41,7 @@ public class NetComClientProxy implements FactoryBean<Object> {
RpcRequest request = new RpcRequest();
request.setServerAddress(serverAddress);
request.setCreateMillisTime(System.currentTimeMillis());
request.setAccessToken(accessToken);
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameterTypes(method.getParameterTypes());

@ -30,14 +30,18 @@ public class NetComServerFactory {
server.destroy();
}
// ---------------------- server init ----------------------
// ---------------------- server instance ----------------------
/**
* init local rpc service map
*/
private static Map<String, Object> serviceMap = new HashMap<String, Object>();
private static String accessToken;
public static void putService(Class<?> iface, Object serviceBean){
serviceMap.put(iface.getName(), serviceBean);
}
public static void setAccessToken(String accessToken) {
NetComServerFactory.accessToken = accessToken;
}
public static RpcResponse invokeService(RpcRequest request, Object serviceBean) {
if (serviceBean==null) {
serviceBean = serviceMap.get(request.getClassName());
@ -49,7 +53,11 @@ public class NetComServerFactory {
RpcResponse response = new RpcResponse();
if (System.currentTimeMillis() - request.getCreateMillisTime() > 180000) {
response.setResult(new ReturnT<String>(ReturnT.FAIL_CODE, "the timestamp difference between admin and executor exceeds the limit."));
response.setResult(new ReturnT<String>(ReturnT.FAIL_CODE, "The timestamp difference between admin and executor exceeds the limit."));
return response;
}
if (accessToken!=null && accessToken.trim().length()>0 && !accessToken.trim().equals(request.getAccessToken())) {
response.setResult(new ReturnT<String>(ReturnT.FAIL_CODE, "The access token[" + request.getAccessToken() + "] is wrong."));
return response;
}

@ -21,8 +21,8 @@ public class JettyClient {
// reqURL
String reqURL = request.getServerAddress();
if (reqURL!=null && reqURL.toLowerCase().indexOf("http://")==-1) {
reqURL = "http://" + request.getServerAddress() + "/";
if (reqURL!=null && reqURL.toLowerCase().indexOf("http")==-1) {
reqURL = "http://" + request.getServerAddress() + "/"; // IP:PORT, need parse to url
}
// remote invoke

@ -1,6 +1,7 @@
package com.xxl.job.core.rpc.netcom.jetty.server;
import com.xxl.job.core.thread.ExecutorRegistryThread;
import com.xxl.job.core.thread.TriggerCallbackThread;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
@ -38,10 +39,16 @@ public class JettyServer {
server.setHandler(handlerc);
try {
// Start the server
// Start server
server.start();
logger.info(">>>>>>>>>>>> xxl-job jetty server start success at port:{}.", port);
// Start Registry-Server
ExecutorRegistryThread.getInstance().start(port, ip, appName);
// Start Callback-Server
TriggerCallbackThread.getInstance().start();
server.join(); // block until thread stopped
logger.info(">>>>>>>>>>> xxl-rpc server join success, netcon={}, port={}", JettyServer.class.getName(), port);
} catch (Exception e) {
@ -56,6 +63,8 @@ public class JettyServer {
}
public void destroy() {
// destroy server
if (server != null) {
try {
server.stop();
@ -67,6 +76,13 @@ public class JettyServer {
if (thread.isAlive()) {
thread.interrupt();
}
// destroy Registry-Server
ExecutorRegistryThread.getInstance().toStop();
// destroy Callback-Server
TriggerCallbackThread.getInstance().toStop();
logger.info(">>>>>>>>>>> xxl-rpc server destroy success, netcon={}", JettyServer.class.getName());
}

@ -5,7 +5,6 @@ import com.xxl.job.core.biz.model.RegistryParam;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.RegistryConfig;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import com.xxl.job.core.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -32,7 +31,7 @@ public class ExecutorRegistryThread extends Thread {
logger.warn(">>>>>>>>>>>> xxl-job, executor registry config fail, appName is null.");
return;
}
if (XxlJobExecutor.adminAddresses==null || XxlJobExecutor.adminAddresses.trim().length()==0) {
if (XxlJobExecutor.getAdminBizList() == null) {
logger.warn(">>>>>>>>>>>> xxl-job, executor registry config fail, adminAddresses is null.");
return;
}
@ -49,15 +48,10 @@ public class ExecutorRegistryThread extends Thread {
@Override
public void run() {
while (!toStop) {
try {
RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(), appName, executorAddress);
for (String addressUrl: XxlJobExecutor.adminAddresses.split(",")) {
String apiUrl = addressUrl.concat("/api");
for (AdminBiz adminBiz: XxlJobExecutor.getAdminBizList()) {
try {
AdminBiz adminBiz = (AdminBiz) new NetComClientProxy(AdminBiz.class, apiUrl).getObject();
ReturnT<String> registryResult = adminBiz.registry(registryParam);
if (registryResult!=null && ReturnT.SUCCESS_CODE == registryResult.getCode()) {
registryResult = ReturnT.SUCCESS;
@ -71,7 +65,6 @@ public class ExecutorRegistryThread extends Thread {
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}

@ -4,7 +4,6 @@ import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.model.HandleCallbackParam;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -43,17 +42,14 @@ public class TriggerCallbackThread {
callbackParamList.add(callback);
// valid
if (XxlJobExecutor.adminAddresses==null || XxlJobExecutor.adminAddresses.trim().length()==0) {
if (XxlJobExecutor.getAdminBizList()==null) {
logger.warn(">>>>>>>>>>>> xxl-job callback fail, adminAddresses is null, callbackParamList{}", callbackParamList);
continue;
}
// callback, will retry if error
for (String addressUrl: XxlJobExecutor.adminAddresses.split(",")) {
String apiUrl = addressUrl.concat("/api");
for (AdminBiz adminBiz: XxlJobExecutor.getAdminBizList()) {
try {
AdminBiz adminBiz = (AdminBiz) new NetComClientProxy(AdminBiz.class, apiUrl).getObject();
ReturnT<String> callbackResult = adminBiz.callback(callbackParamList);
if (callbackResult!=null && ReturnT.SUCCESS_CODE == callbackResult.getCode()) {
callbackResult = ReturnT.SUCCESS;

@ -1,6 +1,6 @@
package com.xxl.job.core.util;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.log.XxlJobFileAppender;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.PumpStreamHandler;
@ -28,7 +28,7 @@ public class ScriptUtil {
*/
public static void markScriptFile(String scriptFileName, String content) throws IOException {
// filePath/
File filePathDir = new File(XxlJobExecutor.logPath);
File filePathDir = new File(XxlJobFileAppender.logPath);
if (!filePathDir.exists()) {
filePathDir.mkdirs();
}

@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.8.1-SNAPSHOT</version>
<version>1.8.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-example</artifactId>
<packaging>war</packaging>
@ -13,12 +13,8 @@
<description>Executor project for spring boot.</description>
<url>http://www.xuxueli.com/</url>
<properties>
<spring.version>3.2.17.RELEASE</spring.version>
</properties>
<dependencies>
<!-- springframe start -->
<!-- spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
@ -29,20 +25,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- mysql-connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
<version>${slf4j-api.version}</version>
</dependency>
<!-- xxl-job-core -->
@ -51,7 +34,6 @@
<artifactId>xxl-job-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

@ -33,6 +33,8 @@
<property name="adminAddresses" value="${xxl.job.admin.addresses}" />
<!-- 执行器日志路径[必填] -->
<property name="logPath" value="${xxl.job.executor.logpath}" />
<!-- 访问令牌,非空则进行匹配校验[选填] -->
<property name="accessToken" value="${xxl.job.accessToken}" />
</bean>

@ -7,4 +7,7 @@ xxl.job.executor.ip=
xxl.job.executor.port=9999
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
### xxl-job, access token
xxl.job.accessToken=

@ -8,6 +8,8 @@ import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
/**
* executor-api client, test
*
* Created by xuxueli on 17/5/12.
*/
public class DemoJobHandlerTest {
@ -31,7 +33,8 @@ public class DemoJobHandlerTest {
triggerParam.setLogDateTim(System.currentTimeMillis());
// do remote trigger
ExecutorBiz executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, "127.0.0.1:9999").getObject();
String accessToken = null;
ExecutorBiz executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, "127.0.0.1:9999", null).getObject();
ReturnT<String> runResult = executorBiz.run(triggerParam);
}

@ -6,7 +6,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.8.1-SNAPSHOT</version>
<version>1.8.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-springboot-example</artifactId>
<packaging>jar</packaging>
@ -16,8 +16,6 @@
<url>http://www.xuxueli.com/</url>
<properties>
<spring-boot.version>1.3.8.RELEASE</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.7</java.version>
@ -33,41 +31,44 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- jetty -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty-server.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty-server.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>${jetty-server.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>${jetty-server.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- spring-boot-starter-web (提供了对web的支持包含了spring webmvc和tomcat等web开发的特性) -->
<!-- spring-boot-starter-web (spring-webmvc + tomcat) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- mysql-connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
</dependency>
<!-- xxl-job-core -->
<dependency>
<groupId>com.xuxueli</groupId>

@ -34,6 +34,8 @@ public class XxlJobConfig {
@Value("${xxl.job.executor.logpath}")
private String logpath;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobExecutor xxlJobExecutor() {
@ -44,6 +46,7 @@ public class XxlJobConfig {
xxlJobExecutor.setAppName(appname);
xxlJobExecutor.setAdminAddresses(addresses);
xxlJobExecutor.setLogPath(logpath);
xxlJobExecutor.setAccessToken(accessToken);
return xxlJobExecutor;
}

@ -15,3 +15,6 @@ xxl.job.executor.port=9998
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
### xxl-job, access token
xxl.job.accessToken=

Loading…
Cancel
Save