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

What are non-container thread errors in tomcat?

在我的 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 ...

所以问题是:当我看到像上面引用的那样的日志时,这意味着什么?错误发生在非容器线程中是什么意思?我怎样才能重新创建它?

第一期:

INFO: An error occurred in processing while on a non-container thread. The connection will be closed immediately

答案:

async.Stockticker 线程因未处理的 ISE 而崩溃。这解释了行为。异常仅记录到控制台。它没有记录到 Tomcat 日志文件中。 这是 Tomcat 的错误处理错误。这不是最近变化的倒退。它可以用 Tomcat 7.0.59.

重现

它已在 trunk 8.0.x (for 8.0.21 onwards)7.0.x (for 7.0.60 onwards) 中修复。所以你可以升级你的tomcat版本。则此信息消息将不会显示。

资源Link:

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



第二期:

java.net.SocketException: Broken pipe

方案一:

这个问题实际上是在我们错过了关闭像 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(); 

方案二:

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

数据需要 long time to be generated 并且负载测试工具没有 wait for the large amounts of data 足够长,然后关闭连接。实际上内存不足导致应用程序关闭接收套接字或完全退出,效果相同。

如果我们向 JVM 添加额外的内存,问题就解决了。

方案三:

按照@EJP的建议,

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

所以您的应用程序协议定义或实施不当。如果发生这种情况,则说明您的应用程序协议规范或实现有问题,很可能是您甚至没有连接。

如果服务器端 HTTP 应用程序收到 Broken Pipe 异常,这仅意味着客户端浏览器 exited/gone 到 history/whatever 中的另一个 page/timed out/gone。算了。

资源Link:

  1. How to fix java.net.SocketException: Broken pipe?
  2. Why a "java.net.SocketException: Broken pipe" will occur?

如果您想重现此错误,那么此 tutorial 可能会对您有所帮助。



第三期:

Exception in thread "Thread-28" java.lang.NumberFormatException: For input string: "boom"

您尝试将字母字符串convert/parse 转换为整数 时会发生此错误。正常java错误NumberFormatException.



更新:

As you have wanted to know that in which cases Tomcat decides to log that additional message when catching exception that was thrown out of an application. So I am sharing briefly.

为了弄清楚"INFO: An error occurred in processing while on a non-container thread. The connection will be closed immediately java.net.SocketException: Broken pipe",首先跟大家说说是socket相关的问题。 tomcat中有6个SocketEvent。他们是OPEN_READ, OPEN_WRITE, STOP, TIMEOUT, DISCONNECT and ERROR。这些发生在每个需要容器进一步处理的套接字上。通常这些事件由套接字实现触发,但它们也可能由容器触发。

套接字事件"ERROR":

非容器线程发生错误,需要return处理容器以进行任何必要的清理。使用它的示例包括:

  • 由 NIO2 发出完成处理程序失败的信号
  • 由容器在 Servlet 3.0 异步处理期间在非容器线程上发出 I/O 错误信号。

当发生此错误并且 INFO 消息显示在 TOMCAT?

中时

这是信息消息所在的代码快照。

  /**
     * 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,因此如果容器代码也锁定该对象,则可能会发生死锁。

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

随着异步处理的引入和 non-container threads calling sendError() tracking the current error state 的可能性,确保调用正确的错误页面变得更加复杂。此状态属性有助于跟踪当前错误状态,并在更改成功或另一个线程先到达状态时通知尝试更改状态的调用方。

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

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

在非容器线程中定义读and/or写监听器发起非阻塞IO时调用该方法。一旦非容器线程完成,它就会被调用,以便容器根据需要对 onWritePossible() and/or onDataAvailable() 进行第一次调用。

处理分派需要(for APR/native at least)套接字已添加到 waitingRequests 队列。当非容器线程完成触发对此方法的调用时,这可能还没有发生。 因此,在 SocketWrapper 上进行编码同步,因为启动此非容器线程的容器线程持有 SocketWrapper 上的锁。在释放 socketWrapper 上的锁之前,容器线程会将套接字添加到 waitingRequests 队列。因此,通过在处理调度之前获取 socketWrapper 上的锁,我们可以确定套接字已经添加到 waitingRequests queue.

What does it really mean that the error occurred in a non-container thread?

当您使用 JSP 3.0+ 异步请求处理时会发生这种情况。

在异步模式下,客户端请求由调用 Servlet 的 service() 方法的 "container" 线程接收。此方法(或子 doXxxx 方法之一)调用 startAsync() 为请求创建 Runnable 并将其分派给执行程序。然后该执行程序处理 ("non-container") 个工作线程上的请求。

(可以在 here 中找到有关异步模式下发生的事情的更详细解释,并附有示例。)

无论如何,"INFO:" 消息只是说原始异常是在 Executor 的工作线程之一的堆栈上抛出的。它在 Tomcat 决定将失败的请求分派回容器线程以便可以执行请求清理时产生。

在您的示例中,我怀疑最初的 SocketException 是由于请求处理时间过长导致客户端(例如用户的浏览器)超时请求并关闭套接字造成的。一段时间后,您的服务器尝试写入响应但由于连接已关闭而失败。

How can I recreate that?

我猜,但您应该能够通过在 Runnablerun() 方法中抛出异常来重现该 "INFO:" 消息。你当然必须使用异步模式。