SpringBoot单体服务无感更新启动,动态检测端口号并动态更新

SpringBoot单体服务无感更新启动

java 复制代码
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;
    }
}
相关推荐
The_SkyUzi几秒前
【无标题】
java·maven
han_hanker18 分钟前
统一拦截异常 @RestControllerAdvice
java·开发语言·数据库
忘记92633 分钟前
什么是spring boot
java·spring boot·后端
Hello World呀36 分钟前
Java实现手机号和身份证号脱敏工具类
java·开发语言
曹牧38 分钟前
Java:serialVersionUID
java·开发语言
毕设源码-钟学长1 小时前
【开题答辩全过程】以 公寓出租系统为例,包含答辩的问题和答案
java·eclipse·echarts
Hello World呀1 小时前
Minio的替代品RustFS
java
爱笑的眼睛111 小时前
强化学习组件:超越Hello World的架构级思考与实践
java·人工智能·python·ai
悟能不能悟1 小时前
java 设置日期返回格式的几种方式
java·开发语言
爱笑的眼睛111 小时前
深入解析PyTorch nn模块:超越基础模型构建的高级技巧与实践
java·人工智能·python·ai