diff --git a/doc/XXL-JOB官方文档.md b/doc/XXL-JOB官方文档.md index d75e24c0..fd4379ea 100644 --- a/doc/XXL-JOB官方文档.md +++ b/doc/XXL-JOB官方文档.md @@ -59,6 +59,7 @@ XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅 - 36、权限控制:执行器维度进行权限控制,管理员拥有全量权限,普通用户需要分配执行器权限后才允许相关操作; - 37、AI任务:原生提供AI执行器,并内置多个AI任务Handler,与spring-ai、ollama、dify等集成打通,支持快速开发AI类任务。 - 38、审计日志:记录任务操作敏感信息,用于系统监控、审计和安全分析,可快速追溯异常行为以及定位排查问题。 +- 39、优雅停机:调度中心停机,检测时间轮非空时主动等待调度完成;客户端停机,检测存在运行中任务时,停止接收新任务并主动等待任务执行完成; ### 1.4 发展 @@ -1824,6 +1825,10 @@ docker compose up -d // 其他:如需调整环境配置,如Mysql密码、端口等,可以在docker-compose.yml中修改;另外,如果需要修改Mysql数据持久化目录,可以通过 MYSQL_PATH 变量在启动时快速设置; MYSQL_PATH={自定义数据库持久化目录} docker compose up -d ``` +### 5.24 优雅停机 +针对任务调度场景,优雅停机包括调度中心与执行器两部分: +- 调度中心优雅停机:调度中心停机时,如果检测到时间轮非空则主动等待等待一段时间,等待调度处理完成;否则立即停机; +- 执行器优雅停机:执行器停机时,如果检测到存在运行中任务,在停止接收新任务后会主动等待一段时间,等待任务执行完成;否则立即停机; ## 六、调度中心/执行器 OpenApi @@ -2748,8 +2753,7 @@ public void execute() { - 2、【优化】调度线程事务提交逻辑调整,避免边界条件下线程异常退出,增强健壮性; - 3、【优化】调度日志列表排序逻辑优化,提升易读性; - 4、【优化】表格交互优化:优化分页显示配置;禁用分页循环;多选行操作优化/默认单选; -- 5、【ING】优雅停机:服务端停机,检测时间轮非空sleep 5s; - - 客户端停机,检测任务运行中,server停止后sleep5s; +- 5、【优化】优雅停机:调度中心停机,检测时间轮非空时主动等待调度完成;客户端停机,检测存在运行中任务时,停止接收新任务并主动等待任务执行完成; - 6、【TODO】任务调度触发后分批批量更新,提升调度性能; diff --git a/xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java b/xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java index e1c55487..9d27cdd0 100644 --- a/xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java +++ b/xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; /** * Created by xuxueli on 2016/3/2 21:14. @@ -29,6 +30,11 @@ import java.util.concurrent.ConcurrentMap; public class XxlJobExecutor { private static final Logger logger = LoggerFactory.getLogger(XxlJobExecutor.class); + /* + * elegant shutdown wait seconds + */ + private static final long ELEGANT_SHUTDOWN_WAITING_SECONDS = 5; + // ---------------------- field ---------------------- private String adminAddresses; private String accessToken; @@ -89,22 +95,31 @@ public class XxlJobExecutor { initAdminBizList(adminAddresses, accessToken, timeout); - // init JobLogFileCleanThread + // 1、init JobLogFileCleanThread JobLogFileCleanThread.getInstance().start(logRetentionDays); - // init TriggerCallbackThread + // 2、init TriggerCallbackThread TriggerCallbackThread.getInstance().start(); - // init executor-server + // 3、init executor-server initEmbedServer(address, ip, port, appname, accessToken); } public void destroy(){ - // destroy executor-server + // 1、destroy executor-server stopEmbedServer(); // destroy jobThreadRepository if (!jobThreadRepository.isEmpty()) { + + // 1.1、elegant shutdown wait job finish + try { + TimeUnit.SECONDS.sleep(ELEGANT_SHUTDOWN_WAITING_SECONDS); + } catch (Throwable e) { + logger.error(e.getMessage(), e); + } + + // 1.2、interupt all job-thread for (Map.Entry item: jobThreadRepository.entrySet()) { JobThread oldJobThread = removeJobThread(item.getKey(), "web container destroy and kill the job."); // wait for job thread push result to callback queue @@ -121,10 +136,10 @@ public class XxlJobExecutor { jobHandlerRepository.clear(); - // destroy JobLogFileCleanThread + // 2、destroy JobLogFileCleanThread JobLogFileCleanThread.getInstance().toStop(); - // destroy TriggerCallbackThread + // 3、destroy TriggerCallbackThread TriggerCallbackThread.getInstance().toStop(); }