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;
    }
}
相关推荐
ONLYOFFICE4 分钟前
【技术教程】如何将文档编辑器集成至基于Java的Web应用程序
java·编辑器·onlyoffice
lbwxxc20 分钟前
手写 Tomcat
java·tomcat
CHEN5_0223 分钟前
【CouponHub项目开发】使用RocketMQ5.x实现延时修改优惠券状态,并通过使用模板方法模式重构消息队列发送功能
java·重构·模板方法模式·项目
杨杨杨大侠24 分钟前
实战案例:商品详情页数据聚合服务的技术实现
java·spring·github
杨杨杨大侠25 分钟前
实战案例:保险理赔线上审核系统的技术实现
java·spring·github
计算机毕设定制辅导-无忧学长26 分钟前
MQTT 与 Java 框架集成:Spring Boot 实战(一)
java·网络·spring boot
叫我阿柒啊28 分钟前
从Java全栈到Vue3实战:一次真实面试的深度复盘
java·spring boot·微服务·vue3·响应式编程·前后端分离·restful api
快乐非自愿31 分钟前
掌握设计模式--模板方法模式
java·设计模式·模板方法模式
云飞云共享云桌面39 分钟前
SolidWorks对电脑的硬件配置要求具体有哪些
java·服务器·前端·网络·数据库
塔子终结者44 分钟前
网络安全A模块专项练习任务十解析
java·服务器·网络安全