Merge branch 'master' into master

This commit is contained in:
许雪里 2020-03-27 12:17:37 +08:00 committed by GitHub
commit d0f5c1f6ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 873 additions and 438 deletions

View File

@ -408,6 +408,24 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
- 287、F5未来商店
- 288、深圳环阳通信息技术有限公司
- 289、遠傳電信
- 290、作业帮北京教育科技有限公司【作业帮】
- 291、成都科鸿智信科技有限公司
- 292、北京木屋时代科技有限公司
- 293、大学通哈尔滨科技有限责任公司
- 294、浙江华坤道威数据科技有限公司
- 295、吉祥航空【吉祥航空】
- 296、南京圆周网络科技有限公司
- 297、广州市洋葱omall电子商务
- 298、天津联物科技有限公司
- 299、跑哪儿科技北京有限公司
- 300、深圳市美西西餐饮有限公司(喜茶)
- 301、平安不动产有限公司【平安】
- 302、江苏中海昇物联科技有限公司
- 303、湖南牙医帮科技有限公司
- 304、重庆民航凯亚信息技术有限公司易通航
- 305、递易上海智能科技有限公司
- 306、亚朵
- 307、浙江新课堂教育股份有限公司
- ……
> 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。

View File

@ -379,6 +379,24 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
- 287、F5未来商店
- 288、深圳环阳通信息技术有限公司
- 289、遠傳電信
- 290、作业帮北京教育科技有限公司【作业帮】
- 291、成都科鸿智信科技有限公司
- 292、北京木屋时代科技有限公司
- 293、大学通哈尔滨科技有限责任公司
- 294、浙江华坤道威数据科技有限公司
- 295、吉祥航空【吉祥航空】
- 296、南京圆周网络科技有限公司
- 297、广州市洋葱omall电子商务
- 298、天津联物科技有限公司
- 299、跑哪儿科技北京有限公司
- 300、深圳市美西西餐饮有限公司(喜茶)
- 301、平安不动产有限公司【平安】
- 302、江苏中海昇物联科技有限公司
- 303、湖南牙医帮科技有限公司
- 304、重庆民航凯亚信息技术有限公司易通航
- 305、递易上海智能科技有限公司
- 306、亚朵
- 307、浙江新课堂教育股份有限公司
- ……
> 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
@ -415,7 +433,7 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
### 1.6 环境
- Maven3+
- Jdk1.7+
- Jdk1.8+
- Mysql5.7+
@ -478,8 +496,8 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
### 调度中心通讯TOKEN [选填]:非空时启用;
xxl.job.accessToken=
### 调度中心国际化配置 [选填] 默认为空,表示中文; "en" 表示英文;
xxl.job.i18n=
### 调度中心国际化配置 [必填] 默认为 "zh_CN"/中文简体, 可选范围为 "zh_CN"/中文简体, "zh_TC"/中文繁体 and "en"/英文;
xxl.job.i18n=zh_CN
## 调度线程池最大线程配置【必填】
xxl.job.triggerpool.fast.max=200
@ -1245,7 +1263,7 @@ API服务请求参考代码com.xxl.job.executor.ExecutorBizTest
- 2、提供基于HTTP的任务HandlerBean任务JobHandler="HttpJobHandler"业务方只需要提供HTTP链接即可不限制语言、平台
### 5.18 任务失败告警
默认提供邮件失败告警,可扩展短信、钉钉等方式,扩展代码位置为 "JobFailMonitorHelper.failAlarm"
默认提供邮件失败告警,可扩展短信、钉钉等方式。如果需要新增一种告警方式,只需要新增一个实现 "com.xxl.job.admin.core.alarm.JobAlarm" 接口的告警实现即可。可以参考默认提供邮箱告警实现 "EmailJobAlarm"。
### 5.19 调度中心Docker镜像构建
可以通过以下命令快速构建调度中心,并启动运行;
@ -1686,46 +1704,61 @@ public ReturnT<String> execute(String param) {
- 注意:最新版本 "XxlJobSpringExecutor" 逻辑有调整历史项目中该组件的配置方式请参考Sample示例项目进行调整尤其注意需要移除组件的init和destroy方法
### 6.28 版本 v2.2.0 Release Notes[迭代中]
- 1、[迭代中]调度中心升级springboot2.x因此系统要求JDK8+
- 1、调度中心升级springboot2.x因此系统要求JDK8+
- 2、执行器注册默认值优化
- 3、SQL脚本编码默认utf8mb4执行避免小概率下容器环境中乱码问题
- 4、多个项目依赖升级至较新稳定版本如mybatis、groovy和mysql驱动等
- 5、默认数据库连接池调整为hikari移除tomcat-jdbc依赖
- 6、任务告警组件模块化如果需要新增一种告警方式只需要新增一个实现 "com.xxl.job.admin.core.alarm.JobAlarm" 接口的告警实现即可,更加灵活、方便定制;
- 7、执行器XxlJob注解扫描逻辑优化修复任务为空时小概率NPE问题
- 8、Web IDE交互问题修复输入源码备注之后按回车跳转error问题处理
- 9、调度中心国际化完善新增 "中文繁体" 支持。默认为 "zh_CN"/中文简体, 可选范围为 "zh_CN"/中文简体, "zh_TC"/中文繁体 and "en"/英文;
- 10、移除旧类注解JobHandler推荐使用基于方法注解 "@XxlJob" 的方式进行任务开发;(如需保留类注解JobHandler使用方式可以参考旧版逻辑定制开发);
- 11、修复bootstrap.min.css.map 404问题
- 12、[迭代中]自定义失败重试时间间隔;
- 13、[迭代中]任务复制功能;点击复制是弹出新建任务弹框,并初始化被复制任务信息;
- 14、[迭代中]新增执行器描述、任务描述属性;
- 15、[迭代中]任务执行一次的时候指定IP
- 16、[迭代中]任务日志支持单个清理和状态转移,方便触发子任务;
- 17、[迭代中]任务结果丢失处理:针对长期处于运行中的任务(设置过期时间时,运行超过"过期时间+1min";未设置超时时间时,运行超过"30min"),主动检测该执行器是否在线,如果不在线主动标记失败;
- 18、[迭代中]优雅停机回调丢失问题修复;
### TODO LIST
- 1、任务分片路由分片采用一致性Hash算法计算出尽量稳定的分片顺序即使注册机器存在波动也不会引起分批分片顺序大的波动目前采用IP自然排序可以满足需求待定
- 2、任务单机多线程提升任务单机并行处理能力
- 2、调度隔离:调度中心针对不同执行器,各自维护不同的调度和远程触发组件。
- 3、调度任务优先级
- 4、多数据库支持在重写并移除Quartz的基础上DAO层通过JPA实现不限制数据库类型
- 4、多数据库支持DAO层通过JPA实现不限制数据库类型
- 5、执行器Log清理功能调度中心Log删除时同步删除执行器中的Log文件
- 6、任务自动注册Bean模式任务JobHandler自动从执行器中查询展示为下拉框选择后自动填充任务名称等属性待考虑因为任务自动注册将会导致任务难以管理控制
- 7、API事件触发类型任务更类似MQ消息支持"动态传参、延时消费"该类型任务不走调度组件单独建立MQ消息表调度中心竞争触发待定该功能与 XXL-MQ 冲突,该场景建议用后者;
- 8、调度线程池改为协程方式实现大幅降低系统内存消耗
- 9、任务、执行器数据全量本地缓存新增消息表广播通知
- 10、忙碌转移优化全部机器忙碌时不再直接失败
- 11、失败重试间隔
- 12、SimpleTrigger除Cron外支持设置固定时间间隔触发
- 13、调度日志列表加上执行时长列并支持排序
- 14、DAG流程任务替换子任务支持参数传递配置并列的"a-b、b-c"路径列表构成串行、并行、dag任务流程"dagre-d3"绘图;任务依赖,流程图,子任务+会签任务,各节点日志;支持根据成功、失败选择分支;
- 15、日期过滤支持多个时间段排除
- 16、告警邮件内容支持自定义模板配置
- 17、暂停状态支持Cron 为空;
- 18、新增任务运行模式 "GLUE模式(GO) "支持GO任务
- 19、注册中心优化实时性注册发现心跳注册间隔10srefresh失败则首次注册并立即更新注册信息心跳类似30s过期销毁
- 20、提供执行器Docker镜像
- 21、脚本任务支持数据参数新版本仅支持单参数不支持需要兼容
- 22、GLUE 模式 Web Ide 版本对比功能;
- 23、批量调度调度请求入queue调度线程批量获取调度请求并发起远程调度提高线程效率
- 24、多语言执行器约定跨语言通讯方案以及通讯接口
- 25、移除commons-exec采用原生实现
- 26、调度中心JDK版本调整为JDK8从而升级至最新版本SpringBoot
- 27、执行器服务端口与注册端口分离支持docker动态随机端口
- 28、执行器端口复用复用容器端口提供通讯服务
- 29、自定义失败重试时间间隔
- 30、分片任务全部成功后触发子任务
- 31、任务复制功能点击复制是弹出新建任务弹框并初始化被复制任务信息
- 32、AccessToken按照执行器维度设置控制调度、回调
- 33、任务执行一次的时候指定IP
- 34、通讯调整双向HTTP回调和其他API自定义AccessTokenRestful执行器复用容器端口
- 35、父子任务参数传递流程任务等透传动态参数
- 36、新增执行器描述、任务描述属性
- 6、延时任务API触发支持"动态传参、延时消费";该功能与 XXL-MQ 冲突,该场景建议用后者;
- 7、调度线程池改为协程方式实现大幅降低系统内存消耗
- 8、任务、执行器数据全量本地缓存新增消息表广播通知
- 9、忙碌转移优化全部机器忙碌时不再直接失败
- 10、任务触发参数优化支持选择 "Cron触发"、"固定间隔时间触发"、"指定时间点触发"、"不选择" 等;
- 11、调度日志列表加上执行时长列并支持排序
- 12、DAG流程任务替换子任务支持参数传递共享数据配置并列的"a-b、b-c"路径列表构成串行、并行、dag任务流程"dagre-d3"绘图;任务依赖,流程图,子任务+会签任务,各节点日志;支持根据成功、失败选择分支;
- 13、日期过滤支持多个时间段排除
- 14、告警增强
- 邮件告警:支持自定义标题、模板格式;
- webhook告警支持自定义告警URL、请求体格式
- 15、新增任务运行模式 "GLUE模式(GO) "支持GO任务
- 16、GLUE 模式 Web Ide 版本对比功能;
- 17、注册中心优化实时性注册发现心跳注册间隔10srefresh失败则首次注册并立即更新注册信息心跳类似30s过期销毁
- 18、提供执行器Docker镜像
- 19、脚本任务支持数据参数新版本仅支持单参数不支持需要兼容
- 20、批量调度调度请求入queue调度线程批量获取调度请求并发起远程调度提高线程效率
- 21、多语言执行器约定跨语言通讯方案以及通讯接口
- 22、架构升级XXL-JOB演进成一种任务调度规范和协议。默认双向Restful通讯。
- 调度中心API
- 说明:提供给执行器使用,不局限于官方执行器;第三方可据此开发业务自己的执行器;
- API列表执行器注册相关、任务回调相关
- 执行器API
- 说明:官方执行器,接收调度中心请求,异步回调任务结果;
- API列表心跳服务、任务触发、任务终止、任务日志查询
- 23、执行器服务端口与注册端口分离支持docker动态随机端口
- 24、执行器端口复用复用容器端口提供通讯服务
- 25、分片任务全部成功后触发子任务
- 26、AccessToken按照执行器维度设置控制调度、回调
## 七、其他

View File

@ -5,6 +5,7 @@
CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci;
use `xxl_job`;
SET NAMES utf8mb4;
CREATE TABLE `xxl_job_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,

23
pom.xml
View File

@ -20,24 +20,25 @@
<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.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.test.skip>true</maven.test.skip>
<xxl-rpc.version>1.5.0</xxl-rpc.version>
<spring.version>4.3.25.RELEASE</spring.version>
<spring-boot.version>1.5.22.RELEASE</spring-boot.version>
<mybatis-spring-boot-starter.version>1.3.5</mybatis-spring-boot-starter.version>
<mysql-connector-java.version>5.1.48</mysql-connector-java.version>
<spring.version>5.2.5.RELEASE</spring.version>
<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
<slf4j-api.version>1.7.29</slf4j-api.version>
<junit.version>4.12</junit.version>
<mybatis-spring-boot-starter.version>2.1.2</mybatis-spring-boot-starter.version>
<mysql-connector-java.version>8.0.19</mysql-connector-java.version>
<groovy.version>2.5.8</groovy.version>
<slf4j-api.version>1.7.30</slf4j-api.version>
<junit.version>4.13</junit.version>
<maven-source-plugin.version>3.2.0</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
<groovy.version>3.0.2</groovy.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.2.0</maven-javadoc-plugin.version>
<maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
<maven-war-plugin.version>3.2.3</maven-war-plugin.version>
</properties>

View File

@ -53,7 +53,7 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- mybatis-startermybatis + mybatis-spring + tomcat-jdbcdefault -->
<!-- mybatis-startermybatis + mybatis-spring + hikaridefault -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>

View File

@ -0,0 +1,20 @@
package com.xxl.job.admin.core.alarm;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
/**
* @author xuxueli 2020-01-19
*/
public interface JobAlarm {
/**
* job alarm
*
* @param info
* @param jobLog
* @return
*/
public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog);
}

View File

@ -0,0 +1,65 @@
package com.xxl.job.admin.core.alarm;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
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.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class JobAlarmer implements ApplicationContextAware, InitializingBean {
private static Logger logger = LoggerFactory.getLogger(JobAlarmer.class);
private ApplicationContext applicationContext;
private List<JobAlarm> jobAlarmList;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
Map<String, JobAlarm> serviceBeanMap = applicationContext.getBeansOfType(JobAlarm.class);
if (serviceBeanMap != null && serviceBeanMap.size() > 0) {
jobAlarmList = new ArrayList<JobAlarm>(serviceBeanMap.values());
}
}
/**
* job alarm
*
* @param info
* @param jobLog
* @return
*/
public boolean alarm(XxlJobInfo info, XxlJobLog jobLog) {
boolean result = false;
if (jobAlarmList!=null && jobAlarmList.size()>0) {
result = true; // success means all-success
for (JobAlarm alarm: jobAlarmList) {
boolean resultItem = false;
try {
resultItem = alarm.doAlarm(info, jobLog);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (!resultItem) {
result = false;
}
}
}
return result;
}
}

View File

@ -0,0 +1,117 @@
package com.xxl.job.admin.core.alarm.impl;
import com.xxl.job.admin.core.alarm.JobAlarm;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
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.util.I18nUtil;
import com.xxl.job.core.biz.model.ReturnT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import javax.mail.internet.MimeMessage;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* job alarm by email
*
* @author xuxueli 2020-01-19
*/
@Component
public class EmailJobAlarm implements JobAlarm {
private static Logger logger = LoggerFactory.getLogger(EmailJobAlarm.class);
/**
* fail alarm
*
* @param jobLog
*/
public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog){
boolean alarmResult = true;
// send monitor email
if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
// alarmContent
String alarmContent = "Alarm Job LogId=" + jobLog.getId();
if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) {
alarmContent += "<br>TriggerMsg=<br>" + jobLog.getTriggerMsg();
}
if (jobLog.getHandleCode()>0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) {
alarmContent += "<br>HandleCode=" + jobLog.getHandleMsg();
}
// email info
XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(Integer.valueOf(info.getJobGroup()));
String personal = I18nUtil.getString("admin_name_full");
String title = I18nUtil.getString("jobconf_monitor");
String content = MessageFormat.format(loadEmailJobAlarmTemplate(),
group!=null?group.getTitle():"null",
info.getId(),
info.getJobDesc(),
alarmContent);
Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
for (String email: emailSet) {
// make mail
try {
MimeMessage mimeMessage = XxlJobAdminConfig.getAdminConfig().getMailSender().createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailUserName(), personal);
helper.setTo(email);
helper.setSubject(title);
helper.setText(content, true);
XxlJobAdminConfig.getAdminConfig().getMailSender().send(mimeMessage);
} catch (Exception e) {
logger.error(">>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:{}", jobLog.getId(), e);
alarmResult = false;
}
}
}
return alarmResult;
}
/**
* load email job alarm template
*
* @return
*/
private static final String loadEmailJobAlarmTemplate(){
String mailBodyTemplate = "<h5>" + I18nUtil.getString("jobconf_monitor_detail") + "</span>" +
"<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n" +
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
" <tr>\n" +
" <td width=\"20%\" >"+ I18nUtil.getString("jobinfo_field_jobgroup") +"</td>\n" +
" <td width=\"10%\" >"+ I18nUtil.getString("jobinfo_field_id") +"</td>\n" +
" <td width=\"20%\" >"+ I18nUtil.getString("jobinfo_field_jobdesc") +"</td>\n" +
" <td width=\"10%\" >"+ I18nUtil.getString("jobconf_monitor_alarm_title") +"</td>\n" +
" <td width=\"40%\" >"+ I18nUtil.getString("jobconf_monitor_alarm_content") +"</td>\n" +
" </tr>\n" +
" </thead>\n" +
" <tbody>\n" +
" <tr>\n" +
" <td>{0}</td>\n" +
" <td>{1}</td>\n" +
" <td>{2}</td>\n" +
" <td>"+ I18nUtil.getString("jobconf_monitor_alarm_type") +"</td>\n" +
" <td>{3}</td>\n" +
" </tr>\n" +
" </tbody>\n" +
"</table>";
return mailBodyTemplate;
}
}

View File

@ -1,5 +1,6 @@
package com.xxl.job.admin.core.conf;
import com.xxl.job.admin.core.alarm.JobAlarmer;
import com.xxl.job.admin.core.scheduler.XxlJobScheduler;
import com.xxl.job.admin.dao.*;
import org.springframework.beans.factory.DisposableBean;
@ -10,6 +11,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Arrays;
/**
* xxl-job config
@ -81,9 +83,14 @@ public class XxlJobAdminConfig implements InitializingBean, DisposableBean {
private JavaMailSender mailSender;
@Resource
private DataSource dataSource;
@Resource
private JobAlarmer jobAlarmer;
public String getI18n() {
if (!Arrays.asList("zh_CN", "zh_TC", "en").contains(i18n)) {
return "zh_CN";
}
return i18n;
}
@ -144,4 +151,8 @@ public class XxlJobAdminConfig implements InitializingBean, DisposableBean {
return dataSource;
}
public JobAlarmer getJobAlarmer() {
return jobAlarmer;
}
}

View File

@ -1,22 +1,14 @@
package com.xxl.job.admin.core.thread;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
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.trigger.TriggerTypeEnum;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.model.ReturnT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.internet.MimeMessage;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@ -69,13 +61,7 @@ public class JobFailMonitorHelper {
// 2fail alarm monitor
int newAlarmStatus = 0; // 告警状态0-默认-1=锁定状态1-无需告警2-告警成功3-告警失败
if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
boolean alarmResult = true;
try {
alarmResult = failAlarm(info, log);
} catch (Exception e) {
alarmResult = false;
logger.error(e.getMessage(), e);
}
boolean alarmResult = XxlJobAdminConfig.getAdminConfig().getJobAlarmer().alarm(info, log);
newAlarmStatus = alarmResult?2:3;
} else {
newAlarmStatus = 1;
@ -121,89 +107,4 @@ public class JobFailMonitorHelper {
}
}
// ---------------------- alarm ----------------------
// email alarm template
private static final String mailBodyTemplate = "<h5>" + I18nUtil.getString("jobconf_monitor_detail") + "</span>" +
"<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n" +
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
" <tr>\n" +
" <td width=\"20%\" >"+ I18nUtil.getString("jobinfo_field_jobgroup") +"</td>\n" +
" <td width=\"10%\" >"+ I18nUtil.getString("jobinfo_field_id") +"</td>\n" +
" <td width=\"20%\" >"+ I18nUtil.getString("jobinfo_field_jobdesc") +"</td>\n" +
" <td width=\"10%\" >"+ I18nUtil.getString("jobconf_monitor_alarm_title") +"</td>\n" +
" <td width=\"40%\" >"+ I18nUtil.getString("jobconf_monitor_alarm_content") +"</td>\n" +
" </tr>\n" +
" </thead>\n" +
" <tbody>\n" +
" <tr>\n" +
" <td>{0}</td>\n" +
" <td>{1}</td>\n" +
" <td>{2}</td>\n" +
" <td>"+ I18nUtil.getString("jobconf_monitor_alarm_type") +"</td>\n" +
" <td>{3}</td>\n" +
" </tr>\n" +
" </tbody>\n" +
"</table>";
/**
* fail alarm
*
* @param jobLog
*/
private boolean failAlarm(XxlJobInfo info, XxlJobLog jobLog){
boolean alarmResult = true;
// send monitor email
if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
// alarmContent
String alarmContent = "Alarm Job LogId=" + jobLog.getId();
if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) {
alarmContent += "<br>TriggerMsg=<br>" + jobLog.getTriggerMsg();
}
if (jobLog.getHandleCode()>0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) {
alarmContent += "<br>HandleCode=" + jobLog.getHandleMsg();
}
// email info
XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(Integer.valueOf(info.getJobGroup()));
String personal = I18nUtil.getString("admin_name_full");
String title = I18nUtil.getString("jobconf_monitor");
String content = MessageFormat.format(mailBodyTemplate,
group!=null?group.getTitle():"null",
info.getId(),
info.getJobDesc(),
alarmContent);
Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
for (String email: emailSet) {
// make mail
try {
MimeMessage mimeMessage = XxlJobAdminConfig.getAdminConfig().getMailSender().createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailUserName(), personal);
helper.setTo(email);
helper.setSubject(title);
helper.setText(content, true);
XxlJobAdminConfig.getAdminConfig().getMailSender().send(mimeMessage);
} catch (Exception e) {
logger.error(">>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:{}", jobLog.getId(), e);
alarmResult = false;
}
}
}
// do something, custom alarm strategy, such as sms
return alarmResult;
}
}

View File

@ -30,8 +30,7 @@ public class I18nUtil {
try {
// build i18n prop
String i18n = XxlJobAdminConfig.getAdminConfig().getI18n();
i18n = (i18n!=null && i18n.trim().length()>0)?("_"+i18n):i18n;
String i18nFile = MessageFormat.format("i18n/message{0}.properties", i18n);
String i18nFile = MessageFormat.format("i18n/message_{0}.properties", i18n);
// load prop
Resource resource = new ClassPathResource(i18nFile);

View File

@ -1,9 +1,9 @@
### web
server.port=8080
server.context-path=/xxl-job-admin
server.servlet.context-path=/xxl-job-admin
### actuator
management.context-path=/actuator
management.server.servlet.context-path=/actuator
management.health.mail.enabled=false
### resources
@ -20,6 +20,7 @@ spring.freemarker.settings.number_format=0.##########
### mybatis
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml
#mybatis.type-aliases-package=com.xxl.job.admin.core.model
### xxl-job, datasource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?Unicode=true&characterEncoding=UTF-8
@ -27,14 +28,18 @@ spring.datasource.username=root
spring.datasource.password=root_pwd
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.max-active=30
spring.datasource.tomcat.test-on-borrow=true
spring.datasource.tomcat.validation-query=SELECT 1
spring.datasource.tomcat.validation-interval=30000
### datasource-pool
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=HikariCP
spring.datasource.hikari.max-lifetime=900000
spring.datasource.hikari.connection-timeout=10000
spring.datasource.hikari.connection-test-query=SELECT 1
### xxl-job email
### xxl-job, email
spring.mail.host=smtp.qq.com
spring.mail.port=25
spring.mail.username=xxx@qq.com
@ -47,8 +52,8 @@ spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFact
### xxl-job, access token
xxl.job.accessToken=
### xxl-job, i18n (default empty as chinese, "en" as english)
xxl.job.i18n=
### xxl-job, i18n (default is zh_CN, and you can choose "zh_CN", "zh_TC" and "en")
xxl.job.i18n=zh_CN
## xxl-job, triggerpool max size
xxl.job.triggerpool.fast.max=200

View File

@ -0,0 +1,262 @@
admin_name=任務調度中心
admin_name_full=分布式任務調度平臺XXL-JOB
admin_version=2.1.1
admin_i18n=
## system
system_tips=系統提示
system_ok=確定
system_close=關閉
system_save=儲存
system_cancel=取消
system_search=搜尋
system_status=狀態
system_opt=操作
system_please_input=請輸入
system_please_choose=请選擇
system_success=成功
system_fail=失敗
system_add_suc=新增成功
system_add_fail=新增失敗
system_update_suc=更新成功
system_update_fail=更新失敗
system_all=全部
system_api_error=API錯誤
system_show=查看
system_empty=
system_opt_suc=操作成功
system_opt_fail=操作失敗
system_opt_edit=編輯
system_opt_del=刪除
system_unvalid=非法
system_not_found=不存在
system_nav=導航
system_digits=整數
system_lengh_limit=長度限制
system_permission_limit=權限控管
system_welcome=歡迎
## daterangepicker
daterangepicker_ranges_recent_hour=最近一小時
daterangepicker_ranges_today=今日
daterangepicker_ranges_yesterday=昨日
daterangepicker_ranges_this_month=本月
daterangepicker_ranges_last_month=上個月
daterangepicker_ranges_recent_week=最近一周
daterangepicker_ranges_recent_month=最近一月
daterangepicker_custom_name=自定義
daterangepicker_custom_starttime=起始時間
daterangepicker_custom_endtime=結束時間
daterangepicker_custom_daysofweek=日,一,二,三,四,五,六
daterangepicker_custom_monthnames=一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月
## dataTable
dataTable_sProcessing=處理中...
dataTable_sLengthMenu=每頁 _MENU_ 條記錄
dataTable_sZeroRecords=沒有相符合記錄
dataTable_sInfo=第 _PAGE_ 頁 ( 總共 _PAGES_ 頁_TOTAL_ 條記錄 )
dataTable_sInfoEmpty=無記錄
dataTable_sInfoFiltered=(由 _MAX_ 項結果過濾)
dataTable_sSearch=搜尋
dataTable_sEmptyTable=表中資料為空
dataTable_sLoadingRecords=載入中...
dataTable_sFirst=首頁
dataTable_sPrevious=上頁
dataTable_sNext=下頁
dataTable_sLast=末頁
dataTable_sSortAscending=: 以升幂排序此列
dataTable_sSortDescending=: 以降幂排序此列
## login
login_btn=登入
login_remember_me=記住密碼
login_username_placeholder=請輸入登入帳號
login_password_placeholder=請輸入登入密碼
login_username_empty=請輸入登入帳號
login_username_lt_4=登入帳號不應低於4位數
login_password_empty=請輸入登入密碼
login_password_lt_4=登入密碼不應低於4位數
login_success=登入成功
login_fail=登入失敗
login_param_empty=帳號或密碼為空值
login_param_unvalid=帳號或密碼錯誤
## logout
logout_btn=登出
logout_confirm=確認登出?
logout_success=登出成功
logout_fail=登出失敗
## change pwd
change_pwd=修改密碼
change_pwd_suc_to_logout=修改密碼成功,即將登出
change_pwd_field_newpwd=新密碼
## dashboard
job_dashboard_name=運行報表
job_dashboard_job_num=任務數量
job_dashboard_job_num_tip=調度中心運行的任務數量
job_dashboard_trigger_num=調度次數
job_dashboard_trigger_num_tip=調度中心觸發的調度次數
job_dashboard_jobgroup_num=執行器數量
job_dashboard_jobgroup_num_tip=調度中心在線的執行器機器數量
job_dashboard_report=調度報表
job_dashboard_report_loaddata_fail=調度報表資料加載異常
job_dashboard_date_report=日期分布圖
job_dashboard_rate_report=成功比例圖
## job info
jobinfo_name=任務管理
jobinfo_job=任務
jobinfo_field_add=新增
jobinfo_field_update=更新任務
jobinfo_field_id=任務ID
jobinfo_field_jobgroup=執行器
jobinfo_field_jobdesc=任務描述
jobinfo_field_gluetype=運行模式
jobinfo_field_executorparam=任務參數
jobinfo_field_cron_unvalid=Cron 格式非法
jobinfo_field_cron_never_fire=Cron 格式非法,永遠不會觸發
jobinfo_field_author=負責人
jobinfo_field_timeout=任務超時秒數
jobinfo_field_alarmemail=告警郵件
jobinfo_field_alarmemail_placeholder=輸入多個告警郵件地址,請以逗號分隔
jobinfo_field_executorRouteStrategy=路由策略
jobinfo_field_childJobId=子任務ID
jobinfo_field_childJobId_placeholder=輸入子任務ID如有多個請以逗號分隔
jobinfo_field_executorBlockStrategy=阻塞處理策略
jobinfo_field_executorFailRetryCount=失敗重試次數
jobinfo_field_executorFailRetryCount_placeholder=失敗重試次數,大於零時生效
jobinfo_script_location=腳本位置
jobinfo_shard_index=分片序號
jobinfo_shard_total=分片總數
jobinfo_opt_stop=停止
jobinfo_opt_start=啟動
jobinfo_opt_log=查詢日誌
jobinfo_opt_run=執行一次
jobinfo_opt_registryinfo=注冊節點
jobinfo_opt_next_time=下次執行時間
jobinfo_glue_remark=源碼備註
jobinfo_glue_remark_limit=源碼備註長度限制為4~100
jobinfo_glue_rollback=版本回復
jobinfo_glue_jobid_unvalid=任務ID非法
jobinfo_glue_gluetype_unvalid=該任務非GLUE模式
jobinfo_field_executorTimeout_placeholder=任務超時時間,單位秒,大於零時生效
## job log
joblog_name=調度日誌
joblog_status=狀態
joblog_status_all=全部
joblog_status_suc=成功
joblog_status_fail=失敗
joblog_status_running=進行中
joblog_field_triggerTime=調度時間
joblog_field_triggerCode=調度結果
joblog_field_triggerMsg=調度備註
joblog_field_handleTime=執行時間
joblog_field_handleCode=執行结果
joblog_field_handleMsg=執行備註
joblog_field_executorAddress=執行器地址
joblog_clean=清理
joblog_clean_log=日誌清理
joblog_clean_type=清理方式
joblog_clean_type_1=清理一個月之前日誌資料
joblog_clean_type_2=清理三個月之前日誌資料
joblog_clean_type_3=清理六個月之前日誌資料
joblog_clean_type_4=清理一年之前日誌資料
joblog_clean_type_5=清理一千條以前日誌資料
joblog_clean_type_6=清理一萬條以前日誌資料
joblog_clean_type_7=清理三萬條以前日誌資料
joblog_clean_type_8=清理十萬條以前日誌資料
joblog_clean_type_9=清理所有日誌資料
joblog_clean_type_unvalid=清理類型參数異常
joblog_handleCode_200=成功
joblog_handleCode_500=失敗
joblog_handleCode_502=失敗(超時)
joblog_kill_log=终止任務
joblog_kill_log_limit=調度失敗,無法终止日誌
joblog_kill_log_byman=人為操作主動終止
joblog_rolling_log=執行日誌
joblog_rolling_log_refresh=更新
joblog_rolling_log_triggerfail=任務發起調度失敗,無法查看執行日誌
joblog_rolling_log_failoften=終止請求Rolling日誌請求失敗次數超上限可刷新頁面重新加載日誌
joblog_logid_unvalid=日誌ID非法
## job group
jobgroup_name=執行器管理
jobgroup_list=執行器列表
jobgroup_add=新增執行器
jobgroup_edit=編輯執行器
jobgroup_del=刪除執行器
jobgroup_field_order=排序
jobgroup_field_title=名稱
jobgroup_field_addressType=注冊方式
jobgroup_field_addressType_0=自動注冊
jobgroup_field_addressType_1=手動登錄
jobgroup_field_addressType_limit=手動登錄注冊方式,機器地址不可為空
jobgroup_field_registryList=機器地址
jobgroup_field_registryList_unvalid=機器地址格式非法
jobgroup_field_registryList_placeholder=請輸入執行器地址列表,多個地址請以逗號分隔
jobgroup_field_appName_limit=限制以小寫字母開頭,由小寫字母、數字和中划線組成
jobgroup_field_appName_length=AppName長度限制為4~64
jobgroup_field_title_length=名稱長度限制為4~12
jobgroup_field_order_digits=請輸入整數
jobgroup_field_orderrange=取值範圍為1~1000
jobgroup_del_limit_0=拒絕刪除,該執行器使用中
jobgroup_del_limit_1=拒絕删除,系统至少保留一個執行器
jobgroup_empty=不存在有效執行器,請聯絡系統管理員
## job conf
jobconf_block_SERIAL_EXECUTION=單機串行
jobconf_block_DISCARD_LATER=丢棄后續調度
jobconf_block_COVER_EARLY=覆蓋之前調度
jobconf_route_first=第一個
jobconf_route_last=最後一個
jobconf_route_round=輪詢
jobconf_route_random=隨機
jobconf_route_consistenthash=一致性HASH
jobconf_route_lfu=最不經常使用
jobconf_route_lru=最近最久未使用
jobconf_route_failover=故障轉移
jobconf_route_busyover=忙碌轉移
jobconf_route_shard=分片廣播
jobconf_idleBeat=空閒檢測
jobconf_beat=心跳檢測
jobconf_monitor=任務調度中心監控告警
jobconf_monitor_detail=監控告警明细
jobconf_monitor_alarm_title=告警類型
jobconf_monitor_alarm_type=調度失敗
jobconf_monitor_alarm_content=告警内容
jobconf_trigger_admin_adress=調度機器
jobconf_trigger_exe_regtype=執行器-注冊方式
jobconf_trigger_exe_regaddress=執行器-地址列表
jobconf_trigger_address_empty=調度失敗:執行器地址為空
jobconf_trigger_run=觸發調度
jobconf_trigger_child_run=觸發子任務
jobconf_callback_child_msg1={0}/{1} [任務ID={2}], 觸發{3}, 觸發備註: {4} <br>
jobconf_callback_child_msg2={0}/{1} [任務ID={2}], 觸發失败, 觸發備註: 任務ID格式錯誤 <br>
jobconf_trigger_type=任務觸發類型
jobconf_trigger_type_cron=Cron觸發
jobconf_trigger_type_manual=手動觸發
jobconf_trigger_type_parent=父任務觸發
jobconf_trigger_type_api=API觸發
jobconf_trigger_type_retry=失敗重試觸發
## user
user_manage=用户管理
user_username=帳號
user_password=密碼
user_role=角色
user_role_admin=管理員
user_role_normal=普通用戶
user_permission=權限
user_add=新增用戶
user_update=更新用戶
user_username_repeat=帳號重複
user_username_valid=限制以小寫字母開頭,由小寫字母、數字組成
user_password_update_placeholder=請輸入新密碼,為空則不更新密碼
user_update_loginuser_limit=禁止操作當前登入帳號
## help
job_help=使用教程
job_help_document=官方文件

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -100,7 +100,7 @@
<h4 class="modal-title" ><i class="fa fa-fw fa-save"></i>${I18n.system_save}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_glue_remark}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" id="glueRemark" placeholder="${I18n.system_please_input}${I18n.jobinfo_glue_remark}" maxlength="64" ></div>
@ -112,7 +112,7 @@
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@ -3,8 +3,6 @@ package com.xxl.job.core.executor.impl;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.glue.GlueFactory;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.core.handler.impl.MethodJobHandler;
import org.slf4j.Logger;
@ -18,9 +16,11 @@ import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* xxl-job executor (for spring)
*
@ -35,7 +35,7 @@ public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationC
public void afterPropertiesSet() throws Exception {
// init JobHandler Repository
initJobHandlerRepository(applicationContext);
/*initJobHandlerRepository(applicationContext);*/
// init JobHandler Repository (for method)
initJobHandlerMethodRepository(applicationContext);
@ -54,7 +54,7 @@ public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationC
}
private void initJobHandlerRepository(ApplicationContext applicationContext) {
/*private void initJobHandlerRepository(ApplicationContext applicationContext) {
if (applicationContext == null) {
return;
}
@ -74,13 +74,14 @@ public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationC
}
}
}
}
}*/
private void initJobHandlerMethodRepository(ApplicationContext applicationContext) {
if (applicationContext == null) {
return;
}
// init job handler from method
String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = applicationContext.getBean(beanDefinitionName);
@ -150,6 +151,7 @@ public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationC
registJobHandler(name, new MethodJobHandler(bean, method, initMethod, destroyMethod));
}
}
}
// ---------------------- applicationContext ----------------------

View File

@ -1,24 +1,24 @@
package com.xxl.job.core.handler.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* annotation for job handler
*
* will be replaced by {@link com.xxl.job.core.handler.annotation.XxlJob}
*
* @author 2016-5-17 21:06:49
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Deprecated
public @interface JobHandler {
String value() default "";
}
//package com.xxl.job.core.handler.annotation;
//
//import java.lang.annotation.ElementType;
//import java.lang.annotation.Inherited;
//import java.lang.annotation.Retention;
//import java.lang.annotation.RetentionPolicy;
//import java.lang.annotation.Target;
//
///**
// * annotation for job handler
// *
// * will be replaced by {@link com.xxl.job.core.handler.annotation.XxlJob}
// *
// * @author 2016-5-17 21:06:49
// */
//@Target({ElementType.TYPE})
//@Retention(RetentionPolicy.RUNTIME)
//@Inherited
//@Deprecated
//public @interface JobHandler {
//
// String value();
//
//}

View File

@ -15,7 +15,7 @@ public @interface XxlJob {
/**
* jobhandler name
*/
String value() default "";
String value();
/**
* init handler, invoked when JobThread init