项目完整解读源码地址:https://gitee.com/duhai123/xxl_job_study
执行器里面,实际上是在应用中内嵌了一个jetty服务器, 服务器在xxlJobExecutor 初始化的时候启动。 本次示例代码中是由spring-boot 中截取而来
第一步:初始化加载执行器配置XxlJobConfig
/**
* 注入bean
* @return
*/
@Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppName(appName);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
执行器Spring版本实现:第二步加载执行器:XxlJobSpringExecutor
/**
* 开始
*
* @see com.xxl.job.core.executor.XxlJobExecutor#start()
*/
@Override
public void start() throws Exception {
//初始化JobHandler的仓库
initJobHandlerRepository(applicationContext);
// refresh GlueFactory
GlueFactory.refreshInstance(1);
//XxlJobExecutor启动
super.start();
}
initJobHandlerRepository:注册jobhandler到map中(@ComponentScan(basePackages = "com.xxl.job.executor.service.jobhandler")中扫描到的)
/**
* 注册jobhandler到map中
*
* @param applicationContext
*/
private void initJobHandlerRepository(ApplicationContext applicationContext) {
if (applicationContext == null) {
return;
}
// 获取有JobHandler注解的所有类
Map serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class);
if (serviceBeanMap != null && serviceBeanMap.size() > 0) {
for (Object serviceBean : serviceBeanMap.values()) {
if (serviceBean instanceof IJobHandler) {
//获取注解中name的值@JobHandler(value="commandJobHandler")
String name = serviceBean.getClass().getAnnotation(JobHandler.class).value();
IJobHandler handler = (IJobHandler) serviceBean;
//从jobHandlerRepository中找,如果存在就抛出异常
if (loadJobHandler(name) != null) {
throw new RuntimeException("xxl-job jobhandler naming conflicts.");
}
//注册到jobHandlerRepository的static的Map中去,方便在调用的时候找到
registJobHandler(name, handler);
}
}
}
}
XxlJobExecutor:父执行器,最终都是这个启动
/**
* 初始化
*
* @throws Exception
*/
public void start() throws Exception {
// 初始化log日志
XxlJobFileAppender.initLogPath(logPath);
// init admin-client
// 初始化调用调度中心的client列表
initAdminBizList(adminAddresses, accessToken);
// init JobLogFileCleanThread
// 初始化日志文件清理线程
JobLogFileCleanThread.getInstance().start(logRetentionDays);
// init TriggerCallbackThread
// 初始化触发器回调线程(用RPC回调调度中心接口)
TriggerCallbackThread.getInstance().start();
// init executor-server
// 初始化执行器服务
port = port > 0 ? port : NetUtil.findAvailablePort(9999);
// 获取ip
ip = (ip != null && ip.trim().length() > 0) ? ip : IpUtil.getIp();
initRpcProvider(ip, port, appName, accessToken);
}
x先看initAdminBizList:初始化调用调度中心的client列表
// -------- admin-client (rpc invoker) --------调度中心client(用这个去调用调度中心的接口)--------TODO---------------
// 调度中心client列表
private static List adminBizList;
/**
* 初始化调度中心
*
* @param adminAddresses
* xxl.job.admin.addresses
* @param accessToken
* xxl.job.accessToken
* @throws Exception
*/
private 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,用这个类可以调用到调度中心的RPC接口
AdminBiz adminBiz = (AdminBiz) new XxlRpcReferenceBean(NetEnum.JETTY,
Serializer.SerializeEnum.HESSIAN.getSerializer(), CallType.SYNC, AdminBiz.class, null,
10000, addressUrl, accessToken, null).getObject();
if (adminBizList == null) {
adminBizList = new ArrayList();
}
adminBizList.add(adminBiz);
}
}
}
}
TriggerCallbackThread:初始化触发器回调线程(用RPC回调调度中心接口)
/**
* 开始执行,不停的回调
*/
public void start() {
// valid
if (XxlJobExecutor.getAdminBizList() == null) {
logger.warn(">>>>>>>>>>> xxl-job, executor callback config fail, adminAddresses is null.");
return;
}
// callback
triggerCallbackThread = new Thread(new Runnable() {
@Override
public void run() {
// 正常的回调
while (!toStop) {
try {
HandleCallbackParam callback = getInstance().callBackQueue.take();
if (callback != null) {
// callback list param
List callbackParamList = new ArrayList();
// int drainToNum =
// 移除此队列中所有可用的元素,并将它们添加到给定 callbackParamList 中
getInstance().callBackQueue.drainTo(callbackParamList);
callbackParamList.add(callback);
// callback, will retry if error
if (callbackParamList != null && callbackParamList.size() > 0) {
doCallback(callbackParamList);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
// last callback===停止后最后的回调
try {
List callbackParamList = new ArrayList();
// int drainToNum =
// 移除此队列中所有可用的元素,并将它们添加到给定 callbackParamList 中
getInstance().callBackQueue.drainTo(callbackParamList);
if (callbackParamList != null && callbackParamList.size() > 0) {
doCallback(callbackParamList);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
logger.info(">>>>>>>>>>> xxl-job, executor callback thread destory.");
}
});
triggerCallbackThread.setDaemon(true);
triggerCallbackThread.start();
// 失败回调文件重试回调进程
triggerRetryCallbackThread = new Thread(new Runnable() {
@Override
public void run() {
while (!toStop) {
try {
// 读取失败回调文件,重试进行回调操作
retryFailCallbackFile();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
try {
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
} catch (InterruptedException e) {
logger.warn(">>>>>>>>>>> xxl-job, executor retry callback thread interrupted, error msg:{}",
e.getMessage());
}
}
logger.info(">>>>>>>>>>> xxl-job, executor retry callback thread destory.");
}
});
triggerRetryCallbackThread.setDaemon(true);
triggerRetryCallbackThread.start();
}
initRpcProvider:初始化执行器服务
/**
* 初始化RpcProvider----- TODO ----
* (与调度中心不同,这边内嵌了一个jetty服务器
* 有一个web端口server.port,一个RPC接口端口xxl.job.executor.port)
*
* @param ip
* @param port
* @param appName
* @param accessToken
* @throws Exception
*/
private void initRpcProvider(String ip, int port, String appName, String accessToken) throws Exception {
// init invoker factory
xxlRpcInvokerFactory = new XxlRpcInvokerFactory();
// init, provider factory 初始化提供者工厂
String address = IpUtil.getIpPort(ip, port);
Map serviceRegistryParam = new HashMap();
serviceRegistryParam.put("appName", appName);
serviceRegistryParam.put("address", address);
// 初始化提供者工厂
xxlRpcProviderFactory = new XxlRpcProviderFactory();
// 最后两个参数与执行器注册线程的相关 TODO
xxlRpcProviderFactory.initConfig(NetEnum.JETTY, Serializer.SerializeEnum.HESSIAN.getSerializer(), ip, port,
accessToken, ExecutorServiceRegistry.class, serviceRegistryParam);
// add services 增加服务接口和服务实现,供给调用中心调用
xxlRpcProviderFactory.addService(ExecutorBiz.class.getName(), null, new ExecutorBizImpl());
// RpcProvider工厂启动:
// 1.执行器注册线程serviceRegistry启动
// 2.内置jetty service启动
xxlRpcProviderFactory.start();
}
1.执行器注册线程serviceRegistry启动:
/**
* xxlRpcProviderFactory.start();内部启用:远程注册到调度中心
*
* @see com.xxl.rpc.registry.ServiceRegistry#start(java.util.Map)
*/
@Override
public void start(Map param) {
// start registry:开始执行器注册线程
ExecutorRegistryThread.getInstance().start(param.get("appName"), param.get("address"));
}
ExecutorRegistryThread:执行器注册线程
/**
* 执行器信息注册到注册中心
*
* @param appName
* @param address
*/
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;
}
// 注册线程
registryThread = new Thread(new Runnable() {
@Override
public void run() {
// registry
while (!toStop) {// 每隔一个时间注册一次,保证执行器的活着状态
try {
RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(),
appName, address);
for (AdminBiz adminBiz : XxlJobExecutor.getAdminBizList()) {
try {
// 执行器信息注册到注册中心(XxlRpcReferenceBean 来代理的adminBiz)
ReturnT registryResult = adminBiz.registry(registryParam);
if (registryResult != null && ReturnT.SUCCESS_CODE == registryResult.getCode()) {
registryResult = ReturnT.SUCCESS;
logger.info(
">>>>>>>>>>> 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 (Exception e) {
logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
try {
// 休息一个时间
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
} catch (InterruptedException e) {
logger.warn(">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}",
e.getMessage());
}
}
// 最后结束的时候,把执行器从远程移除
try {
RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(), appName,
address);
for (AdminBiz adminBiz : XxlJobExecutor.getAdminBizList()) {
try {
// 执行器从注册中心删除
ReturnT registryResult = adminBiz.registryRemove(registryParam);
if (registryResult != null && ReturnT.SUCCESS_CODE == registryResult.getCode()) {
registryResult = ReturnT.SUCCESS;
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 (Exception e) {
logger.info(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}", registryParam,
e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
logger.info(">>>>>>>>>>> xxl-job, executor registry thread destory.");
}
});
registryThread.setDaemon(true);
registryThread.start();
}
2.内置jetty service启动(大概看一看,仔细研究请看xxl-pc)
/**
* JettyServer启动
*
* @see com.xxl.rpc.remoting.net.Server#start(com.xxl.rpc.remoting.provider.XxlRpcProviderFactory)
*/
@Override
public void start(final XxlRpcProviderFactory xxlRpcProviderFactory) throws Exception {
thread = new Thread(new Runnable() {
@Override
public void run() {
// The Server
server = new org.eclipse.jetty.server.Server(new QueuedThreadPool()); // default maxThreads 200,
// minThreads 8
// TODO, thread config, change to async servlet
// HTTP connector
ServerConnector connector = new ServerConnector(server);
/*
* if (ip!=null && ip.trim().length()>0) { // TODO, support set registry ip、and bind ip
* //connector.setHost(ip); // The network interface this connector binds to as an IP address or a
* hostname. If null or 0.0.0.0, then bind to all interfaces. }
*/
connector.setPort(xxlRpcProviderFactory.getPort());
server.setConnectors(new Connector[] { connector });
// Set a handler
HandlerCollection handlerc = new HandlerCollection();
// 设置一个处理器JettyServerHandler(重点)
handlerc.setHandlers(new Handler[] { new JettyServerHandler(xxlRpcProviderFactory) });
server.setHandler(handlerc);
try {
server.start();
logger.info(">>>>>>>>>>> xxl-rpc remoting server start success, nettype = {}, port = {}",
JettyServer.class.getName(), xxlRpcProviderFactory.getPort());
onStarted();
server.join();
} catch (Exception e) {
logger.error(">>>>>>>>>>> xxl-rpc remoting server start error.", e);
} finally {
try {
stop();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
});
thread.setDaemon(true); // daemon, service jvm, user thread leave >>> daemon leave >>> jvm leave
thread.start();
}
JettyServerHandler 接收请求后的处理流程:
/**
* 处理方法{@inheritDoc}
*
* @see org.eclipse.jetty.server.Handler#handle(java.lang.String, org.eclipse.jetty.server.Request,
* javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
if ("/services".equals(target)) { // services mapping
StringBuffer stringBuffer = new StringBuffer("");
for (String serviceKey : xxlRpcProviderFactory.getServiceData().keySet()) {
stringBuffer.append("").append(serviceKey).append(": ")
.append(xxlRpcProviderFactory.getServiceData().get(serviceKey)).append(" ");
}
stringBuffer.append(" ");
writeResponse(baseRequest, response, stringBuffer.toString().getBytes());
return;
} else { // default remoting mapping = 默认的远程mapping
// request parse
XxlRpcRequest xxlRpcRequest = null;
try {
// 获取远程请求信息
xxlRpcRequest = parseRequest(request);
} catch (Exception e) {
writeResponse(baseRequest, response, ThrowableUtil.toString(e).getBytes());
return;
}
// invoke=执行对应类的方法
XxlRpcResponse xxlRpcResponse = xxlRpcProviderFactory.invokeService(xxlRpcRequest);
// response-serialize + response-write 序列化返回信息
byte[] responseBytes = xxlRpcProviderFactory.getSerializer().serialize(xxlRpcResponse);
// 回写
writeResponse(baseRequest, response, responseBytes);
}
}
通过上面的handle中的代码可以知道,主要的执行逻辑在xxlRpcProviderFactory.invokeService的方法中,
/**
* invoke service
* 执行对应类的方法
*
* @param xxlRpcRequest
* @return
*/
public XxlRpcResponse invokeService(XxlRpcRequest xxlRpcRequest) {
// make response
XxlRpcResponse xxlRpcResponse = new XxlRpcResponse();
xxlRpcResponse.setRequestId(xxlRpcRequest.getRequestId());
// match service bean
String serviceKey = makeServiceKey(xxlRpcRequest.getClassName(), xxlRpcRequest.getVersion());
Object serviceBean = serviceData.get(serviceKey);
// valid
if (serviceBean == null) {
xxlRpcResponse.setErrorMsg("The serviceKey[" + serviceKey + "] not found.");
return xxlRpcResponse;
}
if (System.currentTimeMillis() - xxlRpcRequest.getCreateMillisTime() > 3 * 60 * 1000) {
xxlRpcResponse.setErrorMsg("The timestamp difference between admin and executor exceeds the limit.");
return xxlRpcResponse;
}
if (accessToken != null && accessToken.trim().length() > 0
&& !accessToken.trim().equals(xxlRpcRequest.getAccessToken())) {
xxlRpcResponse.setErrorMsg("The access token[" + xxlRpcRequest.getAccessToken() + "] is wrong.");
return xxlRpcResponse;
}
/*
* 通过反射方式来执行方法
*/
try {
Class> serviceClass = serviceBean.getClass();
String methodName = xxlRpcRequest.getMethodName();
Class>[] parameterTypes = xxlRpcRequest.getParameterTypes();
Object[] parameters = xxlRpcRequest.getParameters();
Method method = serviceClass.getMethod(methodName, parameterTypes);
method.setAccessible(true);
// 执行方法
Object result = method.invoke(serviceBean, parameters);
xxlRpcResponse.setResult(result);
} catch (Throwable t) {
logger.error("xxl-rpc provider invokeService error.", t);
xxlRpcResponse.setErrorMsg(ThrowableUtil.toString(t));
}
return xxlRpcResponse;
}
通过调度中心发过来的参数,以及执行器的处理逻辑,我们有理由可以得出此时是执行的是ExecutorBizImpl中的run方法
/**
* 运行某一个触发器: 1.不动/创建/替换一个任务执行线程
*
* @see com.xxl.job.core.biz.ExecutorBiz#run(com.xxl.job.core.biz.model.TriggerParam)
*/
@Override
public ReturnT run(TriggerParam triggerParam) {
// load old:jobHandler + jobThread
// 获取是否有这个JobId的线程
JobThread jobThread = XxlJobExecutor.loadJobThread(triggerParam.getJobId());
// handler
IJobHandler jobHandler = jobThread != null ? jobThread.getHandler() : null;
// 删除旧的线程的原因
String removeOldReason = null;
/*
* valid:jobHandler + jobThread 验证jobHandler 和 jobThread
*/
GlueTypeEnum glueTypeEnum = GlueTypeEnum.match(triggerParam.getGlueType());
if (GlueTypeEnum.BEAN == glueTypeEnum) {// bean模式
// 查询jobHandlerRepository里面是否有该名字的JobHandler
IJobHandler newJobHandler = XxlJobExecutor.loadJobHandler(triggerParam.getExecutorHandler());
// valid old jobThread
if (jobThread != null && jobHandler != newJobHandler) {// 两个不一样
// change handler, need kill old thread
removeOldReason = "change jobhandler or glue type, and terminate the old job thread.";
jobThread = null;
jobHandler = null;
}
// valid handler
if (jobHandler == null) {
jobHandler = newJobHandler;
if (jobHandler == null) {
return new ReturnT(ReturnT.FAIL_CODE,
"job handler [" + triggerParam.getExecutorHandler() + "] not found.");
}
}
} else if (GlueTypeEnum.GLUE_GROOVY == glueTypeEnum) {
// valid old jobThread
if (jobThread != null
&& !(jobThread.getHandler() instanceof GlueJobHandler && ((GlueJobHandler) jobThread.getHandler())
.getGlueUpdatetime() == triggerParam.getGlueUpdatetime())) {
// change handler or gluesource updated, need kill old thread
removeOldReason = "change job source or glue type, and terminate the old job thread.";
jobThread = null;
jobHandler = null;
}
// valid handler
if (jobHandler == null) {
try {
IJobHandler originJobHandler = GlueFactory.getInstance()
.loadNewInstance(triggerParam.getGlueSource());
jobHandler = new GlueJobHandler(originJobHandler, triggerParam.getGlueUpdatetime());
} catch (Exception e) {
logger.error(e.getMessage(), e);
return new ReturnT(ReturnT.FAIL_CODE, e.getMessage());
}
}
} else if (glueTypeEnum != null && glueTypeEnum.isScript()) {
// valid old jobThread
if (jobThread != null && !(jobThread.getHandler() instanceof ScriptJobHandler
&& ((ScriptJobHandler) jobThread.getHandler()).getGlueUpdatetime() == triggerParam
.getGlueUpdatetime())) {
// change script or gluesource updated, need kill old thread
removeOldReason = "change job source or glue type, and terminate the old job thread.";
jobThread = null;
jobHandler = null;
}
// valid handler
if (jobHandler == null) {
jobHandler = new ScriptJobHandler(triggerParam.getJobId(), triggerParam.getGlueUpdatetime(),
triggerParam.getGlueSource(), GlueTypeEnum.match(triggerParam.getGlueType()));
}
} else {
return new ReturnT(ReturnT.FAIL_CODE, "glueType[" + triggerParam.getGlueType() + "] is not valid.");
}
// executor block strategy
if (jobThread != null) {
// 阻塞处理策略
ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum
.match(triggerParam.getExecutorBlockStrategy(), null);
if (ExecutorBlockStrategyEnum.DISCARD_LATER == blockStrategy) {// 并行
// discard when running
if (jobThread.isRunningOrHasQueue()) {
return new ReturnT(ReturnT.FAIL_CODE,
"block strategy effect:" + ExecutorBlockStrategyEnum.DISCARD_LATER.getTitle());
}
} else if (ExecutorBlockStrategyEnum.COVER_EARLY == blockStrategy) {// 覆盖
// kill running jobThread
if (jobThread.isRunningOrHasQueue()) {
removeOldReason = "block strategy effect:" + ExecutorBlockStrategyEnum.COVER_EARLY.getTitle();
jobThread = null;
}
} else {
// just queue trigger
}
}
// replace thread (new or exists invalid)
if (jobThread == null) {// 新建一个jobThread代替老的
jobThread = XxlJobExecutor.registJobThread(triggerParam.getJobId(), jobHandler, removeOldReason);
}
// push data to queue:触发器增加一次要触发执行的任务
ReturnT pushResult = jobThread.pushTriggerQueue(triggerParam);
return pushResult;
}
通过上面我们可以发现, 执行executorBiz的run 方法的时候, 首先会通过JOBID,从本地线程库里面获取该任务对应的线程,同时,如果任务的JobHandler有更新的话,
那么会自动使用最新的jobHandler , 同时根据任务的阻塞策略。 执行不同的操作。 最终,如果是第一次执行任务的时候,系统会分配给改任务一个线程,同时启动该线程。
接下来,可以在具体看一下JobThread 的run方法,看下最终的任务是如何执行的。
@Override
public void run() {
// init
try {
// 执行IJobHandler 中的init方法,以后如果有一些,在执行handler之前的初始化的工作,可以覆写这个方法
handler.init();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
// execute= stop 为fasle的时候执行
while (!toStop) {
running = false;
// 执行次数
idleTimes++;
TriggerParam triggerParam = null;
ReturnT executeResult = null;
try {
// to check toStop signal, we need cycle, so wo cannot use queue.take(), instand of poll(timeout)
// 从linkBlockingQueue中获取数据,如果3秒获取不到,则返回null
triggerParam = triggerQueue.poll(3L, TimeUnit.SECONDS);
if (triggerParam != null) {
running = true;
// 将运行次数清空,保证运行90秒空闲之后会被移除
idleTimes = 0;
// 去掉这条数据
triggerLogIdSet.remove(triggerParam.getLogId());
// log filename, like "logPath/yyyy-MM-dd/9999.log"
// 创建日志
String logFileName = XxlJobFileAppender.makeLogFileName(new Date(triggerParam.getLogDateTim()),
triggerParam.getLogId());
XxlJobFileAppender.contextHolder.set(logFileName);
// 写入分片信息, 将当前机器的分片标记和分片总数写入到ShardingUtil中,到时候,可以在handler中通过这个工具类获取
ShardingUtil.setShardingVo(new ShardingUtil.ShardingVO(triggerParam.getBroadcastIndex(),
triggerParam.getBroadcastTotal()));
// execute
XxlJobLogger.log("
----------- xxl-job job execute start -----------
----------- Param:"
+ triggerParam.getExecutorParams());
// 超时时间大于0
if (triggerParam.getExecutorTimeout() > 0) {
// limit timeout
Thread futureThread = null;
try {
final TriggerParam triggerParamTmp = triggerParam;
// 开一个线程执行
FutureTask> futureTask = new FutureTask>(
new Callable>() {
@Override
public ReturnT call() throws Exception {
// 执行。。。
return handler.execute(triggerParamTmp.getExecutorParams());
}
});
futureThread = new Thread(futureTask);
futureThread.start();
// 获取执行结果
executeResult = futureTask.get(triggerParam.getExecutorTimeout(), TimeUnit.SECONDS);
} catch (TimeoutException e) {
XxlJobLogger.log("
----------- xxl-job job execute timeout");
XxlJobLogger.log(e);
executeResult = new ReturnT(IJobHandler.FAIL_TIMEOUT.getCode(),
"job execute timeout ");
} finally {
futureThread.interrupt();
}
} else {// 否则直接执行
// just execute
executeResult = handler.execute(triggerParam.getExecutorParams());
}
if (executeResult == null) {
executeResult = IJobHandler.FAIL;
}
XxlJobLogger
.log("
----------- xxl-job job execute end(finish) -----------
----------- ReturnT:"
+ executeResult);
} else {
if (idleTimes > 30) {
// 每3秒获取一次数据,获取30次都没有获取到数据之后,则现场被清除
XxlJobExecutor.removeJobThread(jobId, "excutor idel times over limit.");
}
}
} catch (Throwable e) {
if (toStop) {
XxlJobLogger.log("
----------- JobThread toStop, stopReason:" + stopReason);
}
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
String errorMsg = stringWriter.toString();
executeResult = new ReturnT(ReturnT.FAIL_CODE, errorMsg);
XxlJobLogger.log("
----------- JobThread Exception:" + errorMsg
+ "
----------- xxl-job job execute end(error) -----------");
} finally {
if (triggerParam != null) {
// callback handler info
if (!toStop) {
// handler执行完成之后,将结果写入到日志里面去,
// 就是在执行器启动的时候,会建立一个线程,用来实时处理日志,此处是将结果和logID放入到队列里面去,
TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(),
triggerParam.getLogDateTim(), executeResult));
} else {
// is killed
ReturnT stopResult = new ReturnT(ReturnT.FAIL_CODE,
stopReason + " [job running,killed]");
// 任务执行后,放入回调队列
TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(),
triggerParam.getLogDateTim(), stopResult));
}
}
}
}
// callback trigger request in queue
// 当现场被终止之后,队列里面剩余的未执行的任务,将被终止的这些任务放入队列,供日志监控线程来处理,回调给调度中心
while (triggerQueue != null && triggerQueue.size() > 0) {
TriggerParam triggerParam = triggerQueue.poll();
if (triggerParam != null) {
// is killed
ReturnT stopResult = new ReturnT(ReturnT.FAIL_CODE,
stopReason + " [job not executed, in the job queue, killed.]");
// 任务执行后,放入回调队列
TriggerCallbackThread.pushCallBack(
new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTim(), stopResult));
}
}
// destroy
try {
handler.destroy();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
logger.info(">>>>>>>>>>> xxl-job JobThread stoped, hashCode:{}", Thread.currentThread());
}
最后来看一下,TriggerCallbackThread.pushCallBack ()这个方法,将本次任务记录的日志ID和处理结果放入队列中去了
public void start() {
// valid
if (XxlJobExecutor.getAdminBizList() == null) {
logger.warn(">>>>>>>>>>> xxl-job, executor callback config fail, adminAddresses is null.");
return;
}
// callback
triggerCallbackThread = new Thread(new Runnable() {
@Override
public void run() {
// 正常的回调
while (!toStop) {
try {
HandleCallbackParam callback = getInstance().callBackQueue.take();
if (callback != null) {
// callback list param
List callbackParamList = new ArrayList();
// int drainToNum =
// 移除此队列中所有可用的元素,并将它们添加到给定 callbackParamList 中
getInstance().callBackQueue.drainTo(callbackParamList);
callbackParamList.add(callback);
// callback, will retry if error
if (callbackParamList != null && callbackParamList.size() > 0) {
//进行回调操作
doCallback(callbackParamList);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
// last callback===停止后最后的回调
try {
List callbackParamList = new ArrayList();
// int drainToNum =
// 移除此队列中所有可用的元素,并将它们添加到给定 callbackParamList 中
getInstance().callBackQueue.drainTo(callbackParamList);
if (callbackParamList != null && callbackParamList.size() > 0) {
doCallback(callbackParamList);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
logger.info(">>>>>>>>>>> xxl-job, executor callback thread destory.");
}
});
triggerCallbackThread.setDaemon(true);
triggerCallbackThread.start();
// 失败回调文件重试回调进程
triggerRetryCallbackThread = new Thread(new Runnable() {
@Override
public void run() {
while (!toStop) {
try {
// 读取失败回调文件,重试进行回调操作
retryFailCallbackFile();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
try {
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
} catch (InterruptedException e) {
logger.warn(">>>>>>>>>>> xxl-job, executor retry callback thread interrupted, error msg:{}",
e.getMessage());
}
}
logger.info(">>>>>>>>>>> xxl-job, executor retry callback thread destory.");
}
});
triggerRetryCallbackThread.setDaemon(true);
triggerRetryCallbackThread.start();
}
doCallback:
/**
* do callback, will retry if error 回调操作
*
* @param callbackParamList
*/
private void doCallback(List callbackParamList) {
boolean callbackRet = false;
// callback, will retry if error
// 获取调度中心的adminBiz列表,在执行器启动的时候,初始化的,
for (AdminBiz adminBiz : XxlJobExecutor.getAdminBizList()) {
try {
// 这里的adminBiz 调用的callback方法,因为是通过NetComClientProxy 这个factoryBean创建的代理对象,
// 在getObject方法中,最终是没有调用的目标类方法的invoke的。 只是将目标类的方法名,参数,类名,等信息发送给调度中心了
// 发送的地址调度中心的接口地址是 :“调度中心IP/api” 这个接口 。 这个是在执行器启动的时候初始化设置好的。
// 调度中心的API接口拿到请求之后,通过参数里面的类名,方法,参数,反射出来一个对象,然后invoke, 最终将结果写入数据库
ReturnT callbackResult = adminBiz.callback(callbackParamList);
if (callbackResult != null && ReturnT.SUCCESS_CODE == callbackResult.getCode()) {
callbackLog(callbackParamList, "
----------- xxl-job job callback finish.");
// 因为调度中心是集群式的,所以只要有一台机器返回success,那么就算成功,直接break
callbackRet = true;
break;
} else {
// callback log 回调日志
callbackLog(callbackParamList,
"
----------- xxl-job job callback fail, callbackResult:" + callbackResult);
}
} catch (Exception e) {
// callback log 回调日志
callbackLog(callbackParamList,
"
----------- xxl-job job callback error, errorMsg:" + e.getMessage());
}
}
if (!callbackRet) {
// 增加失败回调文件信息
appendFailCallbackFile(callbackParamList);
}
}
retryFailCallbackFile:失败回调文件,进行重试回调操作
/**
* 读取失败回调文件,重试进行回调操作
*/
private void retryFailCallbackFile() {
// load and clear file
List fileLines = FileUtil.loadFileLines(failCallbackFileName);
FileUtil.deleteFile(failCallbackFileName);
// parse
List failCallbackParamList = new ArrayList<>();
if (fileLines != null && fileLines.size() > 0) {
for (String line : fileLines) {
List failCallbackParamListTmp = JacksonUtil.readValue(line, List.class,
HandleCallbackParam.class);
if (failCallbackParamListTmp != null && failCallbackParamListTmp.size() > 0) {
failCallbackParamList.addAll(failCallbackParamListTmp);
}
}
}
// retry callback, 100 lines per page
if (failCallbackParamList != null && failCallbackParamList.size() > 0) {
int pagesize = 100;
List pageData = new ArrayList<>();
for (int i = 0; i < failCallbackParamList.size(); i++) {
pageData.add(failCallbackParamList.get(i));
if (i > 0 && i % pagesize == 0) {
doCallback(pageData);
pageData.clear();
}
}
if (pageData.size() > 0) {
doCallback(pageData);
}
}
}