前言

上文 snail-job 初体验 中,已经运行了一个定时任务,初步了解 snail-job 的基本用法。本文主要是针对集群定时任务,做进一步的了解。这里就包括任务管理页面中的每项配置作用,并对重要的配置项做代码测试。

本文目标

  • 采用 JDK17 以上实现客户端
  • 注解方式实现定时任务客户端
  • 记录日志到任务服务端
  • 任务管理页面中定时任务配置项详细解释
  • 对阻塞策略、超时时间、最大重试次数配置项进行测试理解

Maven依赖

JDK17 及其以上的 Maven 依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>3.3.3</version>
    </dependency>
    <!-- snail-job 客户端依赖 -->
    <dependency>
        <groupId>com.aizuda</groupId>
        <artifactId>snail-job-client-starter</artifactId>
        <version>1.1.2</version>
    </dependency>
    <!-- snail-job 重试相关依赖 -->
    <dependency>
        <groupId>com.aizuda</groupId>
        <artifactId>snail-job-client-retry-core</artifactId>
        <version>1.1.2</version>
    </dependency>
    <!-- snail-job 客户端核心依赖 -->
    <dependency>
        <groupId>com.aizuda</groupId>
        <artifactId>snail-job-client-job-core</artifactId>
        <version>1.1.2</version>
    </dependency>
</dependencies>

大概说明下 snail-job 依赖库的作用:

库名主要作用
snail-job-client-startersnail-job 客户端启动时,和 SpringBoot 框架相结合的初始框架代码
snail-job-client-retry-core实现重试任务的核心代码。
snail-job-client-job-core实现定时任务的核心代码

说明:

如果你的项目中不涉及重试任务,实际上不引入 snail-job-client-retry-core 的依赖也是可以的,不会影响到定时任务;反之亦然。

logback 配置文件

在本文目标中,希望把日志写到服务端上。所以这里需要配置下 logback-spring.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="log.path" value="./logs"/>
    <property name="console.log.pattern"
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>

    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${console.log.pattern}</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>

    <!-- 省略info,warn,error级别的配置 -->
    
     <!-- Snail appender -->
    <appender name="snailLogAppender" class="com.aizuda.snailjob.client.common.appender.SnailLogbackAppender">
    </appender>

    <!--系统操作日志-->
    <root level="info">
        <appender-ref ref="console" />
        <appender-ref ref="file_info" />
        <appender-ref ref="file_warn" />
        <appender-ref ref="file_error" />
        <appender-ref ref="snailLogAppender" />
    </root>

</configuration>

测试类

​ 由于在上文中已经介绍过配置文件和启动类的注意事项了。这里补充说明一点,如果你的服务端 IP 和客户端 IP 不是同一台机器的话,建议在配置文件中指定一下客户端 IP【snail-job.host=192.168.1.123】,否则取到的默认 IP 可能不对,从而造成服务端访问不到客户端的情况。测试类具体注解测试类代码如下:

@Slf4j
@Component
@JobExecutor(name = "testSnailJobWithAnnotation")
public class TestSnailJobWithAnnotation {
    public ExecuteResult jobExecute(JobArgs jobArgs) {
        log.info("定时任务执行成功.参数值:{}", jobArgs);
        SnailJobLog.LOCAL.info("LOCAL日志.参数值:{}", jobArgs);
        SnailJobLog.REMOTE.info("REMOTE日志.参数值:{}", jobArgs);
        return ExecuteResult.success("测试注解方式的定时任务成功");
    }
}

说明:

  1. 默认执行的方法名是 jobExecute,如果需要修改的话,则可以修改 @JobExecutormethod 属性。如:

@JobExecutor(name = "testSnailJobWithAnnotation", method="myJob")

  1. 执行器的名称在同一组下是不允许重复的。如果你另外一个定时任务类也叫 testSnailJobWithAnnotation 那么在客户端启动的时候就会报错:SnailJobClientException: 不允许 executorName 重复的
  2. 本测试代码中 SnailJobLog.LOCAL.info 和采用 slf4jlog 记录日志效果一样,都是在客户端记录日志;而 SnailJobLog.REMOTE.info 则是在服务端记录日志。

定时任务管理页面

对定时任务的任务管理和执行批次页面,进行了解。

image-20240919142353336

任务管理-新增/编辑定时任务

下面是该在服务器的任务管理页面中,添加我们上面的测试类了。在这里顺便解释下定时任务的配置项。

image-20240918152748854

任务名称

定时任务的一个名称,最好是看到这个名字,就知道这个定时是干嘛用的。

组名称

这个在上文中已经有比较详细的描述。这里可以简单把组理解为项目或者应用。

状态

分为启用和禁用两种状态。禁用状态时,服务端不再调度该任务。并且只有禁用状态的任务才能被删除。

任务类型

image-20240918153653110

本文主要是讲述集群任务类型的,这里先不展开其他的任务类型。目前仅需知道集群任务类型,能保证服务端可以按照下面将要说到的路由策略,只命中一台客户端节点执行任务。至于其他的任务类型,将在后续文章中单独介绍。

执行器类型

image-20240918154230292

snail-job 在支持 Java 客户端的同时,还支持 Python 客户端。未来还可能支持 Go 语言的客户端。

执行器名称

image-20240918162822145

最常用的是自定义执行器,由于我们客户端采用注解的方式,所以这里的名字和用注解中的 name 值一致即可。不用像上一篇那样要写全路径类名了。方法参数中填写的内容,可以通过方法参数的 JobArgs 对象获得。除了这个最最常用的自定义执行器,snail-job 还内置了几个执行器名称:

image-20240918163429795

执行器名称作用
Http 执行器通过 HTTP 请求调用一个接口的方式,定时执行任务。
CMD 执行器定时执行 Windows 上的 .bat 文件
PowerShell 执行器定时执行 PowerShell 脚本文件,通常是 .ps1 文件
Shell 执行器定时执行 Linux 上的 .sh 文件

路由策略

就是服务端通过什么策略来路由到客户端。snail-job 有如下路由策略:

image-20240918164720432

路由策略解释说明
最后一个客户端a、b、c三台集群的情况下,总是命中c这台机器
第一个客户端a、b、c三台集群的情况下,总是命中a这台机器
轮询客户端a、b、c三台集群的情况下,总是安装a、b、c这种每台命中一次的节奏周而复始
LRU客户端a、b、c三台集群的情况下,会命中最近最少使用的那台客户端。
随机客户端a、b、c三台集群的情况下,随机命中一台
一致性哈希客户端a、b、c三台集群的情况下,一致性哈希算法会稳定的调度其中一台机器。

阻塞策略

​ 阻塞策略这个是处理服务端到了该触发客户端任务 A 的时候,但是发现任务 A 还在运行。此时就需要一个策略来处理该情况。

image-20240920140948954

简单的说,就是服务端应该触发某个任务,发现该任务没结束呢,此时该咋办呢?那么由你来决定。你可以选择的处理方式有如下几种方式:

策略解释
丢弃你都没干完,我不干
覆盖你别干了,我来干
并行直接开干
恢复继续把失败的任务干完
这几个策略在本文后面的测试中,进行逐一测试验证

触发类型

服务端以哪种类型来触发客户端,包括如下可选类型:

image-20240919104835679

类型名说明
固定时间以一个固定时间间隔来触发客户端执行任务。
CRON 表达式以 Cron 表达式设置的时间来触发客户端执行任务
工作流工作流这个会在后续文章单独介绍

时间间隔

会根据前面选择的触发类型不同而有所变化。

类型时间间隔
固定时间这里是间隔的秒数。如:600 秒,10 分钟触发一次
CRON 表达式这里是一个 Cron 表达式的触发。如:0 35 13 * * ?

Cron 表达式管理页面有很好的支持,可以通过页面进行配置。尤其是其中还有验证表达式是否正确的最近 5 次运行时间

image-20240919110207514

超时时间(秒)

这里的超时时间,就是你对这个定时任务预估完成的时间。如果设置的过短,可能导致其实客户端任务已经返回运行成功了,但是在任务批次列表【后面讲述】中显示任务执行超时

最大重试次数

首先说明,这里有几种情况会触发服务端重新调度客户端任务。

  • 网络超时
  • 客户端明确返回错误
  • 客户端出现异常

这里设置的次数,就是发生上面情况时,重试的最大次数。

特别说明:

任务超时是不会触发服务端重新调度客户端任务的。

重试间隔(秒)

在需要重新调度客户端任务时,两次重试的时间间隔。

描述

对该任务的详细描述。

任务管理-任务列表

image-20240920095546185

说明两个名词定义:

  • 任务项

    这个列表中的每一条记录叫任务项

  • 批次

    每个批次就是一次服务端对客户端的调度。而这个调度可能是一个或者多个任务。当然,针对本文讲述的集群定时任务来说,一个批次调度的是一个任务。而任务类型为:广播、分片、Map、MapReduce 的任务就会牵扯到多个任务。这个后续文章讲述。

任务管理-执行批次

执行批次页面可以通过两种方式进入:

  • 在任务管理-任务列表中,点击任务项后面操作中的批次按钮
  • 直接在菜单项中点击执行批次菜单项

两者的区别是,任务列表中点击某个任务项后面的批次按钮,则直接看到的该任务下的批次列表;而直接通过菜单项看到的是所有任务的批次列表。

进行测试

这里测试的目的包括:

  • 为了实现本文目标,验证日志记录到服务器上。
  • 为了深入理解阻塞策略、超时时间、最大重试次数配置项对定时任务的影响

验证注解方式定时任务

把我们最开始写的测试类,进行验证。

客户端代码

@Slf4j
@Component
@JobExecutor(name = "testSnailJobWithAnnotation")
public class TestSnailJobWithAnnotation {
    public ExecuteResult jobExecute(JobArgs jobArgs) {
        log.info("定时任务执行成功.参数值:{}", jobArgs);
        SnailJobLog.LOCAL.info("LOCAL日志.参数值:{}", jobArgs);
        SnailJobLog.REMOTE.info("REMOTE日志.参数值:{}", jobArgs);
        return ExecuteResult.success("测试注解方式的定时任务成功");
    }
}

页面配置

配置项配置内容
任务名称注解定时任务
组名称service_plat
状态启用
任务类型集群
执行器类型java
执行器名称testSnailJobWithAnnotation
方法参数{"date":"2024-09-20"}
路由策略轮询
阻塞策略丢弃
触发类型固定时间
间隔时长60秒
超时时间(秒)60秒
最大重试次数3
重试间隔1秒

测试结果

  • 执行批次页面中任务处理成功

    image-20240920151024259

  • 日志中能看到记录到服务端的日志

    点击上图中某个批次后面的日志,可以看到日志的详情信息

    image-20240920151534130

    点击查看日志

    image-20240920151753445

测试总结

  • 注解方式的任务通过 @JobExecutor 来实现
  • SnailJobLog.LOCAL.infolog.info 作用基本等价,都是在客户端写日志信息
  • SnailJobLog.REMOTE.info 可以把日志写到服务端

验证最大重试次数和间隔

在前面介绍管理页面的时候,已经介绍过什么情况下会重试,在这里再次回顾下:

  • 网络超时
  • 客户端明确返回错误
  • 客户端出现异常

那我们客户端代码随便来个被 0 除的异常即可。余下的两种情况可自行进行测试。

客户端代码

@Component
public class TestRetryTimes extends AbstractJobExecutor {
    @Override
    protected ExecuteResult doJobExecute(JobArgs jobArgs) {
        int i = 1/0;
        return ExecuteResult.success();
    }
}

这里还是用上一篇文章中推荐的继承虚基类方式来做定时任务。好处有:

  • IDEA 能自动补充需要实现的接口类。省得记入参和返回参数。
  • 避免任务重名

页面关键配置

其他配置项和上面的验证注解方式定时任务配置一样

配置项配置内容
任务名称测试最大重试次数和间隔
执行器名称com.bjltd.test.TestRetryTimes
触发类型CRON表达式
间隔时长0 22 09 ?
最大重试次数4
重试间隔10秒

说明

  • 执行器名称要填写包全路径名。这个是和注解方式有很大区别。
  • 这里采用 Cron 表达式,任务每天就激活一次。主要为了方便查看测试结果,也更贴合实际应用场景。

测试结果

  • 客户端的控台

    这里抽取关键打印信息:

    2024-09-23 09:22:10 [snail-netty-server-2] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 任务执行/调度失败执行重试. 重试次数:[1]
    2024-09-23 09:22:20 [snail-netty-server-3] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 任务执行/调度失败执行重试. 重试次数:[2]
    2024-09-23 09:22:31 [snail-netty-server-4] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 任务执行/调度失败执行重试. 重试次数:[3]
    2024-09-23 09:22:41 [snail-netty-server-5] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 任务执行/调度失败执行重试. 重试次数:[4]   
  • 任务管理页面查看日志

    任务管理页面->点任务名称为测试最大重试次数和间隔任务项后面的批次按钮。在执行批次列表中点批次后面的日志按钮。

    image-20240923132709284

    在执行批次详情页面中点击查看日志按钮

    image-20240923132853922

    在日志详情中可以看到本批次的详细情况

    image-20240923133124726

测试总结

  • 网络超时、客户端明确返回错误、客户端出现异常会触发重试
  • 最大重试次数可以设置重试多少次
  • 重试间隔可以设置每次重试之间间隔的时间

验证阻塞策略-丢弃

策略解释
丢弃你都没干完,我不干

触发阻塞的前提是:服务端应该触发某个任务,发现该任务没结束。结合前提,这里大体测试思路是:每分钟执行一次定时任务,而任务执行时间是 5 分钟。这样就会触发阻塞策略了。

客户端代码

@Component
public class TestDiscardJob extends AbstractJobExecutor {
    @Override
    protected ExecuteResult doJobExecute(JobArgs jobArgs) {
        // 休眠 5 分钟
        ThreadUtil.sleep(5, TimeUnit.MINUTES);
        return ExecuteResult.success();
    }
}

页面配置

配置项配置内容
任务名称测试阻塞策略-丢弃
执行器名称com.bjltd.test.TestDiscardJob
阻塞策略丢弃
触发类型固定时间
间隔时长60秒
超时时间600秒

测试结果

image-20240923164010376

1527 这个批次,状态会从运行中变为处理成功。执行时长也从空更新为最终的执行时长了。

测试总结

  • 丢弃策略会一直等待前一个任务做完后,才会创建新的批次去调用客户端运行任务
  • 在某种程度上解决了幂等性,但不是绝对的

验证阻塞策略-覆盖

策略解释
覆盖你别干了,我来干

新批次开始之前会尝试中断旧批次的线程,但是不一定能终止掉。这里可以理解成在新批次开始前,调用了一个旧批次任务线程池的 shutDownNow 方法

客户端代码

  • 无法中断的情况

    @Slf4j
    @Component
    public class TestOverlayCanNotInterrupted extends AbstractJobExecutor {
        @Override
        protected ExecuteResult doJobExecute(JobArgs jobArgs) {
            log.info("{}开始执行任务", Thread.currentThread().getName());
            for(int i = 1; i <= 5; i++) {
                log.info("{}执行第{}次", Thread.currentThread().getName(), i);
                ThreadUtil.sleep(60, TimeUnit.SECONDS);
            }
            log.info("{}任务执行完成", Thread.currentThread().getName());
            return ExecuteResult.success();
        }
    }
  • 可以中断的情况

    @Slf4j
    @Component
    public class TestOverlayCanInterrupted extends AbstractJobExecutor {
        @Override
        protected ExecuteResult doJobExecute(JobArgs jobArgs) {
            log.info("{}开始执行任务", Thread.currentThread().getName());
            int i = 1;
            while (!Thread.currentThread().isInterrupted()) {
                log.info("{}执行第{}次", Thread.currentThread().getName(), i);
                try {
                    Thread.sleep(60000);
                }catch (InterruptedException e) {
                    log.info("{}任务被中断", Thread.currentThread().getName());
                    break;
                }
                i++;
            }
            log.info("{}任务执行完成", Thread.currentThread().getName());
            return ExecuteResult.success();
        }
    }

页面配置

  • 无法中断的配置
配置项配置内容
任务名称测试阻塞策略-覆盖-无法中断
执行器名称com.bjltd.test.TestOverlayCanNotInterrupted
阻塞策略覆盖
触发类型固定时间
间隔时长60秒
超时时间600秒
  • 可以中断的配置
配置项配置内容
任务名称测试阻塞策略-覆盖-可中断
执行器名称com.bjltd.test.TestOverlayCanInterrupted
阻塞策略覆盖
触发类型固定时间
间隔时长60秒
超时时间600秒

测试结果

  • 无法中断的测试结果

    客户端日志信息:

    2024-09-24 11:19:51 [snail-netty-server-1] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 批次:[1716] 任务调度成功. 
    2024-09-24 11:19:51 [snail-job-job-1,716-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,716-1开始执行任务
    2024-09-24 11:19:51 [snail-job-job-1,716-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,716-1执行第1次
    2024-09-24 11:20:01 [nioEventLoopGroup-2-1] INFO  c.a.s.c.c.l.report.ReportLogListener
     - Data report log successfully requestId:[6]
    2024-09-24 11:20:43 [snail-job-job-1,716-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,716-1执行第2次
    2024-09-24 11:20:43 [snail-job-job-1,716-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,716-1执行第3次
    2024-09-24 11:20:50 [snail-netty-server-3] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 批次:[1717] 任务调度成功. 
    2024-09-24 11:20:50 [snail-job-job-1,717-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,717-1开始执行任务
    2024-09-24 11:20:50 [snail-job-job-1,717-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,717-1执行第1次
    2024-09-24 11:21:01 [nioEventLoopGroup-2-1] INFO  c.a.s.c.c.l.report.ReportLogListener
     - Data report log successfully requestId:[14]
    2024-09-24 11:21:43 [snail-job-job-1,717-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,717-1执行第2次
    2024-09-24 11:21:43 [snail-job-job-1,717-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,717-1执行第3次
    2024-09-24 11:21:43 [snail-job-job-1,716-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,716-1执行第4次
    2024-09-24 11:21:50 [snail-netty-server-5] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 批次:[1718] 任务调度成功. 
    2024-09-24 11:21:50 [snail-job-job-1,718-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,718-1开始执行任务
    2024-09-24 11:21:50 [snail-job-job-1,718-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,718-1执行第1次
    2024-09-24 11:22:01 [nioEventLoopGroup-2-1] INFO  c.a.s.c.c.l.report.ReportLogListener
     - Data report log successfully requestId:[22]
    2024-09-24 11:22:43 [snail-job-job-1,717-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,717-1执行第4次
    2024-09-24 11:22:43 [snail-job-job-1,716-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,716-1执行第5次
    2024-09-24 11:22:43 [snail-job-job-1,718-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,718-1执行第2次
    2024-09-24 11:22:43 [snail-job-job-1,718-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,718-1执行第3次
    2024-09-24 11:22:50 [snail-netty-server-7] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 批次:[1719] 任务调度成功. 
    2024-09-24 11:22:50 [snail-job-job-1,719-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,719-1开始执行任务
    2024-09-24 11:22:50 [snail-job-job-1,719-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,719-1执行第1次
    2024-09-24 11:23:01 [nioEventLoopGroup-2-1] INFO  c.a.s.c.c.l.report.ReportLogListener
     - Data report log successfully requestId:[30]
    2024-09-24 11:23:43 [snail-job-job-1,719-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,719-1执行第2次
    2024-09-24 11:23:43 [snail-job-job-1,719-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,719-1执行第3次
    2024-09-24 11:23:43 [snail-job-job-1,717-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,717-1执行第5次
    2024-09-24 11:23:43 [snail-job-job-1,716-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,716-1任务执行完成
    2024-09-24 11:23:43 [snail-job-job-1,718-1] INFO  c.b.t.TestOverlayCanNotInterrupted
     - snail-job-job-1,718-1执行第4次
    2024-09-24 11:23:50 [snail-netty-server-9] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 批次:[1720] 任务调度成功. 
    从日志打印可以看出,1716 这个批次并没有被真正中断,还是会在后台持续的工作。

    任务批次列表中的显示

    image-20240924113039147

  • 可以中断的测试结果

    客户端日志信息:

    2024-09-24 15:04:41 [snail-netty-server-1] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 批次:[1935] 任务调度成功. 
    2024-09-24 15:04:41 [snail-job-job-1,935-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,935-1开始执行任务
    2024-09-24 15:04:41 [snail-job-job-1,935-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,935-1执行第1次
    2024-09-24 15:04:51 [nioEventLoopGroup-2-1] INFO  c.a.s.c.c.l.report.ReportLogListener
     - Data report log successfully requestId:[4]
    2024-09-24 15:05:40 [snail-job-job-1,935-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,935-1任务被中断
    2024-09-24 15:05:40 [snail-job-job-1,935-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,935-1任务执行完成
    2024-09-24 15:05:41 [snail-netty-server-3] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 批次:[1936] 任务调度成功. 
    2024-09-24 15:05:41 [snail-job-job-1,936-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,936-1开始执行任务
    2024-09-24 15:05:41 [snail-job-job-1,936-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,936-1执行第1次
    2024-09-24 15:05:52 [nioEventLoopGroup-2-1] INFO  c.a.s.c.c.l.report.ReportLogListener
     - Data report log successfully requestId:[12]
    2024-09-24 15:06:40 [snail-job-job-1,936-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,936-1任务被中断
    2024-09-24 15:06:40 [snail-job-job-1,936-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,936-1任务执行完成
    2024-09-24 15:06:40 [snail-netty-server-5] INFO  c.a.s.c.job.core.client.JobEndPoint
     - 批次:[1937] 任务调度成功. 
    2024-09-24 15:06:40 [snail-job-job-1,937-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,937-1开始执行任务
    2024-09-24 15:06:40 [snail-job-job-1,937-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,937-1执行第1次
    2024-09-24 15:06:51 [nioEventLoopGroup-2-1] INFO  c.a.s.c.c.l.report.ReportLogListener
     - Data report log successfully requestId:[20]
    2024-09-24 15:07:40 [snail-job-job-1,937-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,937-1任务被中断
    2024-09-24 15:07:40 [snail-job-job-1,937-1] INFO  c.b.test.TestOverlayCanInterrupted
     - snail-job-job-1,937-1任务执行完成
    从日志输出能看出来,每个批次的任务都会被后面的新批次任务给停止。

    任务批次列表中的显示

    image-20240924151408848

测试总结

  • 新批次任务会停止上个批次任务,但是不保证一定停止成功。
  • 如果想让任务拥有被中断的能力,那么需要在写任务代码时,自己处理。比如本例通过循环判断当前线程是不是被中断。如果你的任务本身不具备这种能力。基本相当于下面将要验证的并行效果一样了。

验证阻塞策略-并行

策略解释
并行直接开干

这个策略顾名思义,就是和上一个批次的任务可以同时执行。

客户端代码

@Slf4j
@Component
public class TestConcurrency extends AbstractJobExecutor {

    @Override
    protected ExecuteResult doJobExecute(JobArgs jobArgs) {
        log.info("{}开始执行任务", Thread.currentThread().getName());
        for(int i = 1; i <= 5; i++) {
            log.info("{}执行第{}次", Thread.currentThread().getName(), i);
            ThreadUtil.sleep(60, TimeUnit.SECONDS);
        }
        log.info("{}任务执行完成", Thread.currentThread().getName());
        return ExecuteResult.success();
    }
}

页面配置

配置项配置内容
任务名称测试阻塞策略-覆盖-可中断
执行器名称com.bjltd.test.TestConcurrency
阻塞策略并行
触发类型固定时间
间隔时长60秒
超时时间600秒

测试结果

image-20240924155824922

测试总结

  • 这个在实际应用中,一定要谨慎使用。
  • 采用该策略一定要充分考虑幂等性问题。

验证阻塞策略-恢复

策略解释
恢复继续把失败的任务干完

该模式是用在任务类型是多个任务的情况下使用的,比如像 Map 这种很多任务的情景下,还有就是工作流。作用就是跑其中失败的任务。集群任务就一个任务项,执行完后批次状态就变了,连触发任务都不能了,所以千万别在集群模式下选该策略。这个恢复策略,在后续讲解中再进行介绍。

验证超时时间

超时时间的设置,目的是在定时任务到了这个阈值还没有执行完,那么就会调用批次任务线程池的 shutDownNow 方法。这个和上面说到的覆盖策略很相似。同样也都不能保证任务一定被停止。

客户端代码

@Slf4j
@Component
public class TestTimeout extends AbstractJobExecutor {
    @Override
    protected ExecuteResult doJobExecute(JobArgs jobArgs) {
        try {
            // 休眠 3 分钟
            Thread.sleep(3*60*1000);
        }catch (InterruptedException e){
            return ExecuteResult.failure();
        }
        return ExecuteResult.success();
    }
}

页面配置

配置项配置内容
任务名称测试超时时间
执行器名称com.bjltd.test.TestTimeout
路由策略轮询
阻塞策略丢弃
触发类型固定时间
间隔时长60秒
超时时间30秒

测试结果

客户端日志信息:

2024-09-24 16:53:40 [snail-netty-server-1] INFO  c.a.s.c.job.core.client.JobEndPoint
 - 批次:[1992] 任务调度成功. 
2024-09-24 16:53:40 [snail-job-job-1,992-1] INFO  com.bjltd.test.TestTimeout
 - snail-job-job-1,992-1开始执行任务
2024-09-24 16:53:50 [nioEventLoopGroup-2-1] INFO  c.a.s.c.c.l.report.ReportLogListener
 - Data report log successfully requestId:[7]
2024-09-24 16:54:10 [snail-job-job-1,992-1] INFO  com.bjltd.test.TestTimeout
 - snail-job-job-1,992-1任务被中断
2024-09-24 16:54:40 [snail-netty-server-3] INFO  c.a.s.c.job.core.client.JobEndPoint
 - 批次:[1993] 任务调度成功. 
2024-09-24 16:54:40 [snail-job-job-1,993-1] INFO  com.bjltd.test.TestTimeout
 - snail-job-job-1,993-1开始执行任务
2024-09-24 16:54:50 [nioEventLoopGroup-2-1] INFO  c.a.s.c.c.l.report.ReportLogListener
 - Data report log successfully requestId:[15]
2024-09-24 16:55:10 [snail-job-job-1,993-1] INFO  com.bjltd.test.TestTimeout
 - snail-job-job-1,993-1任务被中断
从客户端日志中可以看出来,任务从开始到任务被中断间隔 30 秒。

任务批次列表中显示

image-20240924165900683

测试总结

  • 超时时间是设置任务最大可以执行时长。到了这个时间就会停止任务。
  • 停止任务并不保证必然停止
  • 它是一个保底方案。避免因为各种意外而造成的长时间任务驻留。

总结

  • 会用注解和继承类两种方式,实现自己的客户端定时任务。
  • 通过在 Logback 配置中加 SnailLogbackAppender,能把日志写到服务端。
  • 阻塞策略是反正在该执行任务的时候,还有任务还在执行。可以选择的策略如下:
策略解释
丢弃你都没干完,我不干
覆盖你别干了,我来干
并行直接开干
恢复继续把失败的任务干完
  • 超时时间是到了这个时间点,如果任务还没执行完,就调用该任务线程池的 shutdownNow 方法。

本笔记原文来自博主 老马 9527 的文章 2.snail-job集群定时任务
如有侵权,请联系作者 马铃薯头 删除
最后修改:2025 年 10 月 30 日
温柔的好天气总是和我一样,帅的鸭皮!