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

2022-09-03 07:18:55

在我的日志中,我收到由以下开头的第三方库引起的错误:catalina.outTomcat7

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 ...

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


答案 1

在非容器线程中发生错误的真正含义是什么?

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

在异步模式下,客户端请求由调用 Servlet 方法的“容器”线程接收。此方法(或其中一个辅助方法)调用,该调用为请求创建一个并将其分派给执行器。然后,此执行程序处理(“非容器”)工作线程上的请求。service()doXxxxstartAsync()Runnable

(可以在此处找到有关异步模式下所发生情况的更详细说明,并配有示例。

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

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

如何重新创建它?

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


答案 2

第一期:

信息:在非容器线程上进行处理时出错。连接将立即关闭

答:

由于未处理的 ISE,线程崩溃。这解释了行为。异常仅记录到控制台。它不会登录到 Tomcat 日志文件中。这是一个针对Tomcat的错误处理错误。它不是最近变化的倒退。它可与 重现。async.StocktickerTomcat 7.0.59

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

资源链接:

Bug 57683 - 由中止的客户端请求导致的股票代码异步示例崩溃



第二期:

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(); 

解决方案-2:

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

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

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

解决方案-3:

正如@EJP建议的那样,

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

因此,您的应用程序协议定义或实现得很差。如果发生这种情况,则您的应用程序协议规范或实现存在问题,很可能是您甚至没有连接。

如果服务器端HTTP应用程序收到Broken Pipe异常,则仅表示客户端浏览器已退出/返回到历史记录中的另一个浏览器/任何内容。忘掉它吧。page/timed out/gone

资源链接:

  1. 如何修复java.net.SocketException:坏了管道?
  2. 为什么会出现“java.net.SocketException: Broken pipe”?

如果要重现此错误,则本教程可能会对您有所帮助。



第三期:

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

当您尝试将字母字符串转换/解析为 interger 时,会发生此错误。这是正常的java错误。NumberFormatException



更新:

正如您想知道的那样,在哪种情况下,Tomcat决定在捕获从应用程序中抛出的异常时记录该附加消息。所以我简要地分享一下。

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

套接字事件“错误”:

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

  • 通过 NIO2 发出完成处理程序失败的信号
  • by 容器,用于在 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() 的序列时; 对于非容器线程,前置线程可能会干扰后续线程。此锁可防止这种情况发生。它是一个专用对象,因为用户代码可能会锁定AsyncContext,因此如果容器代码也锁定该对象,则可能会发生死锁。dispatch()dispatch()start()

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

随着异步处理的引入以及确保调用正确错误页面的可能性变得更加复杂。此 state 属性通过跟踪当前错误状态并通知尝试更改状态的调用方(如果更改成功或另一个线程先到达状态)来提供帮助。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
     * 
     */

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

通过在非容器线程中定义读取和/或写入侦听器来启动非阻塞 IO 时,将调用此方法。非容器线程完成后调用它,以便容器对和/或(或)进行第一次调用。onWritePossible()onDataAvailable()

处理调度需要将套接字添加到等待请求队列中。在非容器线程完成触发对此方法的调用时,可能尚未发生这种情况。因此,编码的同步在 SocketWrapper 上作为启动此非容器线程的容器线程在 SocketWrapper 上持有一个锁。容器线程会先将套接字添加到等待请求队列中,然后再释放套接字包装器上的锁。因此,通过在处理调度之前获取套接字Wrapper上的锁,我们可以确定套接字已添加到.(for APR/native at least)waitingRequests queue


推荐