docker和K8S环境xxl-job定时任务不执行问题总结_dcoker部署xxljob,无法调用其他服务的定时任务(3)

最后

小编的一位同事在校期间连续三年参加ACM-ICPC竞赛。从参赛开始,原计划每天刷一道算法题,实际上每天有时候不止一题,一年最终完成了 600+:

凭借三年刷题经验,他在校招中很快拿到了各大公司的offer。

入职前,他把他的刷题经验总结成1121页PDF书籍,作为礼物赠送给他的学弟学妹,希望同学们都能在最短时间内掌握校招常见的算法及解题思路。

整本书,我仔细看了一遍,作者非常细心地将常见核心算法题和汇总题拆分为4个章节。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

而对于有时间的同学,作者还给出了他结合众多数据结构算法书籍,挑选出的一千多道题的解题思路和方法,以供有需要的同学慢慢研究。

import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.job.core.context.XxlJobContext;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.log.XxlJobLogger;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JobThread extends Thread {
private static Logger logger = LoggerFactory.getLogger(JobThread.class);
private int jobId;
private IJobHandler handler;
private LinkedBlockingQueue triggerQueue;
private Set triggerLogIdSet;
private volatile boolean toStop = false;
private String stopReason;
private boolean running = false;
private int idleTimes = 0;

public JobThread(int jobId, IJobHandler handler) {
    this.jobId = jobId;
    this.handler = handler;
    this.triggerQueue = new LinkedBlockingQueue();
    this.triggerLogIdSet = Collections.synchronizedSet(new HashSet());
}

public IJobHandler getHandler() {
    return this.handler;
}

public ReturnT<String> pushTriggerQueue(TriggerParam triggerParam) {
    if (this.triggerLogIdSet.contains(triggerParam.getLogId())) {
        logger.info(">>>>>>>>>>> repeate trigger job, logId:{}", triggerParam.getLogId());
        return new ReturnT(500, "repeate trigger job, logId:" + triggerParam.getLogId());
    } else {
        this.triggerLogIdSet.add(triggerParam.getLogId());
        this.triggerQueue.add(triggerParam);
        return ReturnT.SUCCESS;
    }
}

public void toStop(String stopReason) {
    this.toStop = true;
    this.stopReason = stopReason;
}

public boolean isRunningOrHasQueue() {
    return this.running || this.triggerQueue.size() > 0;
}

public void run() {
// 尝试初始化处理器
try {
this.handler.init();
} catch (Throwable var26) {
// 如果有错误,记录错误日志
logger.error(var26.getMessage(), var26);
}

final TriggerParam triggerParam;
ReturnT executeResult;

// 当线程没有被要求停止时
while(!this.toStop) {
    this.running = false;
    ++this.idleTimes;
    triggerParam = null;
    executeResult = null;
    boolean var16 = false;

    ReturnT stopResult;
    label348: {
        try {
            var16 = true;
            // 从触发队列中获取触发器参数,如果在3秒内获取不到,返回null
            triggerParam = (TriggerParam)this.triggerQueue.poll(3L, TimeUnit.SECONDS);
            // 如果触发器参数不为空
            if (triggerParam != null) {
                this.running = true;
                this.idleTimes = 0;
                // 从日志ID集合中移除触发器参数的日志ID
                this.triggerLogIdSet.remove(triggerParam.getLogId());
                // 创建日志文件名称
                String logFileName = XxlJobFileAppender.makeLogFileName(new Date(triggerParam.getLogDateTime()), triggerParam.getLogId());
                // 设置XxlJob上下文
                XxlJobContext.setXxlJobContext(new XxlJobContext(triggerParam.getLogId(), logFileName, triggerParam.getBroadcastIndex(), triggerParam.getBroadcastTotal()));
                // 记录任务启动日志
                XxlJobLogger.log("<br>----------- xxl-job job execute start -----------<br>----------- Param:" + triggerParam.getExecutorParams(), new Object[0]);
                // 如果执行超时时间大于0
                if (triggerParam.getExecutorTimeout() > 0) {
                    Thread futureThread = null;

                    try {
                        // 创建一个新的任务
                        FutureTask<ReturnT<String>> futureTask = new FutureTask(new Callable<ReturnT<String>>() {
                            public ReturnT<String> call() throws Exception {
                                // 执行处理器
                                return JobThread.this.handler.execute(triggerParam.getExecutorParams());
                            }
                        });
                        // 在新线程中执行任务
                        futureThread = new Thread(futureTask);
                        futureThread.start();
                        // 获取执行结果,如果在指定的超时时间内没有结果,抛出超时异常
                        executeResult = (ReturnT)futureTask.get((long)triggerParam.getExecutorTimeout(), TimeUnit.SECONDS);
                    } catch (TimeoutException var24) {
                        // 记录任务超时日志
                        XxlJobLogger.log("<br>----------- xxl-job job execute timeout", new Object[0]);
                        XxlJobLogger.log(var24);
                        // 设置超时的执行结果
                        executeResult = new ReturnT(IJobHandler.FAIL\_TIMEOUT.getCode(), "job execute timeout ");
                    } finally {
                        // 中断线程
                        futureThread.interrupt();
                    }
                } else {
                    // 如果没有设置超时时间,则直接执行处理器
                    executeResult = this.handler.execute(triggerParam.getExecutorParams());
                }

                // 如果执行结果为空
                if (executeResult == null) {
                    // 设置执行失败的结果
                    executeResult = IJobHandler.FAIL;
                } else {
                    // 否则,设置执行结果的消息和内容
                    executeResult.setMsg(executeResult != null && executeResult.getMsg() != null && executeResult.getMsg().length() > 50000 ? executeResult.getMsg().substring(0, 50000).concat("...") : executeResult.getMsg());
                    executeResult.setContent((Object)null);
                }

                // 记录任务结束日志
                XxlJobLogger.log("<br>----------- xxl-job job execute end(finish) -----------<br>----------- ReturnT:" + executeResult, new Object[0]);
                var16 = false;
            } else if (this.idleTimes > 30) {
                // 如果连续30次都没有获取到触发器参数
                if (this.triggerQueue.size() == 0) {
                    // 如果触发队列为空,则移除Job线程
                    XxlJobExecutor.removeJobThread(this.jobId, "executor idle times over limit.");
                    var16 = false;
                } else {
                    var16 = false;
                }
            } else {
                var16 = false;
            }
            break label348;
        } catch (Throwable var27) {
            // 如果在执行过程中抛出异常
            if (this.toStop) {
                // 如果被要求停止,记录停止原因
                XxlJobLogger.log("<br>----------- JobThread toStop, stopReason:" + this.stopReason, new Object[0]);
            }

            // 获取异常信息
            StringWriter stringWriter = new StringWriter();
            var27.printStackTrace(new PrintWriter(stringWriter));
            String errorMsg = stringWriter.toString();
            // 设置执行结果为异常信息
            executeResult = new ReturnT(500, errorMsg);
            // 记录任务异常日志
            XxlJobLogger.log("<br>----------- JobThread Exception:" + errorMsg + "<br>----------- xxl-job job execute end(error) -----------", new Object[0]);
            var16 = false;
        } finally {
            if (var16) {
                // 如果触发器参数不为空
                if (triggerParam != null) {
                    if (!this.toStop) {
                        // 如果没有被要求停止,向回调队列中推送回调参数
                        TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTime(), executeResult));
                    } else {
                        // 如果被要求停止,设置停止结果,并向回调队列中推送回调参数
                        ReturnT<String> stopResult = new ReturnT(500, this.stopReason + " [job running, killed]");
                        TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTime(), stopResult));
                    }
                }

            }
        }

        // 如果触发器参数不为空
        if (triggerParam != null) {
            if (!this.toStop) {
                // 如果没有被要求停止,向回调队列中推送回调参数
                TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTime(), executeResult));
            } else {
                // 如果被要求停止,设置停止结果,并向回调队列中推送回调参数
                stopResult = new ReturnT(500, this.stopReason + " [job running, killed]");
                TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTime(), stopResult));
            }
        }
        continue;
    }

    // 如果触发队列不为空,且队列中还有元素
    while(this.triggerQueue != null && this.triggerQueue.size() > 0) {
        // 从队列中获取触发器参数
        triggerParam = (TriggerParam)this.triggerQueue.poll();
        // 如果触发器参数不为空
        if (triggerParam != null) {
            // 设置未执行的结果,并向回调队列中推送回调参数
            executeResult = new ReturnT(500, this.stopReason + " [job not executed, in the job queue, killed.]");
            TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTime(), executeResult));
        }
    }

    // 尝试销毁处理器
    try {
        this.handler.destroy();
    } catch (Throwable var23) {
        // 如果有错误,记录错误日志
        logger.error(var23.getMessage(), var23);
    }

    // 记录线程停止日志
    logger.info(">>>>>>>>>>> xxl-job JobThread stoped, hashCode:{}", Thread.currentThread());
}  

}


单从源码我们分析 `JobThread` 类源码的关键操作


1. `pushTriggerQueue` 方法:该方法用于将任务触发器参数(`TriggerParam`)添加到任务队列(`triggerQueue`)中。如果队列中已经存在相同的日志ID,那么这个新的任务触发器就不会被添加。否则,将会添加到该队列中。
2. `run` 方法



> 
> 1. 开始一个持续运行的循环
> 2. 在循环中,尝试从触发队列中获取一个触发参数 (triggerParam),如果在3秒内没有获取到,返回null。
> 3. 如果获取到触发参数:
> 
> 
> 	* 将此线程标记为运行状态。
> 	* 从日志ID集合中移除该触发参数的日志ID。
> 	* 设置XxlJob的上下文。
> 	* 记录任务开始执行的日志。
> 	* 如果设置了执行超时时间,将在新线程中执行处理器,如果超过指定的超时时间仍未获取到结果,将记录任务超时日志,并设置超时的执行结果。
> 	* 如果没有设置超时时间,直接执行处理器。
> 	* 根据执行结果是否为空,设置相应的执行结果信息。
> 	* 记录任务结束日志。
> 4. 如果连续30次都没有获取到触发参数,且触发队列为空,将移除该Job线程。
> 
> 
> 


总结一下就是任务调度和任务执行依靠队列 triggerQueue,这个是参数队列,调度直接往这个队列里面put参数对象。执行依靠是JobThread run方法,从队列中获取参数对象,能获取就执行,任务和线程是一一绑定的关系,调度成功,但没有结果回调。  
 所以核心的排查问题方法是我们要知道这个队列中还有多少个任务对象虽然已经被调度到任务队列但是没有被执行。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/7fab692edab341fab60525333dd13228.png)  
 里面放到的对象为这个  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/14c5d2b53df545269b2aac71d3d4bf68.png)  
 所以我们只需要看下这个容器



jmap -histo 进程ID | grep “com.xxl.job.core.biz.model.TriggerParam”


获取到有多少任务在等待。我们就能判断是不是卡在执行任务当中handler.execute();


这就是这个问题的排查思路。希望能帮助到大家。


## 4 问题4 数据库性能问题,导致查询任务和操作日志数据卡主


表:XXL\_JOB\_QRTZ\_TRIGGER\_LOG 约有 16.5 GB


执行


DELETE FROM XXL\_JOB\_QRTZ\_TRIGGER\_LOG WHERE trigger\_time >= ‘2021-12-17 00:18:59’ AND trigger\_time <= ‘2021-12-18 23:59:20’;  
 操作可能导致数据库 死锁或者CPU夯住了,导致 0 时执行的任务,没有执行成功。


它的主要功能是进行任务管理和调度。它在数据库中维护了一系列的日志和状态信息,以确保任务能够正确地调度和执行。在某些情况下,为了维护系统的性能和稳定性,可能会需要清理一些旧的,不再需要的数据。



DELETE FROM XXL_JOB_QRTZ_TRIGGER_LOG WHERE trigger_time >= ‘2021-12-17 00:18:59’ AND trigger_time <= ‘2021-12-18 23:59:20’;


这个操作是在XXL-JOB的任务调度日志表(XXL\_JOB\_QRTZ\_TRIGGER\_LOG)中删除特定时间范围内的日志记录。 为了进行日志清理,以释放存储空间和保持数据库性能。大量的删除操作可能会对数据库性能产生影响,尤其是在高并发或者大数据量的情况下。因此,这种操作通常需要在数据库的低峰期进行,并可能需要使用分批删除等技术以减少对数据库性能的影响。


##### 解决方案


建议任务调度服务中的 XXL\_JOB\_QRTZ\_TRIGGER\_LOG 这张表保留最近一周的日志量,在业务低峰期定时执行脚本。


## 5. 问题5 定时任务方法中有事务,




### 最后

**小编的一位同事在校期间连续三年参加ACM-ICPC竞赛。从参赛开始,原计划每天刷一道算法题,实际上每天有时候不止一题,一年最终完成了 600+:**

**凭借三年刷题经验,他在校招中很快拿到了各大公司的offer。**



**入职前,他把他的刷题经验总结成1121页PDF书籍,作为礼物赠送给他的学弟学妹,希望同学们都能在最短时间内掌握校招常见的算法及解题思路。**

![](https://img-blog.csdnimg.cn/img_convert/01358b165616eb9429e16241cf00d213.png)

**整本书,我仔细看了一遍,作者非常细心地将常见核心算法题和汇总题拆分为4个章节。**

![](https://img-blog.csdnimg.cn/img_convert/3425ac600577268ce7d1f69b2002194d.png)

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

**而对于有时间的同学,作者还给出了他结合众多数据结构算法书籍,挑选出的一千多道题的解题思路和方法,以供有需要的同学慢慢研究。**

![](https://img-blog.csdnimg.cn/img_convert/c941214256693bd4eb05782f3f676721.png)



  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值