SpringBoot单体服务无感更新启动
package com.basaltic.warn;
import cn.hutool.core.io.IoUtil;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import springfox.documentation.oas.annotations.EnableOpenApi;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@MapperScan("com.basaltic.warn.sys.mapper")
@SpringBootApplication
@EnableTransactionManagement
@EnableOpenApi
@EnableScheduling
@EnableAsync
public class BasalticOneNewApplication {
@SneakyThrows
public static void main(String[] args) {
AtomicInteger port = new AtomicInteger(7089);
SpringApplication app = new SpringApplication(BasalticOneNewApplication.class);
try {
app.addInitializers((context) -> {
String portStr = context.getEnvironment().getProperty("server.port");
System.out.println("The port is: " + portStr);
if (StringUtils.isNotBlank(portStr) && StringUtils.isNumeric(portStr)) {
port.set(Integer.parseInt(portStr));
}
});
app.run(args);
} catch (Exception e) {
String[] newArgs = args.clone();
boolean needChangePort = false;
if (isPortInUse(port.get())) {
newArgs = new String[args.length + 1];
System.arraycopy(args, 0, newArgs, 0, args.length);
newArgs[newArgs.length - 1] = "--server.port=7069";
needChangePort = true;
}
ConfigurableApplicationContext run = SpringApplication.run(BasalticOneNewApplication.class, newArgs);
if (needChangePort) {
String osName = System.getProperty("os.name");
while (isPortInUse(port.get())) {
if (osName.startsWith("Windows")) {
killWinTidByPort(port.get());
} else {
killLinuxTidByPort(port.get());
}
System.out.println("已经占用");
TimeUnit.SECONDS.sleep(2);
}
String[] beanNames = run.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
ServletWebServerFactory webServerFactory = run.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
((TomcatServletWebServerFactory) webServerFactory).setPort(port.get());
Method method = ServletWebServerApplicationContext.class.getDeclaredMethod("getSelfInitializer");
method.setAccessible(true);
ServletContextInitializer invoke = (ServletContextInitializer) method.invoke(run);
webServerFactory.getWebServer(invoke).start();
((ServletWebServerApplicationContext) run).getWebServer().stop();
}
}
}
private static boolean isPortInUse(int port) {
try (ServerSocket serverSocket = new ServerSocket(port)) {
return false;
} catch (Exception e) {
return true;
}
}
@SneakyThrows
public static void killLinuxTidByPort(int port) {
String command = String.format("lsof -i :%d | grep LISTEN | awk '{print $2}' | xargs kill -9", port);
Runtime.getRuntime().exec(new String[]{"sh", "-c", command}).waitFor();
}
@SneakyThrows
public static void killWinTidByPort(int port) {
Process process = new ProcessBuilder("cmd.exe", "/c", "taskkill /F /PID " + getWinTidByPort(port)).start();
process.waitFor(3, TimeUnit.SECONDS);
}
@SneakyThrows
public static int getWinTidByPort(int port) {
Process process = new ProcessBuilder("cmd.exe", "/c", "netstat -ano | findstr :" + port).start();
process.waitFor(3, TimeUnit.SECONDS);
ArrayList<String> lines = IoUtil.readUtf8Lines(process.getInputStream(), new ArrayList<>());
for (String line : lines) {
if (StringUtils.isBlank(line)) {
continue;
}
String tidStr = line.substring(line.trim().lastIndexOf(" ")).trim();
if (StringUtils.isNotBlank(tidStr) && StringUtils.isNumeric(tidStr)) {
int tid = Integer.parseInt(tidStr);
if (tid != 0) {
return tid;
}
}
}
return 0;
}
}