我有一个Java程序,在该程序中触发了外壳脚本。Java代码示例是:
ProcessBuilder pb = new ProcessBuilder(cmdList);
p = pb.start();
p.waitFor();
cmdList包含执行外壳程序所需的所有必需输入参数。此Shell脚本内部有一个for循环,并在该循环中执行一些DB脚本,并在文件中打印结果信息和错误日志。
下面是示例shell脚本代码:
#!/bin/bash
导出PATH = / apps / PostgresPlus / as9.6 / bin:/ usr / local / sbin:/ usr / local / bin:/ usr / sbin:/ usr / bin:/ sbin:/ bin
set -eE
################################################ ##开始TIME_ELAPSED =“”TIME_ELAPSED_IN_HOURS =“”SCRIPT_START_TIME_FORMATTED =date '+%F %T'
SCRIPT_START_TIME_IN_SEC = date +%s
PROCESS_LOG_BASE_PATH =“ / data / logs / purge_log /”PROCESS_LOG =“ $ PROCESS_LOG_BASE_PATH / purge.log”
trap'err = $ ?; logError 2>&1“清除期间发生错误。在$ LINENO行以$ err状态退出:$ {BASH_COMMAND}。请查看日志以获取更多信息。” >> $ PROCESS_LOG'错误“ trap'logError 2>&1”清除期间发生错误。由于收到外部中断而退出Shell脚本执行。请查看日志以获取更多信息。“ >> $ PROCESS_LOG;陷阱ERR'INT
banner(){回声“ + ----------------------------------------------- ------------------------------------------------- + ”printf“ |tput bold
[%-40stput sgr0
| \ n”“ $ 1]tput setaf 2
$ 2”回声“ + ----------------------------------------------- ------------------------------------------------- + ”}
logError(){printf“ [ProcessId- $$] [date "+%Y-%m-%d %H:%M:%S"
]tput setaf 1 tput bold
[错误] tput setaf 1
%-40stput sgr0
\ n”“ $ @”}
logInfo(){printf“ [ProcessId- $$] [date "+%Y-%m-%d %H:%M:%S"
]tput setaf 6 bold
[INFO]%-40stput sgr0
\ n”“ $ @”}logWarn(){printf“ [ProcessId- $$] [date "+%Y-%m-%d %H:%M:%S"
]tput setaf 3
tput bold
[警告]%-40stput sgr0
\ n”“ $ @”}
logHint(){printf“ [ProcessId- $$] [date "+%Y-%m-%d %H:%M:%S"
]tput setaf 5
tput sitm
%-40stput sgr0
\ n”“ $ @”}
main(){标语“ $ SCRIPT_START_TIME_FORMATTED”“开始处理” | tee -a $ PROCESS_LOGlogInfo“从$ SCRIPT_START_TIME_FORMATTED开始执行” | tee -a $ PROCESS_LOG
set PGPASSWORD=$DB_PASSWORD
export PGPASSWORD=$DB_PASSWORD
# Call DB function for audit and category wise data purging, population of schema names
SCHEMA_NAMES_RESULT=$(psql -h $HOST_NAME -d $DB_NAME -U $DB_USER -p $DB_PORT -At -c "SELECT $COMMON_SCHEMA_NAME.purge_audit_and_populate_schema_names('$COMMON_SCHEMA_NAME', $PURGE_DATA_INTERVAL_IN_DAYS,'$SCHEMA_NAMES',$NUM_TOP_CONTRIBUTING_TENANTS)")
SCHEMA_NAMES_RESULT=$(echo "$SCHEMA_NAMES_RESULT" | sed 's/{//g; s/}//g; s/"//g' )
SCHEMA_NAMES=$(echo $SCHEMA_NAMES_RESULT | rev | cut -d"," -f2- | rev)
#Convert comma separated string of tenants to array
SCHEMA_NAMES=($(echo "$SCHEMA_NAMES" | tr ',' '\n'))
# loop for multi schema
for element in "${SCHEMA_NAMES[@]}"
do
logInfo "Effective tenant - $element, Script start time - $SCRIPT_START_TIME_FORMATTED" | tee -a $PROCESS_LOG
# PGSQL call to DB function to execute purging
logInfo "Time elapsed since script execution started - $TIME_ELAPSED" | tee -a $PROCESS_LOG
done
#logInfo "Purge completed!" | tee -a $PROCESS_LOG
logInfo "Purge execution completed successfully at `date '+%F %T'`" | tee -a $PROCESS_LOG
exit 0
}
mkdir -p $ PROCESS_LOG_BASE_PATH主要的“ $ @”
################################################ ## 结束以下是我对该程序的观察。
当通过上面的java程序触发shell脚本时,我观察到以下行为。
a。它在for循环中经过特定的迭代后挂起。
b。随着我减少来自Shell脚本的日志条目的数量,迭代(用于循环)的数量不断增加。
c。当我删除所有信息日志并继续仅打印错误日志时,它就成功完成了。
有人可以帮助您确定此行为背后的原因吗?
现在,我检查for循环中的迭代次数,但是当我开始接收多个错误日志时,随时可能会出现该问题。
问候
Kushagra
您必须消耗流程流,以便本机缓冲区不会填满。如果您创建线程来消费每个流,它会更好地工作。 hacky单线程版本是这样的:
ProcessBuilder pb = new ProcessBuilder(cmdList);
p = pb.start();
try (InputStream in = p.getInputStream();
InputStream err = p.getErrorStream();
OutputStream closeOnly = p.getOutputStream()) {
while (p.isAlive()) {
long skipped = in.skip(in.available())
+ err.skip(err.available());
if(skipped == 0L) {
p.waitFor(5L, TimeUnit.MILLISECONDS);
}
}
} finally {
p.destroy();
}
线程方式如下:
public void foo() {
class DevNull implements Runnable {
private final InputStream is;
DevNull(final InputStream is) {
is = Objects.requireNonNull(is);
}
public void run() {
byte[] b = new byte[64];
try {
while (is.read(b) >= 0);
} catch(IOException ignore) {
}
}
}
ExecutorService e = Executors.newCachedThreadPool();
ProcessBuilder pb = new ProcessBuilder(cmdList);
Process p = pb.start();
try (InputStream in = p.getInputStream();
InputStream err = p.getErrorStream();
OutputStream closeOnly = p.getOutputStream()) {
e.execute(new DevNull(in));
e.execute(new DevNull(err));
p.waitFor();
} finally {
p.destroy();
e.shutdown();
}
}