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;
    }
}
相关推荐
逻各斯4 分钟前
redis中的Lua脚本,redis的事务机制
java·redis·lua
计算机毕设指导66 分钟前
基于Springboot学生宿舍水电信息管理系统【附源码】
java·spring boot·后端·mysql·spring·tomcat·maven
计算机-秋大田13 分钟前
基于Spring Boot的兴顺物流管理系统设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·spring·课程设计
计算机小白一个25 分钟前
蓝桥杯 Java B 组之背包问题、最长递增子序列(LIS)
java·数据结构·蓝桥杯
二十雨辰1 小时前
[Java基础]网络编程
java·开发语言
ACGkaka_1 小时前
抓包工具(三)Wireshark代理抓包Java程序的HTTPS请求
java·https·wireshark
Struggle Sheep1 小时前
容器化部署tomcat
java·tomcat
M1A11 小时前
Python语言发展史:从创立到成为全球热门
java
爱吃南瓜的北瓜2 小时前
Maven之jjwt依赖爆红
java·pycharm·maven
土豆儿@2 小时前
java之泛型
java·开发语言