zoukankan      html  css  js  c++  java
  • Java多次启动相同jar程序

    背景

    现在很多软件都支持集群部署,但是测试环境通常资源有限,所以一般通过单台机器模拟集群部署(使用不同端口,运行相同jar包),本文的目的就是通过多种方式实现此需求。

    两个程序

    1、jar程序

      ① springboot程序

      ② 只包含一个main方法,用于启动程序,输出进程ID

      ③ 路径:C:/demo.jarwindows/demo.jarLinux

    2、启动程序

      ① 包含main方法的程序

    多种方式

    1、通过URLClassLoader加载jar程序(windows平台)

    2、通过java -jar命令启动jar程序(windows平台)

    3、通过复制原始jar文件,启动不同的jar程序(windows平台)

    4、通过Linux Shell脚本启动(Linux平台)

    方式一

    1、通过URLClassLoader加载jar程序(windows平台)

      ① 说明

        1) 启动程序多次加载jar程序

        2) jar程序和启动程序使用相同进程,非独立进程,无实际意义,仅介绍

      ② 启动jar程序:运行启动程序main方法

      ③ 终止jar程序:停止启动程序(因为共用同一个进程,终止主程序,jar程序会同时终止)

    2、代码

      ① jar程序

    @SpringBootApplication
    
    public class DemoStarter {
    
        public static void main(String[] args) {
    
            // 获取进程Id
    
            String name = ManagementFactory.getRuntimeMXBean().getName();
    
            String processId = name.split("@")[0];
    
            System.out.println(processId);
    
    
            SpringApplication.run(DemoStarter.class, args);
        }
    }

      ② 启动程序

    import java.lang.reflect.Method;
    import java.net.URL;
    import java.net.URLClassLoader;
    
    public class Starter1 {
    
        public static void main(String[] args) throws Exception {
    
            start("7001");
            start("7002");
            start("7003");
        }
        
        private static void start(String port) throws Exception {
    
            String path = "file:" + "C:/demo.jar";
    
            URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL(path)});
    
            // jar程序的启动类完整路径
            Class demo = classLoader.loadClass("DemoStarter");
    
            Method method = demo.getMethod("main", String[].class);
    
            method.invoke(null, (Object) new String[]{port});
        }
    }

    方式二

    1、通过java -jar命令启动jar包(windows平台)

      ① 说明

        1) 启动程序使用命令多次启动jar

        2) 动态构建cmd命令(不同参数),启动相同jar程序,各个jar程序使用不同进程

      ② 启动jar程序

        1) 运行启动程序main方法

        2) 保存各个进程ID到文件

      ③ 终止jar程序

        1) 根据保存的进程ID停止各个jar程序

    2、代码

      ① jar程序(同方式一)

      ② 启动程序

    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    
    public class Starter2 {
    
        public static void main(String[] args) throws Exception {
    
            cmd("7001");
            cmd("7002");
            cmd("7003");
    
            // 根据文件中的进程Id终止程序
            killByProcessId("PID1");
            killByProcessId("PID2");
            killByProcessId("PID3");
        }
    
        private static void cmd(String port) throws Exception {
    
            String cmd = "java -jar -Dserver.port=" + port + " " + "C:/demo.jar";
    
            Process p = Runtime.getRuntime().exec(cmd);
            InputStream is = p.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    
            // 获取进程Id(DemoStarter-->main方法
            // reader.readLine()第一行为System.out.println(processId)输出内容
            String processId;
            while ((processId = reader.readLine()) != null) {
                break;
            }
            is.close();
            reader.close();
    
            // 这里可以将进程ID保存到文件中
            System.out.println("processId:" + processId);
        }
    
        private static void killByProcessId(String processId) throws Exception {
    
            String cmd = "taskkill /F /PID "" + processId + """;
            Runtime.getRuntime().exec(cmd);
        }
    }
    
     

    方式三

    1、通过复制原始jar文件,启动不同的jar程序(windows平台)

      ① 说明

        1) 复制原始jar包,生成新的jar

        2) 动态构建cmd命令(不同参数、不同jar包名称),启动不同jar包,各个jar包使用不同进程

      ② 启动jar程序

        1) 运行启动程序的main方法

        2) 保存各个进程ID到文件

      ③ 终止程序

        1) 根据保存的进程ID停止各个jar程序

      ④ 复制jar文件需要时间,但可以解决启动相同jar包可能存在的问题

    2、代码

      ③ jar程序(同方式一)

      ④ 启动程序

    import org.apache.commons.io.FileUtils;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    
    public class Starter3 {
    
        public static void main(String[] args) throws Exception {
    
            copyCmd();
    
            // 根据文件中的进程Id终止程序
            killByProcessId("PID1");
            killByProcessId("PID2");
            killByProcessId("PID3");
        }
    
        private static void copyCmd() throws Exception {
    
            File srcFile = new File("C:/demo.jar");
            File destiFile2 = new File("C:/demo2.jar");
            File destiFile3 = new File("C:/demo3.jar");
    
            // 删除之前复制的jar包
            FileUtils.forceDelete(destiFile2);
            FileUtils.forceDelete(destiFile3);
    
            FileUtils.copyFile(srcFile, destiFile2);
            FileUtils.copyFile(srcFile, destiFile3);
    
            copy("java -jar -Dserver.port=7001 C:/demo.jar");
            copy("java -jar -Dserver.port=7002 C:/demo2.jar");
            copy("java -jar -Dserver.port=7003 C:/demo3.jar");
        }
    
        private static void copy(String cmd) throws Exception {
    
            Process p;
    
            p = Runtime.getRuntime().exec(cmd);
            InputStream is = p.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    
            // 获取进程Id(DemoStarter-->main方法
            // reader.readLine()第一行为System.out.println(processId)输出内容
            String processId;
            while ((processId = reader.readLine()) != null) {
                break;
            }
            is.close();
            reader.close();
    
            // 这里可以将进程ID保存到文件中
            System.out.println("ProcessId:" + processId);
        }
    
        private static void killByProcessId(String processId) throws Exception {
    
            String cmd = "taskkill /F /PID "" + processId + """;
            Runtime.getRuntime().exec(cmd);
        }
    }

    方式四

    1、通过Linux Shell脚本启动(Linux平台)

      ① 执行java -jar命令

      ② 根据端口获取进程ID

      ③ 根据进程ID终止程序

    2、代码

      ① jar程序(同方式一)

      ② Shell命令

        1) 启动程序

    #!/bin/bash
    
    java -jar -Dserver.port=7001 /demo.jar
    java -jar -Dserver.port=7002 /demo.jar
    java -jar -Dserver.port=7003 /demo.jar

      2) 终止程序

    #!/bin/bash
    
    pid1=`netstat -anp | grep 7001 | awk '{printf $7}' | cut -d/ -f1`
    pid2=`netstat -anp | grep 7002 | awk '{printf $7}' | cut -d/ -f1`
    pid3=`netstat -anp | grep 7003 | awk '{printf $7}' | cut -d/ -f1`
    
    kill ${pid1}
    kill ${pid2}
    kill ${pid3}

    问题&总结

      1、方式一可以通过调用method.invoke传递参数

      2、其它方式可通过jar命令传递参数

      3、启动程序通过Process启动jar程序并获取jar程序进程ID

      4、多次启动jar程序时报错:”unable to register MBean” 
          设置参数spring.jmx.enabled=false

      5、根据端口号获取进程IDwindows
        netstat -ano|findstr "7001 7002 7003"

      6、根据进程ID停止进程(windows

        taskkill /F /PID "1"

    参考资料

      1、https://my.oschina.net/u/2971292/blog/2960777

      2、https://www.jianshu.com/p/3eea5e7e1e6f

      3、https://www.cnblogs.com/sxdcgaq8080/p/10579073.html

  • 相关阅读:
    C# UDP实现通信的方法
    Leetcode 559. N叉树的最大深度
    101. 对称二叉树
    108. 将有序数组转换为二叉搜索树
    剑指 Offer 55
    Linux
    Linux
    Linux
    Linux
    Linux
  • 原文地址:https://www.cnblogs.com/gossip/p/11680024.html
Copyright © 2011-2022 走看看