java 如何防止“Broken pipe”关闭我的 tomcat 网站?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35761657/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 00:33:30  来源:igfitidea点击:

How do I prevent "Broken pipe" from taking down my tomcat web site?

javatomcat8

提问by Steve Swett

I have a Tomcat 8 web application that uses a 2 MB png image as a "splash/landing page" background. The image is referenced in an external stylesheet.

我有一个 Tomcat 8 Web 应用程序,它使用 2 MB png 图像作为“启动/登录页面”背景。该图像在外部样式表中被引用。

If I clear my browser cache, directly request the URL of the image, but navigate away from the image before it has loaded, it will completely kill my web application. It fails to provide any more responses for any requests.

如果我清除浏览器缓存,直接请求图像的 URL,但在图像加载之前离开图像,它将完全杀死我的 Web 应用程序。它无法为任何请求提供更多响应。

I get this error:

我收到此错误:

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)

I'm trying to catch that exception (or any exception for that matter) and show a friendly ErrorPage.jsp, but I can't redirect to the ErrorPage because the response has already been committed.

我正在尝试捕获该异常(或任何与此相关的异常)并显示友好的 ErrorPage.jsp,但我无法重定向到 ErrorPage,因为响应已经提交。

I am unable to duplicate this in a test environment, but I can make it break at will in production.

我无法在测试环境中复制它,但我可以在生产中随意打破它。

Does anyone know what my options are for solving this kind of problem? Thanks in advance.

有谁知道我有哪些选择可以解决此类问题?提前致谢。

BTW, here is what my Filter class's doFilter method looks like:

顺便说一句,这是我的 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);
            }
        }
    }
}

When the problem occurs, my "Response has been committed..." output statement is indeed written, therefore it is not redirecting to my error page. That's my challenge: how to redirect somewhere when this happens -- or take some better/harmless action.

当问题发生时,我的“响应已提交...”输出语句确实写了,因此它没有重定向到我的错误页面。这就是我的挑战:如何在发生这种情况时重定向到某个地方——或者采取一些更好/无害的行动。

回答by Steve Swett

After making a couple changes, the problem hasn't occurred in production. The most impactful change was to reduce the size of my background/splash image. The other change was that I increased the Tomcat Connector connectionTimeout attribute setting 6-fold.

进行了几次更改后,生产中没有出现问题。最有影响力的变化是减少了我的背景/初始图像的大小。另一个变化是我将 Tomcat Connector connectionTimeout 属性设置增加了 6 倍。