什么是Tomcat中的非容器线程错误?

16

我在我的Tomcat7catalina.out日志中遇到了一个由第三方库引起的错误,它以以下内容开头:

INFO: An error occurred in processing while on a non-container thread. The connection will be closed immediately
java.net.SocketException: Broken pipe
        at java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:113)

“错误发生在非容器线程”真正意味着什么?

我尝试通过从我的应用程序代码中创建的新Thread抛出异常来获取类似的日志消息,例如:

new Thread(){
    @Override
    public void run() {
        Integer.parseInt("boom");
    }
}.start();

但它导致了

Exception in thread "Thread-28" java.lang.NumberFormatException: For input string: "boom"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:492)
    at java.lang.Integer.parseInt(Integer.java:527)
    at ...

那么问题是:当我看到像顶部引用的日志时,这意味着什么?出现错误在一个非容器线程中意味着什么?如何重新创建它?

2个回答

8
当错误发生在非容器线程时,这意味着正在使用JSP 3.0+的异步请求处理。在异步模式下,客户端请求由“容器”线程接收,该线程调用Servlet的service()方法。此方法(或其子doXxxx方法之一)调用startAsync(),该方法为请求创建一个Runnable并将其分派到执行器。然后,该执行程序在(“非容器”)工作线程上处理请求。 在异步模式下发生了什么以及完整示例可以在此处找到详细说明。 无论如何,“INFO:”消息只是说明原始异常是在Executor的某个工作线程的堆栈上引发的。当Tomcat决定将失败的请求分派回容器线程以执行请求清理时,它会产生该消息。 在您的示例中,我怀疑原始的SocketException是由于请求处理时间太长,导致客户端(例如用户的浏览器)超时并关闭套接字而引起的。一段时间后,您的服务器尝试写入响应,但由于连接已关闭,操作失败。 如何重新创建?我的猜测是,您可以通过在Runnablerun()方法中抛出异常来重现该“INFO:”消息。当然,您必须使用异步模式。

6

首要问题:

INFO: 在非容器线程上处理时发生处理错误,连接将立即关闭

答案:

async.Stockticker 线程由于未处理的 ISE 而崩溃。这就是行为表现。异常仅记录在控制台中,而不记录在 Tomcat 日志文件中。这是 Tomcat 错误处理的一个 bug,与最近的更改无关。使用 Tomcat 7.0.59 可以重现此问题。

已经在 trunk 8.0.x(从 8.0.21 开始)7.0.x(从 7.0.60 开始) 中修复了此问题。所以您可以升级您的 Tomcat 版本。然后就不会显示此信息消息。

资源链接:

Bug 57683 - Crash of stockticket async example caused by an aborted client request



次要问题:

java.net.SocketException: Broken pipe

解决方案-1:

当我们忘记关闭诸如 URLConnection 和关闭各种打开的流时,实际上会出现此问题。我想分享一个例子

之前:

OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
bw.write("Lorem ipsum...");
out.close();

之后:

OutputStream os = urlConnection.getOutputStream();
OutputStream out = new BufferedOutputStream(os);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
bw.write("Lorem ipsum...");
bw.close(); //This is must. If you miss to close, then "java.net.SocketException: Broken pipe" error comes
out.close();
os.close(); 

解决方案二:

当我们想要在应用服务器中进行负载测试时,有时会出现这种错误。

数据需要较长时间生成,并且负载测试工具没有等待足够长的时间,然后就关闭了连接。实际上,低内存导致应用程序关闭接收套接字或确实完全退出,具有相同的效果。

如果我们向JVM添加额外的内存,则可以解决此问题。

解决方案三:

正如@EJP所建议的那样,

这是由于在另一端已经关闭连接时仍写入连接造成的。

因此,您的应用程序协议定义不够清晰或实现不够好。如果发生这种情况,则表示您的应用程序协议规范或实现存在问题,最有可能是您甚至没有连接。

如果服务器端HTTP应用程序出现Broken Pipe异常,这只意味着客户端浏览器已退出/转到了其他页面/超时/在历史记录中返回等等。别管它。

资源链接:

  1. 如何解决 java.net.SocketException: Broken pipe?
  2. 为什么会出现“java.net.SocketException: Broken pipe”?

如果您想再现此错误,则可以参考此教程



第三个问题:

线程 "Thread-28" 中的异常 java.lang.NumberFormatException:对于输入字符串:"boom"

您试图将字母字符串转换/解析为整数时,就会出现此错误。它是普通的Java错误NumberFormatException。



更新:

由于您想知道在哪些情况下Tomcat决定在捕获应用程序抛出的异常时记录该附加消息,因此我会简要分享一下。

为了更清楚地理解"INFO: An error occurred in processing while on a non-container thread. The connection will be closed immediately java.net.SocketException: Broken pipe",首先我想与您分享它是套接字相关问题。Tomcat有6个套接字事件。它们是OPEN_READ、OPEN_WRITE、STOP、TIMEOUT、DISCONNECT和ERROR。这些事件每个套接字都需要容器进一步处理。通常,这些事件由套接字实现触发,但也可能由容器触发。

Socket事件 "ERROR":

在非容器线程上发生错误,需要返回到容器以进行任何必要的清理。使用此功能的示例包括:

  • NIO2使用它来通知完成处理程序失败。
  • 容器用它来指示Servlet 3.0异步处理期间非容器线程的I/O错误。

当此错误发生并显示TOMCAT中的INFO消息时?

下面是信息消息出现的代码片段.

  /**
     * Update the current error state to the new error state if the new error
     * state is more severe than the current error state.
     * @param errorState The error status details
     * @param t The error which occurred
     */
    protected void setErrorState(ErrorState errorState, Throwable t) {
        boolean blockIo = this.errorState.isIoAllowed() && !errorState.isIoAllowed();
        this.errorState = this.errorState.getMostSevere(errorState);
        if (blockIo && !ContainerThreadMarker.isContainerThread() && isAsync()) {
            // The error occurred on a non-container thread during async
            // processing which means not all of the necessary clean-up will
            // have been completed. Dispatch to a container thread to do the
            // clean-up. Need to do it this way to ensure that all the necessary
            // clean-up is performed.
            if (response.getStatus() < 400) {
                response.setStatus(500);
            }
            getLog().info(sm.getString("abstractProcessor.nonContainerThreadError"), t);  // This section gives the INFO message "INFO: An error occurred in processing while on a non-container thread. The connection will be closed immediately"
            socketWrapper.processSocket(SocketEvent.ERROR, true);
        }
    }

死锁会如何发生?

当一个请求使用一系列的多个 start(); dispatch() 与非容器线程时,之前的 dispatch() 可能会干扰后续的 start()。这把锁可以防止这种情况发生。这是一个专用对象,因为用户代码可能在 AsyncContext 上进行锁定,所以如果容器代码也锁定该对象,则可能发生死锁。

非容器线程如何定义当前错误状态并给出响应?

随着异步处理的引入和 非容器线程调用 sendError() 跟踪当前错误状态 并确保调用正确的错误页面变得更加复杂。该状态属性通过跟踪当前错误状态并通知尝试更改状态的调用者,如果更改成功或其他线程先到达,则有助于帮助。

   /** 
     * The state machine is very simple:
     *
     * 0 - NONE
     * 1 - NOT_REPORTED
     * 2 - REPORTED
     *
     *
     *   -->---->-- >NONE
     *   |   |        |
     *   |   |        | setError()
     *   ^   ^        |
     *   |   |       \|/
     *   |   |-<-NOT_REPORTED
     *   |            |
     *   ^            | report()
     *   |            |
     *   |           \|/
     *   |----<----REPORTED
     * 
     */

当在非容器线程中调用executeNonBlockingDispatches(...)方法以及它如何与SocketWrapper交互?

当在非容器线程中定义读和/或写监听器来启动非阻塞IO时,将调用此方法。一旦非容器线程完成,则调用该方法,以便容器通过适当的第一次调用onWritePossible()和/或onDataAvailable()

处理分派需要(至少对于APR/native),需要将套接字添加到waitingRequests队列中。这可能在非容器线程完成触发对此方法的调用时尚未发生。因此,编码在SocketWrapper上同步,因为启动此非容器线程的容器线程持有SocketWrapper的锁。容器线程将在释放socketWrapper上的锁之前将套接字添加到waitingRequests队列中。因此,在处理分派之前获取socketWrapper上的锁,我们可以确保套接字已添加到waitingRequests队列中。


1
这并没有回答我的问题。你提到了某个特定示例应用程序中的缺陷,而我并没有询问。消息“在非容器线程上处理时发生错误”来自Tomcat,而不是任何特定的部署应用程序。我想了解在哪些情况下Tomcat决定在捕获应用程序抛出的异常时记录该附加消息。 - TMG
@TMG 我已经了解您的问题。我已经更新了答案。请查看并告诉我您的想法。 - SkyWalker
2
这个回答真的需要这么长吗?里面有很多看起来与问题无关的东西。 - Stephen C
1
@StephenC 我同意您的观点,先生。实际上,解决方案部分已经在最顶端给出。然后我在更新的部分中提供了框架细节。 - SkyWalker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接