- 将单例模式改为实例方法调用,通过getInstance()获取执行器实例 - 引入Helper类替换原有静态线程实现,包括ExecutorRegistryThreadHelper、 JobLogFileCleanThreadHelper和TriggerCallbackThreadHelper - 移除废弃的常量ELEGANT_SHUTDOWN_WAITING_SECONDS,改用Const类中的统一配置 - 更新所有内部方法调用为实例方法,增强代码的可测试性和灵活性 - 使用MessageQueue和CyclicThread优化回调消息处理机制 - 统一资源管理和生命周期控制,改进线程安全和资源清理逻辑3.4.1-release
parent
a7a3d44ee1
commit
9d4ba92537
@ -1,131 +0,0 @@
|
|||||||
package com.xxl.job.core.thread;
|
|
||||||
|
|
||||||
import com.xxl.job.core.constant.RegistType;
|
|
||||||
import com.xxl.job.core.openapi.AdminBiz;
|
|
||||||
import com.xxl.job.core.openapi.model.RegistryRequest;
|
|
||||||
import com.xxl.job.core.constant.Const;
|
|
||||||
import com.xxl.job.core.executor.XxlJobExecutor;
|
|
||||||
import com.xxl.tool.response.Response;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by xuxueli on 17/3/2.
|
|
||||||
*/
|
|
||||||
public class ExecutorRegistryThread {
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(ExecutorRegistryThread.class);
|
|
||||||
|
|
||||||
private static ExecutorRegistryThread instance = new ExecutorRegistryThread();
|
|
||||||
public static ExecutorRegistryThread getInstance(){
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Thread registryThread;
|
|
||||||
private volatile boolean toStop = false;
|
|
||||||
public void start(final String appname, final String address){
|
|
||||||
|
|
||||||
// valid
|
|
||||||
if (appname==null || appname.trim().length()==0) {
|
|
||||||
logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, appname is null.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (XxlJobExecutor.getAdminBizList() == null) {
|
|
||||||
logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, adminAddresses is null.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toStop = false;
|
|
||||||
registryThread = new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
// registry
|
|
||||||
while (!toStop) {
|
|
||||||
try {
|
|
||||||
RegistryRequest registryParam = new RegistryRequest(RegistType.EXECUTOR.name(), appname, address);
|
|
||||||
for (AdminBiz adminBiz: XxlJobExecutor.getAdminBizList()) {
|
|
||||||
try {
|
|
||||||
Response<String> registryResult = adminBiz.registry(registryParam);
|
|
||||||
if (registryResult!=null && registryResult.isSuccess()) {
|
|
||||||
registryResult = Response.ofSuccess();
|
|
||||||
logger.debug(">>>>>>>>>>> xxl-job registry success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!toStop) {
|
|
||||||
TimeUnit.SECONDS.sleep(Const.BEAT_TIMEOUT);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.warn(">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// registry remove
|
|
||||||
try {
|
|
||||||
RegistryRequest registryParam = new RegistryRequest(RegistType.EXECUTOR.name(), appname, address);
|
|
||||||
for (AdminBiz adminBiz: XxlJobExecutor.getAdminBizList()) {
|
|
||||||
try {
|
|
||||||
Response<String> registryResult = adminBiz.registryRemove(registryParam);
|
|
||||||
if (registryResult!=null && registryResult.isSuccess()) {
|
|
||||||
registryResult = Response.ofSuccess();
|
|
||||||
logger.info(">>>>>>>>>>> xxl-job registry-remove success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
logger.info(">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.info(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}", registryParam, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info(">>>>>>>>>>> xxl-job, executor registry thread destroy.");
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
registryThread.setDaemon(true);
|
|
||||||
registryThread.setName("xxl-job, executor ExecutorRegistryThread");
|
|
||||||
registryThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toStop() {
|
|
||||||
toStop = true;
|
|
||||||
|
|
||||||
// interrupt and wait
|
|
||||||
if (registryThread != null) {
|
|
||||||
registryThread.interrupt();
|
|
||||||
try {
|
|
||||||
registryThread.join();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
package com.xxl.job.core.thread;
|
||||||
|
|
||||||
|
import com.xxl.job.core.constant.RegistType;
|
||||||
|
import com.xxl.job.core.openapi.AdminBiz;
|
||||||
|
import com.xxl.job.core.openapi.model.RegistryRequest;
|
||||||
|
import com.xxl.job.core.constant.Const;
|
||||||
|
import com.xxl.job.core.executor.XxlJobExecutor;
|
||||||
|
import com.xxl.tool.concurrent.CyclicThread;
|
||||||
|
import com.xxl.tool.core.CollectionTool;
|
||||||
|
import com.xxl.tool.core.StringTool;
|
||||||
|
import com.xxl.tool.response.Response;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by xuxueli on 17/3/2.
|
||||||
|
*/
|
||||||
|
public class ExecutorRegistryThreadHelper {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ExecutorRegistryThreadHelper.class);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* registry thread
|
||||||
|
*/
|
||||||
|
private CyclicThread registryThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start
|
||||||
|
*/
|
||||||
|
public void start(final XxlJobExecutor xxlJobExecutor){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* valid
|
||||||
|
*/
|
||||||
|
if (StringTool.isBlank(xxlJobExecutor.getAppname())) {
|
||||||
|
logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, appname is null.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (CollectionTool.isEmpty(xxlJobExecutor.getAdminBizList())) {
|
||||||
|
logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, adminAddresses is null.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* registry thread
|
||||||
|
*/
|
||||||
|
registryThread = new CyclicThread("ExecutorRegistryThread#registryThread", true, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
RegistryRequest registryParam = new RegistryRequest(RegistType.EXECUTOR.name(), xxlJobExecutor.getAppname(), xxlJobExecutor.getAddress());
|
||||||
|
for (AdminBiz adminBiz: xxlJobExecutor.getAdminBizList()) {
|
||||||
|
try {
|
||||||
|
Response<String> registryResult = adminBiz.registry(registryParam);
|
||||||
|
if (registryResult!=null && registryResult.isSuccess()) {
|
||||||
|
registryResult = Response.ofSuccess();
|
||||||
|
logger.debug(">>>>>>>>>>> xxl-job registry success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, Const.BEAT_TIMEOUT * 1000L, true);
|
||||||
|
registryThread.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop
|
||||||
|
*/
|
||||||
|
public void stop(final XxlJobExecutor xxlJobExecutor) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1、stop registryThread
|
||||||
|
*/
|
||||||
|
registryThread.stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2、registry remove
|
||||||
|
*/
|
||||||
|
registryRemove(xxlJobExecutor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registryRemove(final XxlJobExecutor xxlJobExecutor){
|
||||||
|
RegistryRequest registryParam = new RegistryRequest(RegistType.EXECUTOR.name(), xxlJobExecutor.getAppname(), xxlJobExecutor.getAddress());
|
||||||
|
for (AdminBiz adminBiz: xxlJobExecutor.getAdminBizList()) {
|
||||||
|
try {
|
||||||
|
Response<String> registryResult = adminBiz.registryRemove(registryParam);
|
||||||
|
if (registryResult!=null && registryResult.isSuccess()) {
|
||||||
|
registryResult = Response.ofSuccess();
|
||||||
|
logger.info(">>>>>>>>>>> xxl-job registry-remove success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
logger.info(">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.warn(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}, error:{}", registryParam, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,128 +0,0 @@
|
|||||||
package com.xxl.job.core.thread;
|
|
||||||
|
|
||||||
import com.xxl.job.core.log.XxlJobFileAppender;
|
|
||||||
import com.xxl.tool.core.DateTool;
|
|
||||||
import com.xxl.tool.io.FileTool;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* job file clean thread
|
|
||||||
*
|
|
||||||
* @author xuxueli 2017-12-29 16:23:43
|
|
||||||
*/
|
|
||||||
public class JobLogFileCleanThread {
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(JobLogFileCleanThread.class);
|
|
||||||
|
|
||||||
private static JobLogFileCleanThread instance = new JobLogFileCleanThread();
|
|
||||||
public static JobLogFileCleanThread getInstance(){
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Thread localThread;
|
|
||||||
private volatile boolean toStop = false;
|
|
||||||
public void start(final long logRetentionDays){
|
|
||||||
|
|
||||||
// limit min value
|
|
||||||
if (logRetentionDays < 3 ) {
|
|
||||||
return; // effective only when logRetentionDays >= 3
|
|
||||||
}
|
|
||||||
|
|
||||||
toStop = false;
|
|
||||||
localThread = new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while (!toStop) {
|
|
||||||
try {
|
|
||||||
// clean log dir, over logRetentionDays
|
|
||||||
File[] childDirs = new File(XxlJobFileAppender.getLogPath()).listFiles();
|
|
||||||
if (childDirs!=null && childDirs.length>0) {
|
|
||||||
|
|
||||||
// today
|
|
||||||
Calendar todayCal = Calendar.getInstance();
|
|
||||||
todayCal.set(Calendar.HOUR_OF_DAY,0);
|
|
||||||
todayCal.set(Calendar.MINUTE,0);
|
|
||||||
todayCal.set(Calendar.SECOND,0);
|
|
||||||
todayCal.set(Calendar.MILLISECOND,0);
|
|
||||||
|
|
||||||
Date todayDate = todayCal.getTime();
|
|
||||||
|
|
||||||
// clean expired logfile
|
|
||||||
for (File childFile: childDirs) {
|
|
||||||
|
|
||||||
// valid log-path: must be directory
|
|
||||||
if (!childFile.isDirectory()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// valid day log-path: like "---/2017-12-25/639.log"
|
|
||||||
if (!childFile.getName().contains("-")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse create-day of file-path
|
|
||||||
Date logFileCreateDate = null;
|
|
||||||
try {
|
|
||||||
logFileCreateDate = DateTool.parseDate(childFile.getName());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
if (logFileCreateDate == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check expired
|
|
||||||
Date expiredDate = DateTool.addDays(logFileCreateDate, logRetentionDays);
|
|
||||||
if (todayDate.getTime() > expiredDate.getTime()) {
|
|
||||||
// expired, remove all log of this day
|
|
||||||
FileTool.delete(childFile);
|
|
||||||
//FileUtil.deleteRecursively(childFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
TimeUnit.DAYS.sleep(1);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info(">>>>>>>>>>> xxl-job, executor JobLogFileCleanThread thread destroy.");
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
localThread.setDaemon(true);
|
|
||||||
localThread.setName("xxl-job, executor JobLogFileCleanThread");
|
|
||||||
localThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toStop() {
|
|
||||||
toStop = true;
|
|
||||||
|
|
||||||
if (localThread == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// interrupt and wait
|
|
||||||
localThread.interrupt();
|
|
||||||
try {
|
|
||||||
localThread.join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,108 @@
|
|||||||
|
package com.xxl.job.core.thread;
|
||||||
|
|
||||||
|
import com.xxl.job.core.log.XxlJobFileAppender;
|
||||||
|
import com.xxl.tool.concurrent.CyclicThread;
|
||||||
|
import com.xxl.tool.core.DateTool;
|
||||||
|
import com.xxl.tool.io.FileTool;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* job file clean thread
|
||||||
|
*
|
||||||
|
* @author xuxueli 2017-12-29 16:23:43
|
||||||
|
*/
|
||||||
|
public class JobLogFileCleanThreadHelper {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(JobLogFileCleanThreadHelper.class);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* monitor thread
|
||||||
|
*/
|
||||||
|
private CyclicThread logFileCleanThread;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start
|
||||||
|
*/
|
||||||
|
public void start(final long logRetentionDays){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* limit min value
|
||||||
|
*/
|
||||||
|
if (logRetentionDays < 3 ) {
|
||||||
|
return; // effective only when logRetentionDays >= 3
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* logFileCleanThread
|
||||||
|
*/
|
||||||
|
logFileCleanThread = new CyclicThread("JobLogFileCleanThreadHelper#logFileCleanThread", true, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// clean log dir, over logRetentionDays
|
||||||
|
File[] childDirs = new File(XxlJobFileAppender.getLogPath()).listFiles();
|
||||||
|
if (childDirs!=null && childDirs.length>0) {
|
||||||
|
|
||||||
|
// today
|
||||||
|
Calendar todayCal = Calendar.getInstance();
|
||||||
|
todayCal.set(Calendar.HOUR_OF_DAY,0);
|
||||||
|
todayCal.set(Calendar.MINUTE,0);
|
||||||
|
todayCal.set(Calendar.SECOND,0);
|
||||||
|
todayCal.set(Calendar.MILLISECOND,0);
|
||||||
|
|
||||||
|
Date todayDate = todayCal.getTime();
|
||||||
|
|
||||||
|
// clean expired logfile
|
||||||
|
for (File childFile: childDirs) {
|
||||||
|
|
||||||
|
// valid log-path: must be directory
|
||||||
|
if (!childFile.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// valid day log-path: like "---/2017-12-25/639.log"
|
||||||
|
if (!childFile.getName().contains("-")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse create-day of file-path
|
||||||
|
Date logFileCreateDate = null;
|
||||||
|
try {
|
||||||
|
logFileCreateDate = DateTool.parseDate(childFile.getName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
if (logFileCreateDate == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check expired
|
||||||
|
Date expiredDate = DateTool.addDays(logFileCreateDate, logRetentionDays);
|
||||||
|
if (todayDate.getTime() > expiredDate.getTime()) {
|
||||||
|
// expired, remove all log of this day
|
||||||
|
FileTool.delete(childFile);
|
||||||
|
//FileUtil.deleteRecursively(childFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, DateTool.MILLIS_PER_DAY, true);
|
||||||
|
logFileCleanThread.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
|
||||||
|
// stop logFileCleanThread
|
||||||
|
logFileCleanThread.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,305 +0,0 @@
|
|||||||
package com.xxl.job.core.thread;
|
|
||||||
|
|
||||||
import com.xxl.job.core.openapi.AdminBiz;
|
|
||||||
import com.xxl.job.core.openapi.model.CallbackRequest;
|
|
||||||
import com.xxl.job.core.context.XxlJobContext;
|
|
||||||
import com.xxl.job.core.context.XxlJobHelper;
|
|
||||||
import com.xxl.job.core.constant.Const;
|
|
||||||
import com.xxl.job.core.executor.XxlJobExecutor;
|
|
||||||
import com.xxl.job.core.log.XxlJobFileAppender;
|
|
||||||
import com.xxl.tool.core.ArrayTool;
|
|
||||||
import com.xxl.tool.core.CollectionTool;
|
|
||||||
import com.xxl.tool.core.StringTool;
|
|
||||||
import com.xxl.tool.crypto.Md5Tool;
|
|
||||||
import com.xxl.tool.json.GsonTool;
|
|
||||||
import com.xxl.tool.io.FileTool;
|
|
||||||
import com.xxl.tool.response.Response;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trigger Callback Thread
|
|
||||||
*
|
|
||||||
* Created by xuxueli on 16/7/22.
|
|
||||||
*/
|
|
||||||
public class TriggerCallbackThread {
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(TriggerCallbackThread.class);
|
|
||||||
|
|
||||||
private static final TriggerCallbackThread instance = new TriggerCallbackThread();
|
|
||||||
public static TriggerCallbackThread getInstance(){
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* job results callback queue
|
|
||||||
*/
|
|
||||||
private final LinkedBlockingQueue<CallbackRequest> callBackQueue = new LinkedBlockingQueue<>();
|
|
||||||
public static void pushCallBack(CallbackRequest callback){
|
|
||||||
getInstance().callBackQueue.add(callback);
|
|
||||||
logger.debug(">>>>>>>>>>> xxl-job, push callback request, logId:{}", callback.getLogId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* callback thread
|
|
||||||
*/
|
|
||||||
private Thread triggerCallbackThread;
|
|
||||||
private Thread triggerRetryCallbackThread;
|
|
||||||
private volatile boolean toStop = false;
|
|
||||||
public void start() {
|
|
||||||
|
|
||||||
// valid
|
|
||||||
if (XxlJobExecutor.getAdminBizList() == null) {
|
|
||||||
logger.warn(">>>>>>>>>>> xxl-job, executor callback config fail, adminAddresses is null.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
toStop = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* trigger callback thread
|
|
||||||
*/
|
|
||||||
|
|
||||||
triggerCallbackThread = new Thread(new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
// normal callback
|
|
||||||
while(!toStop){
|
|
||||||
try {
|
|
||||||
CallbackRequest callback = getInstance().callBackQueue.take();
|
|
||||||
if (callback != null) {
|
|
||||||
|
|
||||||
// collect callback data
|
|
||||||
List<CallbackRequest> callbackParamList = new ArrayList<>();
|
|
||||||
callbackParamList.add(callback); // add one element
|
|
||||||
int drainToNum = getInstance().callBackQueue.drainTo(callbackParamList); // drainTo other all elements
|
|
||||||
|
|
||||||
// do callback, will retry if error
|
|
||||||
if (CollectionTool.isNotEmpty(callbackParamList)) {
|
|
||||||
doCallback(callbackParamList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// thead stop, callback lasttime
|
|
||||||
try {
|
|
||||||
// collect callback data
|
|
||||||
List<CallbackRequest> callbackParamList = new ArrayList<>();
|
|
||||||
int drainToNum = getInstance().callBackQueue.drainTo(callbackParamList);
|
|
||||||
|
|
||||||
// do callback
|
|
||||||
if (CollectionTool.isNotEmpty(callbackParamList)) {
|
|
||||||
doCallback(callbackParamList);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info(">>>>>>>>>>> xxl-job, executor callback thread destroy.");
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
triggerCallbackThread.setDaemon(true);
|
|
||||||
triggerCallbackThread.setName("xxl-job, executor TriggerCallbackThread");
|
|
||||||
triggerCallbackThread.start();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* callback fail retry thread
|
|
||||||
*/
|
|
||||||
triggerRetryCallbackThread = new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while(!toStop){
|
|
||||||
try {
|
|
||||||
retryFailCallbackFile();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
TimeUnit.SECONDS.sleep(Const.BEAT_TIMEOUT);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!toStop) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info(">>>>>>>>>>> xxl-job, executor retry callback thread destroy.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
triggerRetryCallbackThread.setDaemon(true);
|
|
||||||
triggerRetryCallbackThread.setName("xxl-job, executor TriggerRetryCallbackThread");
|
|
||||||
triggerRetryCallbackThread.start();
|
|
||||||
|
|
||||||
}
|
|
||||||
public void toStop(){
|
|
||||||
toStop = true;
|
|
||||||
// stop callback, interrupt and wait
|
|
||||||
if (triggerCallbackThread != null) { // support empty admin address
|
|
||||||
triggerCallbackThread.interrupt();
|
|
||||||
try {
|
|
||||||
triggerCallbackThread.join();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop retry, interrupt and wait
|
|
||||||
if (triggerRetryCallbackThread != null) {
|
|
||||||
triggerRetryCallbackThread.interrupt();
|
|
||||||
try {
|
|
||||||
triggerRetryCallbackThread.join();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* do callback, will retry if error
|
|
||||||
*
|
|
||||||
* @param callbackParamList callback param list
|
|
||||||
*/
|
|
||||||
private void doCallback(List<CallbackRequest> callbackParamList){
|
|
||||||
boolean callbackRet = false;
|
|
||||||
// callback, will retry if error
|
|
||||||
for (AdminBiz adminBiz: XxlJobExecutor.getAdminBizList()) {
|
|
||||||
try {
|
|
||||||
Response<String> callbackResult = adminBiz.callback(callbackParamList);
|
|
||||||
if (callbackResult!=null && callbackResult.isSuccess()) {
|
|
||||||
callbackLog(callbackParamList, "<br>----------- xxl-job job callback finish.");
|
|
||||||
callbackRet = true;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
callbackLog(callbackParamList, "<br>----------- xxl-job job callback fail, callbackResult:" + callbackResult);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
callbackLog(callbackParamList, "<br>----------- xxl-job job callback error, errorMsg:" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!callbackRet) {
|
|
||||||
appendFailCallbackFile(callbackParamList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* callback log
|
|
||||||
*/
|
|
||||||
private void callbackLog(List<CallbackRequest> callbackParamList, String logContent){
|
|
||||||
for (CallbackRequest callbackParam: callbackParamList) {
|
|
||||||
String logFileName = XxlJobFileAppender.makeLogFileName(new Date(callbackParam.getLogDateTim()), callbackParam.getLogId());
|
|
||||||
XxlJobContext.setXxlJobContext(new XxlJobContext(
|
|
||||||
-1,
|
|
||||||
null,
|
|
||||||
-1,
|
|
||||||
-1,
|
|
||||||
logFileName,
|
|
||||||
-1,
|
|
||||||
-1));
|
|
||||||
XxlJobHelper.log(logContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------- fail-callback file ----------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fail-callback file name
|
|
||||||
*/
|
|
||||||
private static final String failCallbackFileName = XxlJobFileAppender
|
|
||||||
.getCallbackLogPath()
|
|
||||||
.concat(File.separator)
|
|
||||||
.concat("xxl-job-callback-{x}")
|
|
||||||
.concat(".log");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* append fail-callback file
|
|
||||||
*
|
|
||||||
* @param callbackParamList callback param list
|
|
||||||
*/
|
|
||||||
private void appendFailCallbackFile(List<CallbackRequest> callbackParamList) {
|
|
||||||
// valid
|
|
||||||
if (CollectionTool.isEmpty(callbackParamList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate callback data
|
|
||||||
String callbackData = GsonTool.toJson(callbackParamList);
|
|
||||||
String callbackDataMd5 = Md5Tool.md5(callbackData);
|
|
||||||
|
|
||||||
|
|
||||||
// create file
|
|
||||||
String finalLogFileName = failCallbackFileName.replace("{x}", callbackDataMd5);
|
|
||||||
|
|
||||||
// write callback log
|
|
||||||
try {
|
|
||||||
FileTool.writeString(finalLogFileName, callbackData);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error(">>>>>>>>>>> TriggerCallbackThread appendFailCallbackFile error, finalLogFileName:{}", finalLogFileName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* retry fail-callback file
|
|
||||||
*/
|
|
||||||
private void retryFailCallbackFile() {
|
|
||||||
|
|
||||||
// valid
|
|
||||||
File callbackLogPath = new File(XxlJobFileAppender.getCallbackLogPath());
|
|
||||||
if (!callbackLogPath.exists()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// valid file type: must be directory
|
|
||||||
if (!FileTool.isDirectory(callbackLogPath)) {
|
|
||||||
FileTool.delete(callbackLogPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// valid file in path: pass if empty
|
|
||||||
if (ArrayTool.isEmpty(callbackLogPath.listFiles())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// load and clear file, do retry
|
|
||||||
for (File callbackLogFile: callbackLogPath.listFiles()) {
|
|
||||||
try {
|
|
||||||
// load data
|
|
||||||
String callbackData = FileTool.readString(callbackLogFile.getPath());
|
|
||||||
if (StringTool.isBlank(callbackData)) {
|
|
||||||
FileTool.delete(callbackLogFile);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse callback param
|
|
||||||
List<CallbackRequest> callbackParamList = GsonTool.fromJsonList(callbackData, CallbackRequest.class);
|
|
||||||
FileTool.delete(callbackLogFile);
|
|
||||||
|
|
||||||
// retry callback
|
|
||||||
doCallback(callbackParamList);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error(">>>>>>>>>>> TriggerCallbackThread retryFailCallbackFile error, callbackLogFile:{}", callbackLogFile.getPath(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,237 @@
|
|||||||
|
package com.xxl.job.core.thread;
|
||||||
|
|
||||||
|
import com.xxl.job.core.constant.Const;
|
||||||
|
import com.xxl.job.core.context.XxlJobContext;
|
||||||
|
import com.xxl.job.core.context.XxlJobHelper;
|
||||||
|
import com.xxl.job.core.executor.XxlJobExecutor;
|
||||||
|
import com.xxl.job.core.log.XxlJobFileAppender;
|
||||||
|
import com.xxl.job.core.openapi.AdminBiz;
|
||||||
|
import com.xxl.job.core.openapi.model.CallbackRequest;
|
||||||
|
import com.xxl.tool.concurrent.CyclicThread;
|
||||||
|
import com.xxl.tool.concurrent.MessageQueue;
|
||||||
|
import com.xxl.tool.core.ArrayTool;
|
||||||
|
import com.xxl.tool.core.CollectionTool;
|
||||||
|
import com.xxl.tool.core.StringTool;
|
||||||
|
import com.xxl.tool.crypto.Md5Tool;
|
||||||
|
import com.xxl.tool.io.FileTool;
|
||||||
|
import com.xxl.tool.json.GsonTool;
|
||||||
|
import com.xxl.tool.response.Response;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger Callback Thread
|
||||||
|
*
|
||||||
|
* Created by xuxueli on 16/7/22.
|
||||||
|
*/
|
||||||
|
public class TriggerCallbackThreadHelper {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(TriggerCallbackThreadHelper.class);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* callback message-queue
|
||||||
|
*/
|
||||||
|
private volatile MessageQueue<CallbackRequest> callbackMessageQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retry callback-file thread
|
||||||
|
*/
|
||||||
|
private CyclicThread retryCallbackThread;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start
|
||||||
|
*/
|
||||||
|
public void start(final XxlJobExecutor xxlJobExecutor) {
|
||||||
|
|
||||||
|
// valid
|
||||||
|
if (xxlJobExecutor.getAdminBizList() == null) {
|
||||||
|
logger.warn(">>>>>>>>>>> xxl-job, executor callback config fail, adminAddresses is null.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1、callback message-queue
|
||||||
|
*/
|
||||||
|
callbackMessageQueue = new MessageQueue<CallbackRequest>(
|
||||||
|
"TriggerCallbackThreadHelper#callbackMessageQueue",
|
||||||
|
messages -> {
|
||||||
|
|
||||||
|
// do callback
|
||||||
|
doCallback(messages, xxlJobExecutor);
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
50);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2、retry callback-file thread
|
||||||
|
*/
|
||||||
|
retryCallbackThread = new CyclicThread("TriggerCallbackThreadHelper#retryCallbackThread", true, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
// valid empty path
|
||||||
|
File callbackLogPath = new File(XxlJobFileAppender.getCallbackLogPath());
|
||||||
|
if (!callbackLogPath.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// valid file type: must be directory
|
||||||
|
if (!FileTool.isDirectory(callbackLogPath)) {
|
||||||
|
FileTool.delete(callbackLogPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// valid none file
|
||||||
|
if (ArrayTool.isEmpty(callbackLogPath.listFiles())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load and clear file, do retry
|
||||||
|
for (File callbackLogFile: callbackLogPath.listFiles()) {
|
||||||
|
try {
|
||||||
|
// load data
|
||||||
|
String callbackData = FileTool.readString(callbackLogFile.getPath());
|
||||||
|
if (StringTool.isBlank(callbackData)) {
|
||||||
|
FileTool.delete(callbackLogFile);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse callback param
|
||||||
|
List<CallbackRequest> callbackParamList = GsonTool.fromJsonList(callbackData, CallbackRequest.class);
|
||||||
|
FileTool.delete(callbackLogFile);
|
||||||
|
|
||||||
|
// retry callback
|
||||||
|
doCallback(callbackParamList, xxlJobExecutor);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error(">>>>>>>>>>> TriggerCallbackThread retryFailCallbackFile error, callbackLogFile:{}", callbackLogFile.getPath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, Const.BEAT_TIMEOUT * 1000L, true);
|
||||||
|
retryCallbackThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop
|
||||||
|
*/
|
||||||
|
public void stop(){
|
||||||
|
// 1、stop callbackMessageQueue
|
||||||
|
if (callbackMessageQueue != null) {
|
||||||
|
callbackMessageQueue.stop(); // attempt wait for callback finish
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2、stop retryCallbackThread
|
||||||
|
retryCallbackThread.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* submit callback message
|
||||||
|
*/
|
||||||
|
public void pushCallBack(CallbackRequest callback){
|
||||||
|
if (!callbackMessageQueue.produce(callback)) {
|
||||||
|
doCallback(new ArrayList<>(Collections.singletonList(callback)), XxlJobExecutor.getInstance());
|
||||||
|
}
|
||||||
|
logger.debug(">>>>>>>>>>> xxl-job, push callback request, logId:{}", callback.getLogId());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------- do callback ----------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* do callback, will retry if error
|
||||||
|
*
|
||||||
|
* @param callbackParamList callback param list
|
||||||
|
*/
|
||||||
|
private void doCallback(List<CallbackRequest> callbackParamList, final XxlJobExecutor xxlJobExecutor){
|
||||||
|
boolean callbackRet = false;
|
||||||
|
|
||||||
|
// callback request, will retry + append-log if fail
|
||||||
|
for (AdminBiz adminBiz: xxlJobExecutor.getAdminBizList()) {
|
||||||
|
try {
|
||||||
|
Response<String> callbackResult = adminBiz.callback(callbackParamList);
|
||||||
|
if (callbackResult!=null && callbackResult.isSuccess()) {
|
||||||
|
appendCallbackResult(callbackParamList, "<br>----------- xxl-job job callback finish.");
|
||||||
|
callbackRet = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
appendCallbackResult(callbackParamList, "<br>----------- xxl-job job callback fail, callbackResult:" + callbackResult);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
appendCallbackResult(callbackParamList, "<br>----------- xxl-job job callback error, errorMsg:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write callback-file, will retry later
|
||||||
|
if (!callbackRet) {
|
||||||
|
writeCallbackLog(callbackParamList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append callback result, to each joblog
|
||||||
|
*/
|
||||||
|
private void appendCallbackResult(List<CallbackRequest> callbackParamList, String logContent){
|
||||||
|
for (CallbackRequest callbackParam: callbackParamList) {
|
||||||
|
String logFileName = XxlJobFileAppender.makeLogFileName(new Date(callbackParam.getLogDateTim()), callbackParam.getLogId());
|
||||||
|
XxlJobContext.setXxlJobContext(new XxlJobContext(
|
||||||
|
-1,
|
||||||
|
null,
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
logFileName,
|
||||||
|
-1,
|
||||||
|
-1));
|
||||||
|
XxlJobHelper.log(logContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------- fail-callback file ----------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fail-callback file name
|
||||||
|
*/
|
||||||
|
private static final String failCallbackFileName = XxlJobFileAppender
|
||||||
|
.getCallbackLogPath()
|
||||||
|
.concat(File.separator)
|
||||||
|
.concat("xxl-job-callback-{x}")
|
||||||
|
.concat(".log");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write fail-callback file, will retry later
|
||||||
|
*
|
||||||
|
* @param callbackParamList callback param list
|
||||||
|
*/
|
||||||
|
private void writeCallbackLog(List<CallbackRequest> callbackParamList) {
|
||||||
|
// valid
|
||||||
|
if (CollectionTool.isEmpty(callbackParamList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate callback data
|
||||||
|
String callbackData = GsonTool.toJson(callbackParamList);
|
||||||
|
String callbackDataMd5 = Md5Tool.md5(callbackData);
|
||||||
|
|
||||||
|
|
||||||
|
// create file
|
||||||
|
String finalLogFileName = failCallbackFileName.replace("{x}", callbackDataMd5);
|
||||||
|
|
||||||
|
// write callback log
|
||||||
|
try {
|
||||||
|
FileTool.writeString(finalLogFileName, callbackData);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error(">>>>>>>>>>> TriggerCallbackThread appendFailCallbackFile error, finalLogFileName:{}", finalLogFileName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in new issue