首要问题:
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()
out.close()
os.close()
解决方案二:
当我们想要在应用服务器中进行负载测试时,有时会出现这种错误。
数据需要较长时间生成,并且负载测试工具没有等待足够长的时间,然后就关闭了连接。实际上,低内存导致应用程序关闭接收套接字或确实完全退出,具有相同的效果。
如果我们向JVM添加额外的内存,则可以解决此问题。
解决方案三:
正如@EJP所建议的那样,
这是由于在另一端已经关闭连接时仍写入连接造成的。
因此,您的应用程序协议定义不够清晰或实现不够好。如果发生这种情况,则表示您的应用程序协议规范或实现存在问题,最有可能是您甚至没有连接。
如果服务器端HTTP应用程序出现Broken Pipe异常,这只意味着客户端浏览器已退出/转到了其他页面/超时/在历史记录中返回等等。别管它。
资源链接:
- 如何解决 java.net.SocketException: Broken pipe?
- 为什么会出现“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消息时?
下面是信息消息出现的代码片段.
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()) {
if (response.getStatus() < 400) {
response.setStatus(500);
}
getLog().info(sm.getString("abstractProcessor.nonContainerThreadError"), t);
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队列
中。