在ProcessBuilder中运行时,Shell脚本会挂起

问题描述 投票:0回答:1

我有一个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 3tput bold [警告]%-40stput sgr0\ n”“ $ @”}

logHint(){printf“ [ProcessId- $$] [date "+%Y-%m-%d %H:%M:%S"]tput setaf 5tput 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主要的“ $ @”

################################################ ## 结束

以下是我对该程序的观察。

  1. 直接在腻子上运行shell脚本时,它可以正确执行,没有任何错误。
  2. 当通过上面的java程序触发shell脚本时,我观察到以下行为。

    a。它在for循环中经过特定的迭代后挂起。

    b。随着我减少来自Shell脚本的日志条目的数量,迭代(用于循环)的数量不断增加。

    c。当我删除所有信息日志并继续仅打印错误日志时,它就成功完成了。

有人可以帮助您确定此行为背后的原因吗?

现在,我检查for循环中的迭代次数,但是当我开始接收多个错误日志时,随时可能会出现该问题。

问候

Kushagra

java shell logging processbuilder hang
1个回答
0
投票

您必须消耗流程流,以便本机缓冲区不会填满。如果您创建线程来消费每个流,它会更好地工作。 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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.