Spring Boot程序正確中止的姿式

Spring Boot提供了2種優雅關閉進程的方式:html

  1. 基於管理端口關閉進程
  2. 基於系統服務方式關閉進程

基於管理端口關閉進程

基於管理端口方式實現進程關閉其實是模塊spring-boot-actuator提供的功能。java

首先,須要在項目中添加對應模塊依賴配置。web

  • 添加Maven依賴
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 添加Gradle依賴
dependencies {
    compile("org.springframework.boot:spring-boot-starter-actuator")
}

其次,在配置文件中添加對應的參數配置(以文件application.properties爲例說明)。spring

# 容許執行關閉操做
management.endpoint.shutdown.enabled=true
# 處於安全考慮,只容許在本地指定關閉操做
management.server.address=127.0.0.1
# 管理端口
management.server.port=8000
# 管理URL基礎路徑,默認爲「/」
management.endpoints.web.base-path=/ops
# 配置關閉進程Endpoint
management.endpoints.web.path-mapping.shutdown=shutdown
# 對外暴露管理Endpoint
management.endpoints.web.exposure.include=info, health, shutdown

完成上述準備工做之後,啓動Spring Boot應用,經過調用POST http://localhost:8000/ops/shutdown便可關閉進程。shell

實踐中一般將上述關閉進程的URL調用寫到腳本中,同時還能夠結合別的方式一塊兒確保進程必定能退出,以下爲腳本示例(pname指進程名稱):ubuntu

#!/bin/bash
# 先經過管理端口關閉進程
curl -X POST http://127.0.0.1:8000/ops/shutdown --connect-timeout 3 --max-time 5

# 再次經過名稱檢查進程是否被成功中止
count=`ps -ef |grep pname |grep -v "grep" |wc -l`
if [ $count -gt 0 ]; then
    if [ -f "$pid_file" ]; then
        # 若是存在進程ID文件,則讀取進程ID使用信號量通知方式關閉進程
        pid=`cat $pid_file`
        kill -15 $pid
    else
        # 經過名稱方式查找到進程ID,使用信號量通知方式關閉進程
        pid=`ps -ef |grep pname |grep -v "grep"| awk '{print $2}'`
        kill -15 $pid
    fi
fi

關於經過管理端口關閉Spring Boot進程的詳細說明參見:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-endpoints安全

經過系統服務方式中止進程

Spring Boot支持直接將打包好的可執行jar包以系統服務方式運行,具體實現方式以下所述。bash

首先,將應用打包爲徹底可執行的jar包。app

  • Maven打包配配置
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <!-- 這個配置很是重要,使打包好的jar包具有可執行權限-->
        <executable>true</executable>
    </configuration>
</plugin>
  • Gradle打包配置
bootJar {
    launchScript()
}

其次,將打包好的應用jar包添加爲系統服務(在ubuntu18.04 LTS上實現,基於systemd)curl

1.假設將Spring Boot應用安裝到/var/myapp目錄下:將上述打包好的jar包拷貝到/var/myapp(目錄不存在,手動建立)
2.在/etc/systemd/system下添加指定名稱的系統服務:myapp.service,內容以下:

[Unit]
Description=myapp
After=syslog.target

[Service]
User=root  ## 注意:這裏配置的是未來啓動該服務的Linux系統用戶名,影響權限
ExecStart=/var/myapp/myapp.jar
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

3.啓動服務

$ sudo systemctl enable myapp.service
$ sudo systemctl start myapp.service

若是須要查看應用啓動日誌,請執行:$ journalctl -f

若是啓動服務失敗,請檢查對應名稱的服務文件是否放在正確位置(如:systemd系統須要放在/etc/systemd/system目錄下),或者檢查啓動服務的用戶權限,一些錯誤情形能夠參考:https://springjavatricks.blogspot.com/2018/06/installing-spring-boot-services-in.html

關於將Spring Boot應用部署爲系統服務的詳細說明參見: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#deployment-install

寫在最後

我在如何優雅地中止Java進程中有講到如何實如今進程退出以前作一些收尾的工做,這在Spring Boot中一樣適用,只須要監聽對應的信號量並註冊JVM關閉鉤子便可。

@SpringBootApplication
public class SpringbootApplication {
    private static final Logger logger = LoggerFactory.getLogger(SpringbootApplication.class);
    public static void main(String[] args) {
        // 在Spring Boot應用中經過監聽信號量和註冊關閉鉤子來實如今進程退出以前執行收尾工做
        // 監聽信號量
        Signal sg = new Signal("TERM");
        Signal.handle(sg, new SignalHandler() {
            @Override
            public void handle(Signal signal) {
                logger.info("do signal handle: {}", signal.getName());
                System.exit(0);
            }
        });

        // 註冊關閉鉤子
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                // 執行收尾工做
                logger.info("do something on shutdown hook");
            }
        });

        SpringApplication.run(SpringbootApplication.class, args);
        logger.info("Start DONE.");
    }
}

另外,須要注意的是:在普通的Java應用程序中,當出現RuntimeExeception或OOM時會觸發關閉鉤子的執行;可是在Spring Boot應用中,當出現RuntimeException或OOM時並不會觸發關閉鉤子的執行(Spring Boot使用了嵌入式Tomcat)。

【參考】
https://www.jianshu.com/p/44ef43b282f0 正確、安全地中止SpringBoot應用服務

相關文章
相關標籤/搜索