springboot shutdown(停機)
工作中還沒有使用過springboot搭建分布式服務。只是通過springboot搭建了一個簡單的web工程,跑一些定時任務。所以不清楚springboot集群是如何部署和啟停應用的。因為某些原因,工作中不能直接使用springboot打包成jar形式發布。最終決定自己寫打包腳本,打包zip(tar.gz),並編寫啟動和停止腳本。(見文章《springboot打包成zip部署,並實現優雅停機》和《springboot 啟動腳本優化》)
問題
那篇文章中通過springboot提供的應用監控插件,可以實現停機服務。但是有個問題,停機請求的url是寫在stop.sh腳本的。如果應用部署的的配置調整了端口號,這樣stop.sh腳本也要調整。現在因為單機部署還是沒有什么問題,后面如果開始集群部署,肯定會引起很多麻煩。
思路
這里就想到了tomcat的啟動和停止原理。
tomcat
tomcat啟動時讀取server.xml配置,同時監聽一個端口,用於接收停機指令,一般默認是8005。當我們執行shutdown.sh時,執行同樣的代碼,但是會傳入shutdown的參數,同樣讀取server.xml配置,根據配置的端口,發送shutdown指令,這樣啟動的服務就收到了停機指令,就開始停機操作。通過啟動和停機都讀取同一份配置文件,避免啟動和停機需要維護兩份配置的問題。
springboot
鑒於tomcat的啟停原理,如果我停機的java代碼中也能讀取springboot的配置文件,獲取應用的端口信息,再根據這個端口信息去發送停機請求。這樣就可以避免修改stop.sh腳本。所以最重要的是如何自己讀取springboot的配置文件。
改造
因為重點是讀取springboot的配置,項目采用的是yml形式配置,一開始想到的是自己解析yml,但是出現了問題。項目采用application-dev.yml、application-test.yml等區分不同環境配置。我需要先解析application.yml配置,再根據spring.profiles.active配置讀取對應配置文件。這樣是不是太麻煩了呢?這個工作springboot不是已經做了一遍了嗎?我是不是可以直接拿springboot解析配置文件的代碼直接使用呢?
帶着這樣的疑問,我決定尋找一些springboot讀取配置文件的原理。最后找到了這篇文章Spring Boot源碼分析-配置文件加載。文中用的springboot版本是Spring Boot 2.1.0.RELEASE。我用的是Spring Boot 2.0.4.RELEASE,剛好可以借鑒一下。
具體springboot如何讀取配置文件,文章中已經寫的很清楚了。這里就不復述了。直接貼一下改造后的Shutdown類。
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.config.ConfigFileApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.util.StringUtils;
import java.io.IOException;
/**
* 應用關閉入口
*
* @author dingzg
*/
public class Shutdown extends ConfigFileApplicationListener {
private static final Logger log = LoggerFactory.getLogger(Shutdown.class);
public static void main(String[] args) {
String url = "http://127.0.0.1:%s/actuator/shutdown";
Shutdown shutdown = new Shutdown();
ConfigurableEnvironment environment = shutdown.load();
String port = environment.getProperty("management.server.port");
if (StringUtils.isEmpty(port)) {
port = environment.getProperty("server.port");
}
url = String.format(url, port);
log.info("shutdown url = {}", url);
HttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
try {
HttpResponse httpResponse = httpClient.execute(httpPost);
String strResult = EntityUtils.toString(httpResponse.getEntity());
log.info("response = {}", strResult);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 讀取springboot配置信息
* @return
*/
public ConfigurableEnvironment load() {
ConfigurableEnvironment environment = new StandardEnvironment();
super.addPropertySources(environment, new DefaultResourceLoader());
return environment;
}
}