如何將任務(wù)調(diào)度應(yīng)用優(yōu)雅下線
本文介紹如何將任務(wù)調(diào)度應(yīng)用優(yōu)雅下線。
背景信息
在實(shí)際業(yè)務(wù)場(chǎng)景下,定時(shí)任務(wù)持續(xù)地按固定頻率在應(yīng)用進(jìn)程中執(zhí)行。當(dāng)應(yīng)用在發(fā)布重啟時(shí),進(jìn)程需暫時(shí)下線。如果直接關(guān)閉應(yīng)用,正在進(jìn)行中的定時(shí)任務(wù)將被中斷,可能導(dǎo)致數(shù)據(jù)不完整或其他問(wèn)題。為避免該情況,SchedulerX實(shí)現(xiàn)了定時(shí)任務(wù)的優(yōu)雅下線功能,在關(guān)閉應(yīng)用前,需等待當(dāng)前正在進(jìn)行的任務(wù)執(zhí)行完成后,然后再安全地關(guān)閉應(yīng)用。
使用限制
客戶端版本為1.10.8及以上。
優(yōu)雅下線示意
如何配置
當(dāng)前 SchedulerX 可同時(shí)支持單機(jī)任務(wù)、分布式任務(wù)多種執(zhí)行模式下的優(yōu)雅下線能力。另外,離線定時(shí)任務(wù)不同于實(shí)時(shí)在線業(yè)務(wù)場(chǎng)景,可能存在執(zhí)行長(zhǎng)耗時(shí)的特性,但是應(yīng)用進(jìn)程在停機(jī)時(shí)不會(huì)無(wú)限等待。您可以通過(guò)以下配置運(yùn)行中的任務(wù)應(yīng)用延遲停機(jī)的時(shí)間。
#優(yōu)雅下線模式,WAIT_ALL:等待全部; WAIT_RUNNING:等待運(yùn)行中。
#該參數(shù)不配置則不啟用優(yōu)雅下線功能(默認(rèn)不開(kāi)啟優(yōu)雅下線)。
spring.schedulerx2.graceShutdownMode=WAIT_ALL
#優(yōu)雅下線等待超時(shí)時(shí)長(zhǎng)(單位:秒),默認(rèn)無(wú)超時(shí)。
spring.schedulerx2.graceShutdownTimeout=10
#是否開(kāi)啟http服務(wù)接口,默認(rèn)false。
spring.schedulerx2.httpServerEnable=true
#http下線接口端口,默認(rèn)51886。
spring.schedulerx2.httpServerPort=52333
下線模式 | 描述 |
等待全部( | (推薦)該模式下,待所有已接收的任務(wù)和子任務(wù)執(zhí)行完成后,應(yīng)用才退出。 |
等待運(yùn)行中( | 該模式下,應(yīng)用在退出時(shí),將等待已分配線程并在處理中的任務(wù)或子任務(wù)執(zhí)行完成,隊(duì)列中的任務(wù)將被放棄執(zhí)行。 |
下線實(shí)現(xiàn)方式
為了方便業(yè)務(wù)應(yīng)用集成在各種部署形態(tài)下的發(fā)布流程,您可以使用以下幾種方式實(shí)現(xiàn)優(yōu)雅停機(jī)。
方式一:通過(guò)kill -15優(yōu)雅停止
SchedulerX SDK 添加了JVM進(jìn)程關(guān)閉時(shí)的鉤子函數(shù),實(shí)現(xiàn)了程序優(yōu)雅退出的功能。用戶在停止應(yīng)用進(jìn)程時(shí)可以采用kill -15 進(jìn)程ID
的方式停止應(yīng)用進(jìn)程,運(yùn)行中的應(yīng)用進(jìn)程將按照對(duì)應(yīng)的配置策略完成任務(wù)執(zhí)行后的優(yōu)雅退出。
執(zhí)行下線腳本停止業(yè)務(wù)應(yīng)用時(shí),不能直接采用kill -9
,否則該能力將失效。部分業(yè)務(wù)應(yīng)用下線的腳本先采用kill -15
,監(jiān)測(cè)并等待一段時(shí)間后,再執(zhí)行kill -9
命令強(qiáng)制停止。建議根據(jù)應(yīng)用任務(wù)的特征來(lái)合理設(shè)置下線等待的時(shí)間,防止發(fā)布過(guò)程過(guò)慢。
方式二:通過(guò)SpringBoot shutdown事件停止
如果通過(guò) SpringBoot 方式初始化接入 SchedulerX,可以兼容SpringBoot 提供的actuator功能來(lái)實(shí)現(xiàn)優(yōu)雅下線,它能在響應(yīng) Spring 容器關(guān)閉事件時(shí)進(jìn)行定時(shí)任務(wù)的優(yōu)雅下線處理。
開(kāi)啟SpringBoot actuator步驟如下:
在應(yīng)用程序的
pom.xml
文件中添加依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置開(kāi)啟shutdown接口。
management.endpoint.shutdown.enabled=true
management.endpoints.web.exposure.include=shutdown
執(zhí)行下線。
curl -X GET http://NodeIP:Port/actuator/shutdown
方式三:通過(guò)HTTP接口停止
通過(guò)配置項(xiàng)在當(dāng)前業(yè)務(wù)的應(yīng)用進(jìn)程中開(kāi)啟一個(gè)HTTP接口服務(wù),用于外部自定義請(qǐng)求實(shí)現(xiàn)當(dāng)前應(yīng)用進(jìn)程中的定時(shí)任務(wù)優(yōu)雅下線處理。應(yīng)用參數(shù)配置如下:
#是否開(kāi)啟http服務(wù)接口,默認(rèn)false。
spring.schedulerx2.httpServerEnable=true
#http下線接口端口,默認(rèn)51886。
spring.schedulerx2.httpServerPort=51886
調(diào)用如下HTTP接口即可實(shí)現(xiàn)任務(wù)調(diào)度服務(wù)的優(yōu)雅下線
curl -X GET http://NodeIP:51886/schedulerx/worker/shutdown
運(yùn)用集成方案
通常需要將對(duì)應(yīng)的優(yōu)雅下線能力集成到日常發(fā)布的運(yùn)維流程中,以規(guī)避應(yīng)用重啟時(shí)出現(xiàn)的定時(shí)任務(wù)業(yè)務(wù)有損。以下將介紹幾種常見(jiàn)的集成形式。
方案一:自建部署流程集成
通常在自建CD流程中會(huì)有一個(gè)應(yīng)用進(jìn)程停止的節(jié)點(diǎn),該節(jié)點(diǎn)可通過(guò)構(gòu)建一個(gè)stop.sh腳本用于應(yīng)用進(jìn)程的停止退出。腳本內(nèi)容需包含應(yīng)用優(yōu)雅下線的相關(guān)邏輯處理,可采用上述章節(jié)下線實(shí)現(xiàn)方式的任意一種方式即可。
自建CD流程如圖所示:
停止應(yīng)用進(jìn)程的腳本案例:
#應(yīng)用啟用成功后進(jìn)程ID信息會(huì)寫入app.pid文件
PID="{應(yīng)用部署路徑}/app.pid"
FORCE=1
if [ -f ${PID} ]; then
TARGET_PID=`cat ${PID}`
kill -15 ${TARGET_PID}
loop=1
while(( $loop<=5 ))
do
#health 檢查當(dāng)前應(yīng)用進(jìn)程確實(shí)已經(jīng)結(jié)束,可根據(jù)應(yīng)用特征自定義
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生命周期中銷毀時(shí)的preStop事件處理來(lái)觸發(fā)應(yīng)用的優(yōu)雅下線,同時(shí)preStep hook可通過(guò)exec執(zhí)行腳本和HTTP請(qǐng)求方式來(lái)實(shí)現(xiàn)優(yōu)雅下線邏輯處理。
Exec腳本方式:直接通過(guò)停止應(yīng)用進(jìn)程,或者調(diào)用提前預(yù)設(shè)的
stop.sh
腳本實(shí)現(xiàn)應(yīng)用進(jìn)程退出。
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"]
HTTP接口方式:
對(duì)于啟用SpringBoot actuator shutdown 能力時(shí)可配置Path為
/actuator/shutdown
。非Spring應(yīng)用可在再開(kāi)啟SchedulerX SDK優(yōu)雅下線HTTP接口后,配置Path為
/schedulerx/worker/shutdown
。
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:
httpGet:
path: /schedulerx/worker/shutdown
port: 51886
scheme: HTTP
方案三:阿里云上應(yīng)用發(fā)布平臺(tái)自動(dòng)集成
如果業(yè)務(wù)應(yīng)用已經(jīng)采用了公有云上EDAS或者M(jìn)SE和ACK發(fā)布部署時(shí),在對(duì)應(yīng)的平臺(tái)上開(kāi)啟了無(wú)損下線能力即可自動(dòng)集成SchedulerX定時(shí)任務(wù)的優(yōu)雅下線能力。具體操作,請(qǐng)參見(jiàn)EDAS應(yīng)用發(fā)布開(kāi)啟應(yīng)用無(wú)損下線和MSE+ACK部署形態(tài)下應(yīng)用無(wú)損下線。