【问题标题】:Tomcat issue while running a linux command运行linux命令时出现Tomcat问题
【发布时间】:2014-10-07 05:16:39
【问题描述】:

我正在尝试在 java 代码中运行 linux 命令。该命令是 raspivid ,我已将它放在服务器上的 test.sh 文件中,用于实时摄像头流式传输。一切正常,但问题是在启动 tomcat 服务器几分钟后流停止。就像在 java 中运行命令时流式传输在 6-7 分钟后停止一样,但在后台运行 raspivid 进程。另一方面,当我在不使用 java 代码的情况下运行相同的命令时,它工作正常。这是tomcat堆的问题还是其他停止流式传输的问题?请帮忙看看下面的代码:

try {
         Process p = Runtime.getRuntime().exec(new String[]{"sudo","sh","/home/pi/test.sh"});
            BufferedReader stdInput = new BufferedReader(new
                 InputStreamReader(p.getInputStream()));

            BufferedReader stdError = new BufferedReader(new
                 InputStreamReader(p.getErrorStream()));

            // read the output from the command
            LOGGER.info("Here is the standard output of the command:\n");
            while ((s = stdInput.readLine()) != null) {
                LOGGER.info(s);
            }

            // read any errors from the attempted command
            LOGGER.info("Here is the standard error of the command (if any):\n");
            while ((s = stdError.readLine()) != null) {
                LOGGER.info(s);
            }

        }

【问题讨论】:

    标签: java linux tomcat debian raspberry-pi


    【解决方案1】:

    问题是 BufferedReader.readLine() 阻塞直到可以读取整行(由任何行结束字符序列终止),并且您不会读取进程“并行”的 2 个输出,如果它的缓冲区被填满并且进程被阻塞。

    需要读取进程输出的数据。

    一个进程有两个输出流:标准输出和错误输出。您必须阅读both,因为该过程可能会写入这两个输出。

    进程的输出流有缓冲区。如果输出流的缓冲区已满,则尝试向被阻塞进程写入更多数据。

    做这样的事情:

    BufferedReader stdInput = new BufferedReader(
            new InputStreamReader(p.getInputStream()));
    
    BufferedReader stdError = new BufferedReader(
            new InputStreamReader(p.getErrorStream()));
    
    while (p.isAlive()) {
        while (stdInput.ready())
            LOGGER.info(stdInput.readLine());
    
        while (stdError.ready())
            LOGGER.info(stdError.readLine());
    
        Thread.sleep(1);
    }
    

    这个解决方案的问题(并修复它):

    此解决方案有错误。该进程可能不会将完整的行写入其输出。如果是这种情况,进程可能仍然挂起,例如,如果它向其标准输出写入 1 个字符,则 stdInput.readLine() 将阻塞(因为它会读取直到遇到换行符)并且如果进程将继续写入其错误流,当错误流的缓冲区已满时,进程将被阻塞。

    所以最好不要按行而是按字符读取缓冲区的输出流(当然这会使记录变得更加困难):

    StringBuilder lineOut = new StringBuilder(); // std out buffer
    StringBuilder lineErr = new StringBuilder(); // std err buffer
    
    while (p.isAlive()) {
        while (stdInput.ready()) {
            // Append the character to a buffer or log if it is the line end
            char c = (char) stdInput.read();
            if (c == '\n') {  // On UNIX systems line separator is one char: '\n'
                LOGGER.info(lineOut.toString());
                lineOut.setLength(0);
            }
            else
                lineOut.append(c);
        }
    
        while (stdError.ready()) {
            // Append the character to a buffer or log if it is the line end
            char c = (char) stdError.read()
            if (c == '\n') {  // On UNIX systems line separator is one char: '\n'
                LOGGER.info(lineErr.toString());
                lineErr.setLength(0);
            }
            else
                lineErr.append(c);
        }
    
        Thread.sleep(1);
    }
    

    替代(更清洁、更简单)的解决方案

    或者,您可以启动 2 个线程,一个读取进程的标准输出,一个读取进程的标准错误。这可以简化事情:

    private static void consumeStream(final InputStream is) {
        new Thread() {
            @Override
            public void run() {
                BufferedReader r = new BufferedReader(new InputStreamReader(is));
                String line;
                while ((line = r.readLine()) != null)
                    LOGGER.info(line);
            }
        }.start();
    }
    

    并使用它:

    consumeStream(p.getInputStream());
    consumeStream(p.getErrorStream());
    

    【讨论】:

    • 如何将标准输出和标准错误重定向到文件(或 nulll)以避免在 Java 中读取它们? ``sudo sh /home/pi/test.sh >/dev/null 2>/dev/null''
    • 这也可能有效,但是您将失去分析 Java 输出或使用 LOGGER 指定的格式选项的可能性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-12
    • 2011-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-02
    • 2015-04-09
    相关资源
    最近更新 更多