SpringBoot 實戰 之 優雅終止服務
由於 SpringBoot 是一個微服務框架,其生產部署的方式也需要儘可能的簡單,與常規的 Web 應用有著一個巨大的不同之處,它可以內嵌一個 Web 容器,如:Tomcat、Jetty等,不再需要將應用打包成容器規定的特定形式。
對於 SpringBoot 來說,打包成一個簡單的 Jar 包直接使用 java -jar
即可啟動,這是一種非常優雅的方式,但同時也帶來了一定的問題,如:應用如何停止?在過去,應用程式是部署在特定的容器中的,使用容器提供的指令碼可以優雅停服,但現在容器被內嵌了,指令碼沒有了,怎麼辦?直接 kill 是一種方式,但未免顯得太過粗魯,而且可能帶來許多意想不到的問題。
既然我們能想到問題,框架的制定者也會想到,那麼他們有沒有為我們準備好解決方案呢?答案是有的,下面我介紹下我瞭解到的幾種方案。
1. 使用 Endpoints
在 SpringBoot 官方文件的第4部分中介紹了為應用釋出生產準備的各種特性,其中,通過 Actuator 的 HTTP Endpoint,開發人員可以方便地對應用的監控與管理。
引入指定的 starter 包:
"org.springframework.boot:spring-boot-starter-actuator:${springbootVersion}"
在 application.yml 中開啟如下兩個配置,即可實現通過 Http 請求停止應用
management:
security:
enabled: false
endpoints:
shutdown:
enabled: true
操作命令如下:
curl -X POST http://host:port/shutdown
但這種方式有一個非常嚴重的問題,那就是任意人都可以控制應用的停止,這對於一個生產應用無疑是不可接受的。有些人可能會想,現在的連結地址太簡單了,非維護人員也可以輕易地猜出來,如果使用一個非常複雜的地址是否可以避免這個問題。很好,這個提議不錯,那我們再看看 SpringBoot 為我們提供的相關配置。
endpoints:
shutdown:
enabled: true
path: /xxx
配置完成後,上面的命令就不可用了,需更新命令為:
curl -X POST http://host:port/xxx
其中的/xxx
當然只是我隨手設定的一個,你可以設定任意的地址。雖然安全性高了那麼一點,但這樣的安全級別仍然是無法應用到生產環境的。那是否還有其它的防護手段呢?有,除了修改shutdown的路徑外,我們還可以給所有的管理操作加上一個統一的上下文,配置獨立的埠,並限制指定IP訪問(一般限定為本機),配置如下:
management:
security:
enabled: false
port: 9001
address: 127.0.0.1
context-path: /admin
變更後的停服命令為:
curl -X POST http://127.0.0.1:9001/admin/xxx
這樣其實已經足夠安全了,為了進一步的保證系統的安全,再給其加上一層 HTTP Basic Auth。
增加 Security 依賴:
"org.springframework.boot:spring-boot-starter-security:${springbootVersion}"
修改配置檔案如下:
endpoints:
shutdown:
enabled: true
path: /xxx
management:
security:
enabled: true
port: 9001
address: 127.0.0.1
context-path: /admin
security:
basic:
enabled: true
path: /admin
user:
name: root
password: 123456
配置完成後,最終的停服命令為:
curl -X POST -u root:123456 http://127.0.0.1:9001/admin/xxx
2. 註冊為系統服務
除了使用 java -jar
執行 SpringBoot 應用程式外,還可以輕鬆地用 init.d 或 systemd 註冊成 Linux/Unix 系統服務,這使得在生產環境中,安裝和管理 SpringBoot 應用程式變得非常簡單。
在Maven工程裡面,為了建立一個“完全可執行”的 jar,需要引入如下外掛:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.2.RELEASE</version>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
在 Gradle 工程裡面,等效配置如下:
plugins {
id 'org.springframework.boot' version '1.5.2.RELEASE'
}
springBoot {
executable = true
}
配置完成後,即可以通過 ./application-name.jar
執行構建好的應用程式。
最後,我們需要將打包好的應用程式安裝成一個init.d 服務,這樣就可以很方便地使用 Unix/Linux 進行管理了。操作方式很簡單,只需要將應用程式簡單的連結到 init.d 即可(其中funda為我自己的應用名,自己實驗時需要視情況替換)。
ln -s /app/funda/funda.jar /etc/init.d/funda
檢查連結是否建立成功
ls -l /etc/init.d/funda
啟動服務,應用日誌可檢視檔案 /var/log/funda.log
service funda start
其它常用命令
# 檢視應用執行狀態
service funda status
# 停止應用
service funda stop
問題彙總:
- 在連結成功後,應用啟動時,無法成功啟動,提示
Unable to find Java
,使用如下命令將Jdk的java命令連結到/sbin/java即可。
ln -s /usr/local/jdk1.8.0_131/bin/java /sbin/java
參考連結:
專案連結: