Java从入门到实践

Java—用ProcessBuilder执行Shell任务

2020-03-07  本文已影响0人  Sunflow007
16.jpg

知乎编辑器效果有限,原文发布在语雀文档上,看上去效果更好~

Java—用ProcessBuilder执行Shell任务 · 语雀​www.yuque.com


概述

在Java中执行Shell任务可以用两种方式:1.java.lang下的Runtime 2.java.lang下的ProcessBuilder

但是,通过源码可以发现,二者最终都是通过ProcessBuilder类来执行操作的。为了在Java中执行大数据中的shell任务,添加任务失败重试次数、成功/失败消费者,我们可以设计如下几个类:

ShellMonitor

主要是ShellTask的监控和管理,定义了失败重试方法、任务失败/成功后的消费者、以及Task实体对象

ShellTask

执行任务的主体,继承了抽象类Task

Task

抽象类,定义了所有任务的公共属性:任务重试次数:retryNum、cmdTextConsumer(cmd输出消费者)、任务失败后的回调(消费者)failedCallback、任务成功后的回调(successCallback)

StreamGobbler

日志流接收类,用于处理ProcessBuilder执行时的输入/输出/错误流

ProcessBuilder

Java.lang包下的,用于在Java中执行shell的类,文章后有详细介绍。


任务执行示例

image

启动一个Shell任务很简单,就2句:

ShellTask shellTask = new ShellTask(confList.toArray(new String[confList.size()]));

new ShellMonitor<String>(shellTask, logger::info, null, null).run();

1.将List<String>类型的commands转化为String[],并new一个ShellTask任务
2.new一个ShellMonitor将该ShellTask放进去,配置任务成功/失败消费者为空,然后执行run()


相关类说明

1.ShellMonitor

image

主要属性

task

即任务执行的主体

failedTimes

属性记录了Task失败次数

failureConsumer successConsumer

则分别对应失败/成功后的消费者(消费者还是个Task任务)

主要方法

run()

主要的方法run(),调用task的run()

redo()

失败重试方法,在失败次数 < Task中定义的失败重试次数时,调用redo()方法


2.ShellTask

ShellTask即任务执行的主体类,继承了抽象类Task,并重新了run()和redo()方法

image

主要属性

commands

String[],装的是执行的参数集合

path

执行shell时需要用到的文件

构造函数

三种构造器:
无参、带String[]参数、带File和String[]构造器

主要方法

run()

run方法即执行的主方法,在其中调用runCommand()具体执行

redo()

失败重试方法,在其中调用runCommand()具体执行

runCommand()

执行的主方法,具体如下:

public void runCommand() {
        try {
            logger.info("start");
            long start = System.nanoTime();
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commands);
            builder.directory(path == null ? FileUtils.getUserDirectory() : path);
            //启动工作线程
            Process process = builder.start();
            //启动日志流记录线程
            StreamGobbler streamGobbler =
                    new StreamGobbler(process.getInputStream(), process.getErrorStream(), super.cmdTextConsumer);
            new Thread(streamGobbler).start();
            int exitCode = process.waitFor();
            long end = System.nanoTime();
            if (exitCode == 0) {
                if (successCallback != null)
                    super.successCallback.accept(this);
            } else {
                if (failedCallback != null)
                    super.failedCallback.accept(this);
            }
            logger.info("ec=" + exitCode);
            logger.info("end");
            logger.info("duration:" + ((double) (end - start) / 10e8));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

程序逻辑很简单,启动ProcessBuilder工作线程、启动StreamGobbler记录线程。通过exitCode获取工作线程的执行状态码,0表示运行正常结束,如果正常结束则调用任务成功消费者、否则调用失败消费者(如果定义了消费者)


3.Task

Task是一个抽象类实现了Runnable接口,提供了所有任务的抽象方法和通用属性字段

image

主要属性

retryNum

任务失败重试次数,默认为3次

failedCallback

任务失败时的消费者

successCallback

任务成功时的消费者

构造函数

三种构造器:
无参、带String[]参数、带File和String[]构造器

主要方法

run()

重新runnable接口中的run()

redo()

失败重试方法


4.StreamGobbler

实现了Runnable接口的一个线程类,用于任务执行过程中的输出流、错误流

image

主要run方法如下:

public void run() {
        Runnable inputRunnable = () -> new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(consumer);
        Runnable errorRunnable = () -> new BufferedReader(new InputStreamReader(errorStream)).lines().forEach(consumer);
        new Thread(inputRunnable).start();
        new Thread(errorRunnable).start();
    }

此类分别启动两个线程,用于直接打印任务运行时的正常输出和错误输出


5.ProcessBuilder

ProcessBuilder类是Java1.5在java.lang中新添加的一个新类,此类用于创建操作系统进程,它提供一种启动和管理进程(也就是应用程序)的方法。在1.5之前,都是由Process类处理实现进程的控制管理。每个 ProcessBuilder 实例管理一个进程属性集。它的start() 方法利用这些属性创建一个新的 Process 实例。start() 方法可以从同一实例重复调用,以利用相同的或相关的属性创建新的子进程。

具体可以参考:https://blog.csdn.net/u013256816/article/details/54603910

主要属性

点进去ProcessBuilder的源码看看:

image

可见有五个属性,其中command是shell命令,directory是shell中会用到的文件,environment环境配置,还有个布尔值,是否重定向错误流,以及redirects[]重定向数组。一般情况下,只需要用到command。

构造函数

查看类的构造器,可以看到有两种:

image

这两个构造器中command字段,分别接受List<String>和String[]类型的参数,最终都会将command转化为List<String>类型

主要方法

略...


好久没写文章了,一是工作忙了,最主要是人变懒了,不过我又回来了,后面的文章还是会不断出来~重点是Java基本经典的书、读书笔记类型的,过一遍然后Java就暂时放一边(项目中比较常用的,经典的代码还是会记录下)。

后面就是AI深度学习、图像识别的东西~

上一篇 下一篇

猜你喜欢

热点阅读