java.lang.Process执行本机命令总结

2023-01-06 工作总结,java执行本机命令获取控制台输出,包括有限输出和无限输出(ctr+c打断)

运行命令工具类:

import ProcessDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.*;

@Slf4j
public class RunCmdUtil {

    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(50, 400, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(5));

    public static ProcessDTO runCmd(String cmd, int waitSeconds) {
        log.info("cmd: {}", cmd);
        ProcessDTO dto = new ProcessDTO();

        Process process = null;
        try {
            process = Runtime.getRuntime().exec(cmd);

            dto.setExeResult(readInputTimeout(process.getInputStream(), 5));
            dto.setError(readInputTimeout(process.getErrorStream(), 2));

            process.waitFor(waitSeconds, TimeUnit.SECONDS);
            Integer exitValue = null;
            try {
                exitValue = process.exitValue();
            } catch (IllegalThreadStateException e) {
                dto.setError("process has not exited");
            }
            dto.setExitValue(exitValue);
        } catch (Exception e) {
            dto.setError(ExceptionUtils.getFullStackTrace(e));
        } finally {
            if (process != null) {
                process.destroy();
            }
        }

        log.info("exitValue: {}", dto.getExitValue());
        String exeResult = dto.getExeResult();
        log.info("exeResult: {}", exeResult.length() > 2000 ? "length=" + exeResult.length() : exeResult);
        String error = dto.getError();
        if (StringUtils.isNotBlank(error)) {
            log.info("error: {}", error.length() > 2000 ? "length=" + error.length() : error);
        }
        return dto;
    }

    public static String readInputTimeout(InputStream inputStream, int timeout) {
        FutureTask<String> futureTask = new FutureTask<>(() -> readInput(inputStream));
        executor.execute(futureTask);
        try {
            return futureTask.get(timeout, TimeUnit.SECONDS);
        } catch (Exception e) {
            return "futureTask.get timeout";
        }
    }

    public static String readInput(InputStream inputStream) {
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder builder = new StringBuilder();
        String line;
        int lineNumber = 0;
        try {
            // 无限输出的只读1000条
            while ((line = br.readLine()) != null && lineNumber < 1000) {
                builder.append(line);
                builder.append("\n");
                lineNumber++;
            }
            return builder.toString();
        } catch (Exception e) {
            return ExceptionUtils.getFullStackTrace(e);
        }
    }

}

无限输出,计算每条打印记录的平均大小(场景:拉取kafka topic的即时数据)

// 计算平均数据大小,单位:B
public BigDecimal tryAvgFlowSize(String topic) {
    BigDecimal avgSize = BigDecimal.ZERO;

    // 拉取topic数据
    String cmd = "kafka-console-consumer.sh" +
            " --bootstrap-server {}" +
            " --topic {}" +
            " --consumer.config local-ssl.properties";
    ProcessDTO dto = RunCmdUtil.runCmd(cmd, shortWaitSeconds);

    // 读取数据条数
    int lineNumber = 0;
    String exeResult = dto.getExeResult();
    log.info("topicResult.length : {}", exeResult.length());
    if (StringUtils.isNotBlank(exeResult)) {
        LineNumberReader lineNumberReader = new LineNumberReader(new StringReader(exeResult));
        try {
            lineNumberReader.skip(Integer.MAX_VALUE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        lineNumber = lineNumberReader.getLineNumber();
    }

    if (lineNumber != 0) {
        avgSize = BigDecimal.valueOf(exeResult.length()).divide(BigDecimal.valueOf(lineNumber), 4, RoundingMode.HALF_UP);
        log.info("topicResult.lineNumber : {}", lineNumber);
        log.info("topicResult.avgSize : {}", avgSize);
    }

    return avgSize;
}

有限输出,解析控制台返回(场景:获取消费偏移量,并解析所需topic下的offset)

// 偏移量和数据大小、累计流量
public JSONObject countUserConsumption(String topic, String user, Double flowSize) {
    JSONObject jsonObject = new JSONObject();

    // 平均数据大小,单位:B
    BigDecimal avgSize = BigDecimal.valueOf(flowSize);

    String cmd = "kafka-consumer-groups.sh" +
            " --bootstrap-server {}" +
            " --describe --group {}" +
            " --command-config local-ssl.properties";
    ProcessDTO dto = RunCmdUtil.runCmd(cmd, shortWaitSeconds);
    int exitValue = dto.getExitValue();
    String error = dto.getError();
    if (exitValue != 0) {
        log.error("kafka flowCount error:{}", error);
        return null;
    }

    String exeResult = dto.getExeResult();
    // 解析偏移量返回
    BigDecimal totalOffset = this.totalOffsetFromResult(exeResult, topic);

    jsonObject.put("offset", totalOffset);
    jsonObject.put("avgSize", avgSize);
    // B -> GB
    jsonObject.put("totalFlow", avgSize.multiply(totalOffset).divide(BigDecimal.valueOf(1024 * 1024 * 1024), 2, RoundingMode.HALF_UP));

    log.info("jsonObject: {}", jsonObject.toJSONString());

    return jsonObject;
}

偏移量输出样例

GROUP           TOPIC           PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG   
zhangsan        fawaikuangt     0          2537186         21783878        19246692

解析方法

private BigDecimal totalOffsetFromResult(String exeResult, String topic) {
    BigDecimal totalOffset = BigDecimal.ZERO;

    // 解析偏移量返回
    boolean dataArea = false;
    String[] split = exeResult.split("\n");
    for (String str : split) {
        if (dataArea) {
            // 空格区
            int spaceArea = 0;
            boolean topicMatch = false;
            for (int i = 0; i < str.length(); i++) {
                char c = str.charAt(i);
                if (c == ' ' && str.charAt(i + 1) != ' ') {
                    spaceArea++;
                }
                // topic
                if (spaceArea == 1 && !topicMatch) {
                    int end = i;
                    while (str.charAt(++end) != ' ') {
                        if (end - 1 > 500) {
                            break;
                        }
                    }
                    if (!topic.equals(str.substring(i + 1, end).trim())) {
                        break;
                    } else {
                        topicMatch = true;
                    }
                }
                // current-offset
                if (spaceArea == 3) {
                    int end = i;
                    while (str.charAt(++end) != ' ') {
                        if (end - i > 50) {
                            break;
                        }
                    }
                    totalOffset = totalOffset.add(new BigDecimal(str.substring(i + 1, end).trim()));
                    break;
                }
            }
        } else if (str.startsWith("GROUP")) {
            dataArea = true;
        }
    }

    return totalOffset;
}

补充一下 local-ssl.properties 内容

security.protocol=SASL_SSL
ssl.truststore.location=/home/kafka.server.keystore.jks
ssl.truststore.password={}
sasl.mechanism=SCRAM-SHA-256
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="" password="";

首次应用,拓展新知识

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论

打赏作者

鱼摸摸

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值