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

免责声明:文章转载自《Java多次启动相同jar程序》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【翻译】tus----一个可续传文件上传的开放协议IIS虚拟目录与UNC路径权限初探下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

java通过jdbc连接数据库并更新数据(包括java.util.Date类型数据的更新)

一、步骤 1.获取Date实例,并通过getTime()方法获得毫秒数; 2.将获取的毫秒数存储到数据库中,注意存储类型为nvarchar(20); 3.读取数据库的毫秒数,作为Date构造方法的参数创建实例,有需要再转换时间格式。 二、代码示例 packagecom.yh.dao; importjava.sql.Connection; importja...

Java 设计模式六原则及23中常用设计模式

一、设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访...

SpringBoot入门篇

一    什么是springboot Spring官网:http://spring.io/projects SpringBoot是Spring项目中的一个子工程,与我们所熟知的Spring-framework 同属于spring的产品: Springboot不是什么真正意义上的新框架,就像maven整合了所有的jar包,spring boot整合了所有常...

Unity3D 批量图片资源导入设置

原地址:http://blog.csdn.net/asd237241291/article/details/8433548 创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 QQ群:【119706192】 本文链接地址: Unity3D 批量图片资源导入设置 复制代码 usingUnityEngine; usingSystem.Colle...

Freemarker 基本数据类型

一 数据类型简介 freemarker 模板中的数据类型由如下几种: 1. 布尔型:等价于java中的boolean类型, 不同的是不能直接输出,可以转换成字符串再输出 2. 日期型:等价于java中的Date类型, 不同之处在于不能直接输出,需要转换成字符串再输出 3. 数值型:等价于java 中的int, float, double 等数值类型,有三...

Underscore.string Version (2.0.0) 中文文档

Underscore.string Version (2.0.0) 中文文档 Underscore.string.js Version (2.0.0) 中文文档 Javascript缺乏完整的字符串操作。Underscore.string.js试图填补这一空白。您可以在深入 JavaScript 中找到生成中方法列表 正如名称指出的Underscore....