SpringBoot 優(yōu)雅退出

歡迎轉(zhuǎn)載,但請(qǐng)?jiān)陂_頭或結(jié)尾注明原文出處【blog.chaosjohn.com】

背景

公司某項(xiàng)目的后端技術(shù)棧采用的是 SpringBoot + Kotlin,具體細(xì)節(jié)本文不作展開。

業(yè)務(wù)中,用戶在我們平臺(tái)上購(gòu)買產(chǎn)品,我們通過實(shí)時(shí)請(qǐng)求供應(yīng)商的交易API,在商戶余額(即我們?cè)诠?yīng)商那邊的儲(chǔ)值)里扣除對(duì)應(yīng)的金額后,才會(huì)將產(chǎn)品的“源文件”返回,進(jìn)而交付給用戶。

項(xiàng)目迭代中我們需要不斷的部署新的版本上線,最初的做法很暴力,構(gòu)建新的 flatjar 運(yùn)行起來(lái),然后直接結(jié)束掉 舊的 java 進(jìn)程。

然后就發(fā)現(xiàn)問題了:某次購(gòu)買行為中,交易API的請(qǐng)求發(fā)送出去了,還沒等待請(qǐng)求返回,進(jìn)程就被殺死了,造成儲(chǔ)值余額被扣了,但是“源文件”并沒有拿到,而供應(yīng)商的API又存在延遲,即交易API處理成功,但是通過訂單查詢API卻查不到。

所以,如何才能在 java 進(jìn)程被殺死的時(shí)候,做完 善后工作 再退出呢?

解決

一般我們殺死進(jìn)程,都是給進(jìn)程發(fā)送信號(hào) Signal

  • SIGINT
  • SIGTERM

這里我們用到兩個(gè)類:

  • sun.misc.Signal,代表信號(hào)
  • sun.misc.SignalHandler,用來(lái)處理進(jìn)程接收到的信號(hào)

同時(shí),我們?cè)O(shè)計(jì)以下全局變量:

  • var killSignalReceived = false // 用來(lái)表示是否接收到終止信號(hào)
  • val jobSet = mutableSetOf<String>() // 表示需要在結(jié)束之前等待完成的任務(wù)

所以交易API的請(qǐng)求處理,我們將改成:

if (!killSignalReceived) { // 只有為 `false`,才進(jìn)行交易處理
  synchronized(jobSet) {
      jobSet.add(jobTitle) // 處理之前,將當(dāng)前處理任務(wù)存入 `jobSet`
  }
  // Todo: 具體實(shí)現(xiàn)請(qǐng)求交易API的處理
  synchronized(jobSet) {
      jobSet.remove(jobTitle) // 處理結(jié)束,將當(dāng)前處理任務(wù)從 `jobSet` 中移除
  }
}

在程序主函數(shù)中,新增:

val killHandler = SignalHandler {
    logger.error("intercept signal of ${it.toJson()}")
    killSignalReceived = true
    while (jobSet.isNotEmpty()) {
        logger.error("Background Jobs: \n\t\t" + jobSet.joinToString("\n\t\t") + "\n")
        Thread.sleep(1000)
    }
    exitProcess(0)
}
Signal.handle(Signal("INT"), killHandler)
Signal.handle(Signal("TERM"), killHandler)

當(dāng) java 進(jìn)程接收到 SIGINTSIGTERM 信號(hào)后:

  • killSignalReceived 置為 true
  • 循環(huán)檢查 jobSet 是否為空
    • 不為空,打印當(dāng)前未結(jié)束的任務(wù)列表,等待1s后再次檢查
    • 為空,程序退出

如果需要忽略 善后工作 強(qiáng)行退出,給進(jìn)程發(fā)送 SIGKILL 即可:

  • kill -KILL pid
  • kill -9 pid
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容