Springboot Tomcat 单体项目如何实现快速jar包升级

同一个项目jar 启动由于项目端口一致导致启动失败,可以通过以下方式启动

大概思路:
1、 判断端口是否占用
2、占用则先通过其他端口启动
3、等待启动完毕后终止老进程
4、重新创建容器实例并且关联DispatcherServlet
在第三步和第四步之间,速度很快的,这样就达到了无缝更新代码的目的。

java 复制代码
@SpringBootApplication()
@EnableScheduling
public class WebMainApplication {
    public static void main(String[] args) {
        String[] newArgs = args.clone();
        int defaultPort = 8088;
        boolean needChangePort = false;
        if (isPortInUse(defaultPort)) {
            newArgs = new String[args.length + 1];
            System.arraycopy(args, 0, newArgs, 0, args.length);
            newArgs[newArgs.length - 1] = "--server.port=9090";
            needChangePort = true;
        }
        ConfigurableApplicationContext run = SpringApplication.run(WebMainApplication.class, newArgs);
        if (needChangePort) {
            String command = String.format("lsof -i :%d | grep LISTEN | awk '{print $2}' | xargs kill -9", defaultPort);
            try {
                Runtime.getRuntime().exec(new String[]{"sh", "-c", command}).waitFor();
                while (isPortInUse(defaultPort)) {
                }
                ServletWebServerFactory webServerFactory = getWebServerFactory(run);
                ((TomcatServletWebServerFactory) webServerFactory).setPort(defaultPort);
                WebServer webServer = webServerFactory.getWebServer(invokeSelfInitialize(((ServletWebServerApplicationContext) run)));
                webServer.start();

                ((ServletWebServerApplicationContext) run).getWebServer().stop();
            } catch (IOException | InterruptedException ignored) {
            }
        }

    }

    private static ServletContextInitializer invokeSelfInitialize(ServletWebServerApplicationContext context) {
        try {
            Method method = ServletWebServerApplicationContext.class.getDeclaredMethod("getSelfInitializer");
            method.setAccessible(true);
            return (ServletContextInitializer) method.invoke(context);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

    }

    private static boolean isPortInUse(int port) {
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            return false;
        } catch (IOException e) {
            return true;
        }
    }

    protected static Collection<ServletContextInitializer> getServletContextInitializerBeans(ConfigurableApplicationContext context) {
        return new ServletContextInitializerBeans(context.getBeanFactory());
    }


    private static ServletWebServerFactory getWebServerFactory(ConfigurableApplicationContext context) {
        String[] beanNames = context.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);

        return context.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    }

}

具体逻辑想了解的可以访问文章原文链接 https://mp.weixin.qq.com/s/jaKN1YpvDycX_P13NAQNPw

相关推荐
扣丁梦想家2 小时前
RocketMQ 详细教程(Spring Boot & Spring Cloud Alibaba)
spring boot·spring cloud·微服务·rocketmq
兮动人4 小时前
SpringBoot加载配置文件的优先级
java·spring boot·后端·springboot加载配置
m0_748254664 小时前
Spring Boot 热部署
java·spring boot·后端
佳佳_6 小时前
Spring Boot SSE 示例
spring boot·后端
臣妾写不来啊6 小时前
使用dify的api连接外部知识库,dify连接ragflow的知识库(附java代码)
java·开发语言·spring boot
李长渊哦7 小时前
Spring Boot 接口延迟响应的实现与应用场景
spring boot·后端·php
Cent'Anni8 小时前
【RabbitMQ】事务
java·spring boot·rabbitmq
小杨4048 小时前
springboot框架项目应用实践五(websocket实践)
spring boot·后端·websocket
浪九天8 小时前
Java直通车系列28【Spring Boot】(数据访问Spring Data JPA)
java·开发语言·spring boot·后端·spring
一只爱打拳的程序猿8 小时前
【SpringBoot】实现登录功能
javascript·css·spring boot·mybatis·html5