如何开启优雅下线

本文旨在为您介绍如何在阿里云任务调度XXL-JOB版开启优雅下线,帮助您处理实际业务下线的场景。

背景信息

在实际业务场景下,定时任务持续地按固定频率在应用进程中执行。当应用发布重启时,进程需停止下线。如果直接关闭应用,正在运行中的定时任务将被中断,可能导致数据不完整调度成功大幅下跌等问题。为避免该情况,MSE XXL-JOB版本在完全兼容开源的基础上,增强实现了定时任务的优雅下线功能。在关闭应用前,先通知调度服务端摘流,再等待当前正在运行中的任务执行完成后,然后再安全地关闭应用。

前提条件

  • 引擎版本需2.1.0及以上。版本详情,请参见XXL-JOB引擎版本

  • 客户端需接入SchedulerX plugin包。版本详情,请参见XXL-JOB插件版本

    <dependency>
      <groupId>com.aliyun.schedulerx</groupId>
      <artifactId>schedulerx3-plugin-xxljob</artifactId>
      <version>最新版本</version>
    </dependency>

优雅下线示意图

image.jpeg

如何启用优雅下线

如何在不同的业务形态和部署场景中,完整配置并启用优雅下线方案。该过程主要包括两个步骤:

步骤一:执行器各框架初始化集成

业务应用不同的部署形态,需要按不同方式进行初始化集成。

形态一:SpringBoot业务应用(推荐)

如果业务应用采用了SpringBoot方式集成XXL-Job的执行器,那么可自动完成相应的优雅下线能力初始化集成。只需完成如下两个步骤即可:

  1. 添加SchedulerX插件pom依赖。版本详情,请参见XXL-JOB插件版本

    <dependency>
      <groupId>com.aliyun.schedulerx</groupId>
      <artifactId>schedulerx3-plugin-xxljob</artifactId>
      <version>最新版本</version>
    </dependency>
  2. 添加应用配置参数,开启优雅下线。详细参数说明,请参见配置参数说明

    # 参数说明参考上一章节
    xxl.job.executor.shutdownMode=WAIT_ALL

形态二:Spring业务应用

如果业务应用是通过Spring框架启动的Web应用,除了添加POM依赖应用启动参数(参考形态一:SpringBoot业务应用(推荐)),还需通过初始化配置XxlJobExecutorEnhancerInitializer,在web.xml中添加如下配置:

<web-app>
  <context-param>
        <!-- Spring ApplicationContextInitializer 增强xxljob executor功能 -->
        <param-name>globalInitializerClasses</param-name>
        <param-value>com.aliyun.schedulerx.xxljob.enhance.XxlJobExecutorEnhancerInitializer</param-value>
    </context-param>
</web-app>

形态三:Frameless Java业务应用

如果业务应用采用xxl-job executor案例中的Frameless方式,通过纯Java编码方式启动业务应用时,可通过自定义编码完成优雅下线功能初始化集成。首先业务应用还是需要添加POM依赖应用启动参数(参考形态一:SpringBoot业务应用(推荐)),相关参考案例如下:

  • Executor启动前,添加:EnhancerLoader.load(xxlJobProp),完成功能增加加载。

  • Executor启动前,添加:Runtime.getRuntime().addShutdownHook(...),为当前应用添加下线Hook实现。

示例代码

public static void main(String[] args) {
    try {
        // load executor prop
        Properties xxlJobProp = FrameLessXxlJobConfig.loadProperties("xxl-job-executor.properties");

        // 初始化增加xxl-job executor加载
        EnhancerLoader.load(xxlJobProp);

        // start xxl-job executor
        FrameLessXxlJobConfig.getInstance().initXxlJobExecutor(xxlJobProp);

        // 添加系统优雅线下hook
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                FrameLessXxlJobConfig.getInstance().destroyXxlJobExecutor();
            }
        });
        // Blocks until interrupted
        while (true) {
            try {
                TimeUnit.HOURS.sleep(1);
            } catch (InterruptedException e) {
                break;
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    } finally {
        // destroy
        FrameLessXxlJobConfig.getInstance().destroyXxlJobExecutor();
    }
}

步骤二:应用停机下线处理

自建部署,通过kill -15停机

在自建CD流程中通常会有一个应用进程停止的节点,该节点可通过构建一个stop.sh 脚本用于应用进程停止退出。脚本内容需包含应用优雅下线的相关逻辑处理,参考如下停机脚本。

image.jpeg

应用进程停机脚本案例:

# 应用启用成功后进程ID信息会写入app.pid文件
PID="{应用部署路径}/app.pid"
FORCE=1
if [ -f ${PID} ]; then
  TARGET_PID=`cat ${PID}`
  kill -15 ${TARGET_PID}
  loop=1
  while(( $loop<=5 ))
  do
    ## health 检查当前应用进程确实已经结束,可根据应用特征自定义
    health
    if [ $? == 0 ]; then
      echo "check $loop times, current app has not stop yet."
      sleep 5s
      let "loop++"
    else
      FORCE=0
      break
    fi
  done
  if [ $FORCE -eq 1 ]; then
  	echo "App(pid:${TARGET_PID}) stop timeout, forced termination."
    kill -9 ${TARGET_PID}
  if
  rm -rf ${PID}
  echo "App(pid:${TARGET_PID}) stopped successful."
fi

K8S容器化部署,通过PreStop停机

利用k8s pod的生命周期管理可默认实现优雅下线。同时还可以使用preStop hook,通过exec执行脚本和HTTP请求方式来实现优雅下线逻辑处理。

  • 默认无效修改:如果业务应用进程为容器中的主进程PID 1,则默认会先给主进程发送SIGTERM信号进行优雅下线。

  • 自定义preStop:如容器内为复杂的多进程关系,可通过配置preStop脚本,自定义通过kill -15 PID停止应用进程,或者调用提前预设的stop.sh脚本实现应用进程退出。

重要

方案中PodterminationGracePeriodSeconds参数会控制优雅下线的整体最长等待时间(默认:30s),需要按业务需要合理配置。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app-container
        image: my-app-image:latest
        lifecycle:
          preStop:
            exec:
              # command: ["/bin/sh", "-c", "kill -15 PID && sleep 30"]
              command: ["/bin/sh", "-c", "脚本路径/stop.sh"]

阿里云上应用发布平台自动集成

敬请期待。

配置参数说明

在业务应用中需要配置如下参数开启优雅下线功能,并且支持通过该参数配置两种不同的优雅下线策略。

# 优雅下线模式,WAIT_ALL:等待全部; WAIT_RUNNING:等待运行中。
# 该参数不配置,则表示保持XXL-JOB原始逻辑不变(默认不开启优雅下线)。
xxl.job.executor.shutdownMode=WAIT_ALL

下线模式

描述

等待全部(WAIT_ALL

(推荐)该模式下,待所有已接收的任务(正在运行及队列中等待的任务)执行完成后,应用才退出。

等待运行中(WAIT_RUNNING

该模式下,应用在退出时,将等待已分配线程并在处理中的执行记录完成,队列中的任务将被放弃执行。