【发布时间】:2018-05-17 07:46:07
【问题描述】:
我有一个 Tomcat 8 Web 应用程序,它使用 2 MB png 图像作为“启动/登陆页面”背景。图像在外部样式表中被引用。
如果我清除浏览器缓存,直接请求图像的 URL,但在图像加载之前离开图像,它将完全杀死我的 Web 应用程序。它无法为任何请求提供更多响应。
我收到此错误:
org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe
at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:396)
at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:344)
at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:421)
at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:409)
at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:97)
at org.apache.catalina.servlets.DefaultServlet.copy(DefaultServlet.java:1795)
at org.apache.catalina.servlets.DefaultServlet.serveResource(DefaultServlet.java:919)
at org.apache.catalina.servlets.DefaultServlet.doGet(DefaultServlet.java:400)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at other.JsCssImgResponseHeaderFilter.doFilter(JsCssImgResponseHeaderFilter.java:36)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at other.CatchAnyExceptionFilter.doFilter(CatchAnyExceptionFilter.java:39)
我正在尝试捕获该异常(或任何与此相关的异常)并显示友好的 ErrorPage.jsp,但我无法重定向到 ErrorPage,因为响应已经提交。
我无法在测试环境中复制它,但我可以在生产环境中随意破坏它。
有人知道我有哪些解决此类问题的方法吗?提前致谢。
顺便说一句,这是我的 Filter 类的 doFilter 方法的样子:
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain fc) throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession();
try
{
fc.doFilter(req, res);
// Have learned that response headers should not be set until we have determined there won't be an exception.
// Can't easily redirect or forward (to an error page, for example) if headers have already been committed.
if (SessionHelper.hasNeedsJsCssImgResponseHeaderFlag(request))
{
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, JsCssImgResponseHeaderFilter.SECONDS_TO_CACHE);
response.setHeader("Cache-Control", "PUBLIC, max-age=" + JsCssImgResponseHeaderFilter.SECONDS_TO_CACHE + ", must-revalidate");
response.setHeader("Expires", DateUtils.getDateInExpiresHeaderFormat(cal.getTime()));
}
else if (SessionHelper.hasNeedsDynamicPageResponseHeaderFlag(request))
{
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expires", "Mon, 25 Nov 2013 00:00:01 GMT"); // in the past
}
else if (SessionHelper.hasNeedsRestServicesResponseHeaderFlag(request))
{
// CORS - Cross Origin Resource Sharing: allow service requests from other domains --
// such as local development domains hosting AngularJS projects.
response.setHeader("Access-Control-Allow-Origin", "http://localhost.domain.com:8080");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE");
// response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With");
}
}
catch (Exception e)
{
SessionHelper.setErrorPageException(req, session, e);
final String uri = request.getRequestURI();
final String playerName = SessionHelper.getLoggedInPlayerName(session);
String outerLogEntryText = "Player = " + playerName + " - IP = " + request.getRemoteAddr() +
" - Mobile = " + SessionHelper.isMobileBrowser(request) +
" - URI = " + uri + " - Exception MSG = " + e.getMessage();
LogUtils.writeLogEntry(outerLogEntryText, true, e);
SessionHelper.setRedirectPage(session, SessionHelper.ERROR_PAGE_PATH);
}
finally
{
String redirectPage = SessionHelper.getRedirectPage(session);
SessionHelper.clearRedirectPage(session);
if (redirectPage != null)
{
if (response.isCommitted())
{
// Article: http://stackoverflow.com/questions/11305563/cause-of-servlets-response-already-committed
System.out.println("Response has been committed. Cannot redirect to: " + redirectPage);
}
else
{
response.sendRedirect(redirectPage);
}
}
}
}
当问题发生时,我的“响应已提交...”输出语句确实被写入,因此它没有重定向到我的错误页面。这就是我的挑战:当这种情况发生时如何重定向——或者采取一些更好/无害的行动。
【问题讨论】:
-
你使用什么浏览器重要吗? Chrome v. Firefox v. IE v. Safari?
-
导航走到底是什么意思?关闭页面?切换标签?转到同一站点上的不同页面?损坏的管道意味着在页面完成加载之前服务器和客户端之间的连接已被切断。顺便说一句,一个网站上的 2mb 图像太小了,即使它是那里唯一的东西。
-
这个异常已经被捕获。 这意味着它甚至没有杀死它被抛出的线程,更不用说整个Tomcat了。我认为问题出在其他地方。
-
Khuderm,我所说的导航离开是指点击一个链接到其他地方——就像浏览器书签栏上的一个链接到另一个站点。从最初的帖子开始,我使用tinypng.com 将我的启动背景图像大小减少到 539 KB。
-
EJP,是的,异常被捕获。从技术上讲,它可能没有杀死 Tomcat,但是我的 Web 应用程序停止响应请求,并且 Tomcat Manager Web 应用程序不再工作。我必须重启Tomcat服务才能恢复。