性能文章>java的cmd命令行和jsch执行压测>

java的cmd命令行和jsch执行压测原创

316056

前言

项目中用到了cmd命令去执行,但是发现一个问题就是,当需要切换用户和执行命令的时候特别的麻烦,所以后面又该用了jsch的连接方式,测试一下性能理想不理想,看看有劣势。

相关配置

ssh的连接数

# cat /etc/ssh/sshd_config | grep MaxSessions

#MaxSessions 1000

测试代码

@GetMapping("/cmd")
public String testCmd(@RequestParam Integer count) {
    log.info(" id = {}", count);
    ExecutorService executorService = Executors.newFixedThreadPool(count);
    long start = System.nanoTime();
    CountDownLatch latch = new CountDownLatch(count);
    for (int i = 0; i < count; i++) {
        executorService.execute(()->{
            try {
                String command = "sleep 3 && echo 'Done waiting!' "; // 替换成您要运行的CMD命令
                //                    Process process = Runtime.getRuntime().exec(command);
                // 创建ProcessBuilder对象,设置要执行的命令
                ProcessBuilder pb = new ProcessBuilder("sleep", "10");

                // 启动进程
                Process process = pb.start();
                InputStream inputStream = process.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                String line;
                while ((line = reader.readLine()) != null) {
                    log.info("data:{}", line);
                }
                reader.close();
                latch.countDown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    try {
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    long end = System.nanoTime();
    long offset = TimeUnit.NANOSECONDS.toMillis(end - start);
    log.info("cost time = {} ms", offset);
    return "hello: " + offset;
}

@GetMapping("/ssh")
public String testSsh(@RequestParam Integer count) {
    log.info(" id = {}", count);
    ExecutorService executorService = Executors.newFixedThreadPool(count);
    long start = System.nanoTime();
    CountDownLatch latch = new CountDownLatch(count);
    for (int i = 0; i < count; i++) {
        executorService.execute(()->{
            try {
                cmdCreateFileDirectory(" sleep 10 && echo 'Done waiting!'", "steven",
                                       "admin");
                latch.countDown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    try {
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    long end = System.nanoTime();
    long offset = TimeUnit.NANOSECONDS.toMillis(end - start);
    log.info("cost time = {} ms", offset);
    return "hello: " + offset;
}


// write a create file directory function
public void cmdCreateFileDirectory(final String command, final String serverUser,
                                   final String serverPassword) {
    int exitValue = 0;
    long startTime = System.currentTimeMillis();
    final StringBuilder result = new StringBuilder();
    try {
        JSch jsch = new JSch();
        Session session = jsch.getSession(serverUser, "127.0.0.1", 22);
        session.setPassword(serverPassword);
        session.setConfig("StrictHostKeyChecking", "no");
        session.connect(10000);

        Channel channel = session.openChannel("exec");
        ((ChannelExec) channel).setCommand(command);
        channel.setInputStream(null);
        ((ChannelExec) channel).setErrStream(System.err);

        InputStream in = channel.getInputStream();
        channel.connect();
        byte[] tmp = new byte[1024];
        while (true) {
        while (in.available() > 0) {
        int i = in.read(tmp, 0, 1024);
        if (i < 0) break;
        result.append(new String(tmp, 0, i));
        }
        if (channel.isClosed()) {
        if (in.available() > 0) continue;
        exitValue = channel.getExitStatus();
        log.debug("exit-status: {}", exitValue);
        break;
        }
        try {
        Thread.sleep(10);
        } catch (Exception ee) {
        }
        }
        channel.disconnect();
        session.disconnect();
        if (exitValue != 0) {
        log.error("create file directory error: " + exitValue + " " + command);
        }
        log.info(" create file directory success, command: {}, content:{} total cost time:{}", command,
        result.toString(), System.currentTimeMillis() - startTime);
        } catch (JSchException e) {
        log.error(e.getMessage(), e);
        } catch (IOException e) {
        log.error(e.getMessage(), e);
        }
        }

测试cmd

CPU还算稳定,刚开始120%后面50%,然后30%,然后50%这样波动

程序日志

2023-07-12 17:12:18.643 INFO 6466 --- [nio-8082-exec-1] com.example.demotest.api.TestController : id = 1000

2023-07-12 17:12:32.385 INFO 6466 --- [nio-8082-exec-1] com.example.demotest.api.TestController : cost time = 13739 ms

2023-07-12 17:13:04.250 INFO 6466 --- [nio-8082-exec-2] com.example.demotest.api.TestController : id = 1000

2023-07-12 17:13:17.461 INFO 6466 --- [nio-8082-exec-2] com.example.demotest.api.TestController : cost time = 13210 ms

2023-07-12 17:13:42.428 INFO 6466 --- [nio-8082-exec-3] com.example.demotest.api.TestController : id = 1000

2023-07-12 17:13:55.645 INFO 6466 --- [nio-8082-exec-3] com.example.demotest.api.TestController : cost time = 13216 ms

2023-07-12 17:14:04.956 INFO 6466 --- [nio-8082-exec-4] com.example.demotest.api.TestController : id = 1000

2023-07-12 17:14:17.633 INFO 6466 --- [nio-8082-exec-4] com.example.demotest.api.TestController : cost time = 12676 ms

平均执行时间在13s左右

测试ssh

cpu在240~100之间徘徊

2023-07-12 17:18:10.288 INFO 6466 --- [nio-8082-exec-5] com.example.demotest.api.TestController : cost time = 30565 ms

2023-07-12 17:19:59.174 INFO 6466 --- [nio-8082-exec-9] com.example.demotest.api.TestController : cost time = 35478 ms

介于ssh连接的原因,还是会有很多连接不上ssh,当IO操作不频繁有一部分可能会导致CPU变高,因为线程数很多。

com.jcraft.jsch.JSchException: Session.connect: java.net.SocketException: Connection reset

at com.jcraft.jsch.Session.connect(Session.java:565) ~[jsch-0.1.55.jar!/:na]

at com.example.demotest.api.TestController.cmdCreateFileDirectory(TestController.java:113) ~[classes!/:0.0.1-SNAPSHOT]

at com.example.demotest.api.TestController.lambda$testSsh$1(TestController.java:82) ~[classes!/:0.0.1-SNAPSHOT]

at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]

at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]

at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

2023-07-12 18:08:47.839 ERROR 6466 --- [l-50-thread-136] com.example.demotest.api.TestController : Session.connect: java.net.SocketException: Connection reset

com.jcraft.jsch.JSchException: Session.connect: java.net.SocketException: Connection reset

at com.jcraft.jsch.Session.connect(Session.java:565) ~[jsch-0.1.55.jar!/:na]

at com.example.demotest.api.TestController.cmdCreateFileDirectory(TestController.java:113) ~[classes!/:0.0.1-SNAPSHOT]

at com.example.demotest.api.TestController.lambda$testSsh$1(TestController.java:82) ~[classes!/:0.0.1-SNAPSHOT]

100条以内执行统计

# cat /etc/ssh/sshd_config | grep MaxSessions

#MaxSessions 10

改成请求100

cpu

ssh30~4%. cmd无影响

执行耗时单位ms

[root@dev-base-01 ~]# curl http://localhost:8082/api/test/ssh?count=10

hello: 10430

[root@dev-base-01 ~]# curl http://localhost:8082/api/test/ssh?count=10

hello: 10356

[root@dev-base-01 ~]# curl http://localhost:8082/api/test/ssh?count=10

hello: 10502

[root@dev-base-01 ~]# curl http://localhost:8082/api/test/cmd?count=10

hello: 10017

[root@dev-base-01 ~]# curl http://localhost:8082/api/test/cmd?count=10

hello: 10023

[root@dev-base-01 ~]# curl http://localhost:8082/api/test/cmd?count=10

hello: 10018

改成500

cpu消耗

cmd: 50~10% ssh: 200~30%

耗时消耗ms

[root@dev-base-01 ~]# curl http://localhost:8082/api/test/cmd?count=1000

hello: 14946[root@dev-base-01 ~]# curl http://localhost:8082/api/test/cmd?count=500

hello: 12318[root@dev-base-01 ~]# curl http://localhost:8082/api/test/cmd?count=500

hello: 11037[root@dev-curl http://localhost:8082/api/test/ssh?count=500

^[[Ahello: 20826[root@dev-base-01 ~]# curl http://localhost:8082/api/test/ssh?count=500

hello: 22675[root@dev-base-01 ~]# curl http://localhost:8082/api/test/ssh?count=500

hello: 22629[root@dev-base-01 ~]#

总结

从测试结果来看cmd的执行耗时cpu明显要低很多而且耗时也低很多,但是明明cmd创建了一个进程去执行命令,所以但从表面数据来看是好像明显cmd更加的明显,但是从代码维护和后续更多功能开发又觉得cmd有点像一个附加功能,不是主要的做命令执行的,SSH之类的脚本还是需要连接工具去执行,也搜索到类似的文章,也看过类似的书籍,都讨论JAVA命令行的缺点,之前一个是内存溢出,后面一个案例是重构很难适应新业务。

一般来说,使用第三方库连接SSH执行脚本的性能更好。因为第三方库连接SSH可以直接在Java代码中执行远程命令,而不需要像底层调用exec一样需要创建进程、执行命令、等待命令执行完成等操作,从而可以更加高效地执行远程命令。但具体的性能情况还需要根据具体的实现方式和场景来进行评估。

引用

SSH Connection With Java | Baeldung

JSch- Java Secure Channel - Examples

JavaJSchExample to run Shell Commands on SSH Unix Server | DigitalOcean

点赞收藏
分类:标签:
查拉图斯特拉说

让世界因你而不同

请先登录,查看5条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步
6
5