- 组件化优化,移除对 spring 的依赖:非spring应用选用 "XxlJobExecutor" 、spring应用选用 "XxlJobSpringExecutor" 作为执行器组件;

- 任务RollingLog展示逻辑优化,修复超时任务无法查看的问题;
This commit is contained in:
xuxueli 2018-11-01 10:35:54 +08:00
parent af6c46743f
commit df2b9f7e0c
11 changed files with 213 additions and 136 deletions

View File

@ -1328,12 +1328,14 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
- 2、底层通讯组件迁移至 xxl-rpc - 2、底层通讯组件迁移至 xxl-rpc
- 3、IP获取逻辑优化优先遍历网卡来获取可用IP - 3、IP获取逻辑优化优先遍历网卡来获取可用IP
- 4、任务新增的API服务接口返回任务ID方便调用方实用 - 4、任务新增的API服务接口返回任务ID方便调用方实用
- 5、新增无框架执行器Sample示例项目 "xxl-job-executor-sample-frameless"。不依赖第三方框架只需main方法即可启动运行执行器 - 5、组件化优化移除对 spring 的依赖非spring应用选用 "XxlJobExecutor" 、spring应用选用 "XxlJobSpringExecutor" 作为执行器组件;
- 6、[迭代中]任务状态与quartz解耦降低quartz调度压力仅NORMAL状态任务绑定quartz - 6、新增无框架执行器Sample示例项目 "xxl-job-executor-sample-frameless"。不依赖第三方框架只需main方法即可启动运行执行器
- 7、[迭代中]新增任务默认运行状态,任务更新时运行状态保持不变; - 7、任务RollingLog展示逻辑优化修复超时任务无法查看的问题
- 8、[迭代中]原生提供通用命令行任务HandlerBean任务"CommandJobHandler");业务方只需要提供命令行即可,可执行任意命令; - 8、[迭代中]任务状态与quartz解耦降低quartz调度压力仅NORMAL状态任务绑定quartz
- 9、[迭代中]cron在线生成工具如 "cronboot/cron.qqe2" - 9、[迭代中]新增任务默认运行状态,任务更新时运行状态保持不变;
- 10、[迭代中]docker镜像并且推送docker镜像到中央仓库更进一步实现产品开箱即用 - 10、[迭代中]原生提供通用命令行任务HandlerBean任务"CommandJobHandler");业务方只需要提供命令行即可,可执行任意命令;
- 11、[迭代中]cron在线生成工具如 "cronboot/cron.qqe2"
- 12、[迭代中]docker镜像并且推送docker镜像到中央仓库更进一步实现产品开箱即用
### TODO LIST ### TODO LIST

View File

@ -13,6 +13,7 @@ import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam; import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import com.xxl.rpc.util.IpUtil; import com.xxl.rpc.util.IpUtil;
import com.xxl.rpc.util.ThrowableUtil;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -190,7 +191,7 @@ public class XxlJobTrigger {
runResult = executorBiz.run(triggerParam); runResult = executorBiz.run(triggerParam);
} catch (Exception e) { } catch (Exception e) {
logger.error(">>>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.", address, e); logger.error(">>>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.", address, e);
runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e ); runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ThrowableUtil.toString(e));
} }
StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + ""); StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + "");

View File

@ -1,7 +1,7 @@
$(function() { $(function() {
// trigger fail, end // trigger fail, end
if (triggerCode != 200) { if ( !(triggerCode == 200 || handleCode != 0) ) {
$('#logConsoleRunning').hide(); $('#logConsoleRunning').hide();
$('#logConsole').append('<span style="color: red;">'+ I18n.joblog_rolling_log_triggerfail +'</span>'); $('#logConsole').append('<span style="color: red;">'+ I18n.joblog_rolling_log_triggerfail +'</span>');
return; return;

View File

@ -179,7 +179,7 @@ $(function() {
"render": function ( data, type, row ) { "render": function ( data, type, row ) {
// better support expression or string, not function // better support expression or string, not function
return function () { return function () {
if (row.triggerCode == 200){ if (row.triggerCode == 200 || row.handleCode != 0){
var temp = '<a href="javascript:;" class="logDetail" _id="'+ row.id +'">'+ I18n.joblog_rolling_log +'</a>'; var temp = '<a href="javascript:;" class="logDetail" _id="'+ row.id +'">'+ I18n.joblog_rolling_log +'</a>';
if(row.handleCode == 0){ if(row.handleCode == 0){
temp += '<br><a href="javascript:;" class="logKill" _id="'+ row.id +'" style="color: red;" >'+ I18n.joblog_kill_log +'</a>'; temp += '<br><a href="javascript:;" class="logKill" _id="'+ row.id +'" style="color: red;" >'+ I18n.joblog_kill_log +'</a>';

View File

@ -36,13 +36,6 @@
<version>${commons-exec.version}</version> <version>${commons-exec.version}</version>
</dependency> </dependency>
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- jackson --> <!-- jackson -->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
@ -50,6 +43,14 @@
<version>${jackson.version}</version> <version>${jackson.version}</version>
</dependency> </dependency>
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -4,7 +4,6 @@ import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.impl.ExecutorBizImpl; import com.xxl.job.core.biz.impl.ExecutorBizImpl;
import com.xxl.job.core.handler.IJobHandler; import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobFileAppender; import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.thread.ExecutorRegistryThread; import com.xxl.job.core.thread.ExecutorRegistryThread;
import com.xxl.job.core.thread.JobLogFileCleanThread; import com.xxl.job.core.thread.JobLogFileCleanThread;
@ -21,9 +20,6 @@ import com.xxl.rpc.util.IpUtil;
import com.xxl.rpc.util.NetUtil; import com.xxl.rpc.util.NetUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -31,7 +27,7 @@ import java.util.concurrent.ConcurrentHashMap;
/** /**
* Created by xuxueli on 2016/3/2 21:14. * Created by xuxueli on 2016/3/2 21:14.
*/ */
public class XxlJobExecutor implements ApplicationContextAware { public class XxlJobExecutor {
private static final Logger logger = LoggerFactory.getLogger(XxlJobExecutor.class); private static final Logger logger = LoggerFactory.getLogger(XxlJobExecutor.class);
// ---------------------- param ---------------------- // ---------------------- param ----------------------
@ -65,16 +61,6 @@ public class XxlJobExecutor implements ApplicationContextAware {
this.logRetentionDays = logRetentionDays; this.logRetentionDays = logRetentionDays;
} }
// ---------------------- applicationContext ----------------------
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
// ---------------------- start + stop ---------------------- // ---------------------- start + stop ----------------------
public void start() throws Exception { public void start() throws Exception {
@ -82,12 +68,10 @@ public class XxlJobExecutor implements ApplicationContextAware {
// init logpath // init logpath
XxlJobFileAppender.initLogPath(logPath); XxlJobFileAppender.initLogPath(logPath);
// init JobHandler Repository
initJobHandlerRepository(applicationContext);
// init admin-client // init admin-client
initAdminBizList(adminAddresses, accessToken); initAdminBizList(adminAddresses, accessToken);
// init JobLogFileCleanThread // init JobLogFileCleanThread
JobLogFileCleanThread.getInstance().start(logRetentionDays); JobLogFileCleanThread.getInstance().start(logRetentionDays);
@ -108,6 +92,7 @@ public class XxlJobExecutor implements ApplicationContextAware {
jobThreadRepository.clear(); jobThreadRepository.clear();
} }
// destory JobLogFileCleanThread // destory JobLogFileCleanThread
JobLogFileCleanThread.getInstance().toStop(); JobLogFileCleanThread.getInstance().toStop();
@ -228,27 +213,6 @@ public class XxlJobExecutor implements ApplicationContextAware {
public static IJobHandler loadJobHandler(String name){ public static IJobHandler loadJobHandler(String name){
return jobHandlerRepository.get(name); return jobHandlerRepository.get(name);
} }
private void initJobHandlerRepository(ApplicationContext applicationContext){
if (applicationContext == null) {
return;
}
// init job handler action
Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class);
if (serviceBeanMap!=null && serviceBeanMap.size()>0) {
for (Object serviceBean : serviceBeanMap.values()) {
if (serviceBean instanceof IJobHandler){
String name = serviceBean.getClass().getAnnotation(JobHandler.class).value();
IJobHandler handler = (IJobHandler) serviceBean;
if (loadJobHandler(name) != null) {
throw new RuntimeException("xxl-job jobhandler naming conflicts.");
}
registJobHandler(name, handler);
}
}
}
}
// ---------------------- job thread repository ---------------------- // ---------------------- job thread repository ----------------------

View File

@ -0,0 +1,67 @@
package com.xxl.job.core.executor.impl;
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 org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Map;
/**
* xxl-job executor (for spring)
*
* @author xuxueli 2018-11-01 09:24:52
*/
public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationContextAware {
@Override
public void start() throws Exception {
// init JobHandler Repository
initJobHandlerRepository(applicationContext);
// refresh GlueFactory
GlueFactory.refreshInstance(1);
// super start
super.start();
}
private void initJobHandlerRepository(ApplicationContext applicationContext){
if (applicationContext == null) {
return;
}
// init job handler action
Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class);
if (serviceBeanMap!=null && serviceBeanMap.size()>0) {
for (Object serviceBean : serviceBeanMap.values()) {
if (serviceBean instanceof IJobHandler){
String name = serviceBean.getClass().getAnnotation(JobHandler.class).value();
IJobHandler handler = (IJobHandler) serviceBean;
if (loadJobHandler(name) != null) {
throw new RuntimeException("xxl-job jobhandler naming conflicts.");
}
registJobHandler(name, handler);
}
}
}
}
// ---------------------- applicationContext ----------------------
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}

View File

@ -1,90 +1,43 @@
package com.xxl.job.core.glue; package com.xxl.job.core.glue;
import com.xxl.job.core.executor.XxlJobExecutor; import com.xxl.job.core.glue.impl.SpringGlueFactory;
import com.xxl.job.core.handler.IJobHandler; import com.xxl.job.core.handler.IJobHandler;
import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyClassLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.AnnotationUtils;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/** /**
* glue factory, product class/object by name * glue factory, product class/object by name
*
* @author xuxueli 2016-1-2 20:02:27 * @author xuxueli 2016-1-2 20:02:27
*/ */
public class GlueFactory { public class GlueFactory {
private static Logger logger = LoggerFactory.getLogger(GlueFactory.class);
private static GlueFactory glueFactory = new GlueFactory();
public static GlueFactory getInstance(){
return glueFactory;
}
public static void refreshInstance(int type){
if (type == 0) {
glueFactory = new GlueFactory();
} else if (type == 1) {
glueFactory = new SpringGlueFactory();
}
}
/** /**
* groovy class loader * groovy class loader
*/ */
private GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); private GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
// ----------------------------- spring support -----------------------------
private static GlueFactory glueFactory = new GlueFactory();
public static GlueFactory getInstance(){
return glueFactory;
}
/** /**
* inject action of spring * load new instance, prototype
* @param instance *
* @param codeSource
* @return
* @throws Exception
*/ */
private void injectService(Object instance){
if (instance==null) {
return;
}
Field[] fields = instance.getClass().getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
Object fieldBean = null;
// with bean-id, bean could be found by both @Resource and @Autowired, or bean could only be found by @Autowired
if (AnnotationUtils.getAnnotation(field, Resource.class) != null) {
try {
Resource resource = AnnotationUtils.getAnnotation(field, Resource.class);
if (resource.name()!=null && resource.name().length()>0){
fieldBean = XxlJobExecutor.getApplicationContext().getBean(resource.name());
} else {
fieldBean = XxlJobExecutor.getApplicationContext().getBean(field.getName());
}
} catch (Exception e) {
}
if (fieldBean==null ) {
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.getApplicationContext().getBean(qualifier.value());
} else {
fieldBean = XxlJobExecutor.getApplicationContext().getBean(field.getType());
}
}
if (fieldBean!=null) {
field.setAccessible(true);
try {
field.set(instance, fieldBean);
} catch (IllegalArgumentException e) {
logger.error(e.getMessage(), e);
} catch (IllegalAccessException e) {
logger.error(e.getMessage(), e);
}
}
}
}
// ----------------------------- load instance -----------------------------
// load new instance, prototype
public IJobHandler loadNewInstance(String codeSource) throws Exception{ public IJobHandler loadNewInstance(String codeSource) throws Exception{
if (codeSource!=null && codeSource.trim().length()>0) { if (codeSource!=null && codeSource.trim().length()>0) {
Class<?> clazz = groovyClassLoader.parseClass(codeSource); Class<?> clazz = groovyClassLoader.parseClass(codeSource);
@ -104,4 +57,13 @@ public class GlueFactory {
throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadNewInstance error, instance is null"); throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadNewInstance error, instance is null");
} }
/**
* inject service of bean field
*
* @param instance
*/
public void injectService(Object instance) {
// do something
}
} }

View File

@ -0,0 +1,80 @@
package com.xxl.job.core.glue.impl;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import com.xxl.job.core.glue.GlueFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.AnnotationUtils;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* @author xuxueli 2018-11-01
*/
public class SpringGlueFactory extends GlueFactory {
private static Logger logger = LoggerFactory.getLogger(SpringGlueFactory.class);
/**
* inject action of spring
* @param instance
*/
@Override
public void injectService(Object instance){
if (instance==null) {
return;
}
if (XxlJobSpringExecutor.getApplicationContext() == null) {
return;
}
Field[] fields = instance.getClass().getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
Object fieldBean = null;
// with bean-id, bean could be found by both @Resource and @Autowired, or bean could only be found by @Autowired
if (AnnotationUtils.getAnnotation(field, Resource.class) != null) {
try {
Resource resource = AnnotationUtils.getAnnotation(field, Resource.class);
if (resource.name()!=null && resource.name().length()>0){
fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(resource.name());
} else {
fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getName());
}
} catch (Exception e) {
}
if (fieldBean==null ) {
fieldBean = XxlJobSpringExecutor.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 = XxlJobSpringExecutor.getApplicationContext().getBean(qualifier.value());
} else {
fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getType());
}
}
if (fieldBean!=null) {
field.setAccessible(true);
try {
field.set(instance, fieldBean);
} catch (IllegalArgumentException e) {
logger.error(e.getMessage(), e);
} catch (IllegalAccessException e) {
logger.error(e.getMessage(), e);
}
}
}
}
}

View File

@ -22,7 +22,7 @@
<context:component-scan base-package="com.xxl.job.executor.service.jobhandler" /> <context:component-scan base-package="com.xxl.job.executor.service.jobhandler" />
<!-- 配置02、执行器 --> <!-- 配置02、执行器 -->
<bean id="xxlJobExecutor" class="com.xxl.job.core.executor.XxlJobExecutor" init-method="start" destroy-method="destroy" > <bean id="xxlJobSpringExecutor" class="com.xxl.job.core.executor.impl.XxlJobSpringExecutor" init-method="start" destroy-method="destroy" >
<!-- 执行器注册中心地址[选填],为空则关闭自动注册 --> <!-- 执行器注册中心地址[选填],为空则关闭自动注册 -->
<property name="adminAddresses" value="${xxl.job.admin.addresses}" /> <property name="adminAddresses" value="${xxl.job.admin.addresses}" />
<!-- 执行器AppName[选填],为空则关闭自动注册 --> <!-- 执行器AppName[选填],为空则关闭自动注册 -->

View File

@ -1,6 +1,6 @@
package com.xxl.job.executor.core.config; package com.xxl.job.executor.core.config;
import com.xxl.job.core.executor.XxlJobExecutor; import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -41,18 +41,18 @@ public class XxlJobConfig {
@Bean(initMethod = "start", destroyMethod = "destroy") @Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobExecutor xxlJobExecutor() { public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init."); logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobExecutor xxlJobExecutor = new XxlJobExecutor(); XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobExecutor.setAdminAddresses(adminAddresses); xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobExecutor.setAppName(appName); xxlJobSpringExecutor.setAppName(appName);
xxlJobExecutor.setIp(ip); xxlJobSpringExecutor.setIp(ip);
xxlJobExecutor.setPort(port); xxlJobSpringExecutor.setPort(port);
xxlJobExecutor.setAccessToken(accessToken); xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobExecutor.setLogPath(logPath); xxlJobSpringExecutor.setLogPath(logPath);
xxlJobExecutor.setLogRetentionDays(logRetentionDays); xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobExecutor; return xxlJobSpringExecutor;
} }
} }