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="";
首次应用,拓展新知识