如何優雅的停止spring boot service

前言

往往"停止服務"的代名詞就是暴力,不計後果的,因爲在強制停止的時候,不會管裏面是否還有正在運行的線程。

碰巧最近由於在搞AWS的auto scalinng,不知道的朋友,可以把它理解爲AWS可以自動的擴展或者是收縮我們的服務器,使得可以減少經費,想更深入瞭解的可以自行google。

這個出發點好是好,但是我也在實際使用的時候,發現了點問題:如果docker被stop了,裏面可能存活的就被強制停止了,這個時候我麼應該怎麼辦呢?

正文

根據

  1. docker stop命令實際上執行的是kill pid 指令,如果不跟隨停止信號的話,默認情況下使用的是SIGNTEMR
  2. 並且如果docker中的主進程被停止,那麼docker自然會停止。

所以推斷問題的關鍵在於,我們需要去操控spring boot 需要優雅的stop,也就是我們今天的主角。

說了這麼多廢話該提起,下面進入正題,網上其實有很多這方面的教程例如說下面這個就寫的很好:

https://www.cnblogs.com/harrychinese/p/SpringBoot-graceful-shutdown.html

但是網上的文檔幾乎都是把注入bean放在啓動類中的,而我給它放在了@configuration 的類裏,下面呢看下主要代碼:

首先是最主要的監聽容器關閉,並且進行處理的代碼:

package com.demo.timeout.tomcat;

import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
    private volatile Connector connector;
    private int waitTime;

    @Value("${STOP_WAIT_TIMEOUT}")
    public void setWaitTime(int waitTime) {
        this.waitTime = waitTime;
    }
    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        this.connector.pause();
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        try {
            if (executor instanceof ThreadPoolExecutor) {
                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                threadPoolExecutor.shutdown();
                if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                    System.out.println("Tomcat 進程在" + waitTime + " 秒內無法結束,嘗試強制結束");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
    }
}

然後是將其注入的代碼:

package com.demo.timeout.config;

import com.demo.timeout.tomcat.GracefulShutdown;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class GracefulShutDownConfig {

    @Autowired
    private GracefulShutdown gracefulShutdown;

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();
        tomcatServletWebServerFactory.addConnectorCustomizers(gracefulShutdown);
        return tomcatServletWebServerFactory;
    }
}

最後寫了一個API的測試代碼

package com.demo.timeout.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/30s-process")
    public String longProcessAPI(){
        System.out.println("enter this long process function");
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "30s線程啓動";
    }
}

如果有需要可以看一下我上傳的代碼:

https://github.com/luckypoison/SpringBoot-Shutdown-Graceful

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章