Quartz blocked 线上故障解决

故障描述:

线上的job正常跑一段时间后就进入了blocked状态,更新quartz版本仍无法解决

 

故障分析:

先说正常获取、触发任务执行的流程:

调度器线程执行的时候,首先从 triggers 表获取状态为 WAITING 的将要发射的触发器,然后条件更新状态 TRIGGER_STATE 的值为 ACQUIRED ,更新成功则表示抢占到了,否则可能是被其他调度器抢占。然后插入触发器信息及实例名到 FIRED_TRIGGERS 表。前面的更新与插入操作是在同一个事务里完成。

抢占到触发器后,调度器线程等待触发时间到来。

执行时间到来后,调度器线程首先会把 FIRED_TRIGGERS 表里触发器记录的状态更新为 EXECUTING ,如果任务允许并发执行,把 TRIGGERS 表里的状态更新为 WAITING , PAUSED 或 COMPLETE (不需要再执行的) ;如果任务不允许并发执行,还会把 TRIGGERS 表里的状态更新为 BLOCKED 或 PAUSED_BLOCKED,这个更新是根据 任务名和任务所属组名 而不是触发器名称和触发器所属组名 来更新的,这就解决了一个任务有多个触发器的并发问题;然后调度器线程会创建一个执行环境来执行任务,以便在任务执行完成后更新触发器的状态。任务执行完成后,在一个事务里把 triggers 里的触发器状态更新为 WAITING,删除 FIRED_TRIGGERS 表里对应的记录。

即状态变化为:waiting->blocked->executing->waiting

分析线上job得出blocked原因:

1、job执行线程执行周期过长,下一个执行任务周期到了,导致blocked。一般是业务过多或者http请求未设置超时时长

2、异常处理,导致一直失败重试,可能导致blocked代码如下

@Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("execute task:{}....", jobExecutionContext.getTrigger().getKey());

        try {
            runJob(jobExecutionContext);
        } catch (Exception e) {
            JobExecutionException e2 = new JobExecutionException(e);
            e2.setRefireImmediately(true);
           
            throw e2;
            
        }
    }

如果runJob方法一直异常,这会一直throw exception,导致blocked。

 

解决方法:

一、控制job执行不会重叠,保证每个执行周期不会重叠。

1)、如果处理业务很多,可以用多线程的方式,并行处理,缩短执行时长。

2)、配置cron 表达式要注意避免执行周期交叉。

3)、如果使用http或者其他远程调用的方法,一定要设置超时时长

 

二、异常一定要控制重试次数,代码如下

 @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("execute task:{}....", jobExecutionContext.getTrigger().getKey());

        try {
            runJob(jobExecutionContext);
        } catch (Exception e) {
            JobExecutionException e2 = new JobExecutionException(e);
            e2.setRefireImmediately(true);
            log.error("execute task:{} exception, setRefireImmediately true", jobExecutionContext.getTrigger().getKey());
            Integer result = (Integer) jobExecutionContext.get("result");
            if (result == null || result < cronConfig.getRetryCount()) {
                jobExecutionContext.put("result", result == null ? 2 : result + 1);
                throw e2;
            }
        }
    }
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值