Как мне предотвратить отключение моего веб-сайта Tomcat от Broken pipe?

У меня есть веб-приложение Tomcat 8, которое использует изображение PNG размером 2 МБ в качестве фона «заставки / целевой страницы». На изображение есть ссылка во внешней таблице стилей.

Если я очищу кеш браузера, напрямую запрошу URL-адрес изображения, но уйду от изображения до его загрузки, это полностью убьет мое веб-приложение. Он не может предоставить больше ответов на какие-либо запросы.

Я получаю такую ​​ошибку:

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, потому что ответ уже был зафиксирован.

Я не могу воспроизвести это в тестовой среде, но могу по желанию сломать его в производственной среде.

Кто-нибудь знает, какие у меня есть варианты решения такой проблемы? Заранее спасибо.

Кстати, вот как выглядит метод doFilter моего класса Filter:

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

Когда возникает проблема, мой выходной оператор «Ответ был подтвержден ...» действительно записывается, поэтому он не перенаправляет на мою страницу с ошибкой. Это моя проблема: как перенаправить куда-нибудь, когда это происходит, или предпринять какие-то более безопасные / безопасные действия.


person Steve Swett    schedule 03.03.2016    source источник
comment
имеет значение, какой браузер вы используете? Chrome против Firefox против IE против Safari?   -  person Scott Sosna    schedule 03.03.2016
comment
что именно вы имеете в виду, говоря «уйти»? закрыть страницу? переключить вкладку? перейти на другую страницу того же сайта? разорванный канал означает, что соединение между сервером и клиентом было разорвано до завершения загрузки страницы. кстати, 2мб изображения на сайте - это слишком мало, даже если это единственное, что там есть.   -  person RisingSun    schedule 03.03.2016
comment
Это исключение было перехвачено. Это означает, что оно даже не убило поток, в который оно было брошено, не говоря уже о Tomcat в целом. Я предполагаю, что проблема в другом.   -  person user207421    schedule 03.03.2016
comment
Худерм, под «уходить» я имею в виду щелчок по ссылке, которая ведет в другом месте - например, по ссылке на панели закладок браузера, ведущей на другой сайт. Начиная с исходного сообщения, я использовал tinypng.com, чтобы уменьшить размер фонового изображения заставки до 539 КБ.   -  person Steve Swett    schedule 04.03.2016
comment
EJP, да, исключение отловлено. Технически это, вероятно, не убило Tomcat, но мое веб-приложение перестает отвечать на запросы, и веб-приложение Tomcat Manager больше не работает. Мне нужно перезапустить службу Tomcat для восстановления.   -  person Steve Swett    schedule 04.03.2016
comment
Пока что из-за уменьшения размера файла изображения мне не удалось воспроизвести проблему в продакшене, но моя скорость загрузки через Интернет (47 Мбит / с) могла быть слишком высокой, чтобы ее можно было уловить. В течение следующих нескольких дней я посмотрю, не сломаются ли другие пользователи / обстоятельства. Будет держать вас в курсе.   -  person Steve Swett    schedule 04.03.2016
comment
Другой вариант: если вы используете apache, nginx или аналогичный веб-сервер в качестве обратного прокси, вы можете выгружать статические ресурсы для загрузки непосредственно с веб-сервера, тем самым уменьшая нагрузку с вашего tomcat.   -  person Gerald Schneider    schedule 04.03.2016
comment
@SteveSwett Итак, если исключение было обнаружено, ваше заявление о том, что оно «привело к остановке сайта Tomcat», неверно. Если у вас есть другая проблема, опубликуйте ее.   -  person user207421    schedule 11.12.2017


Ответы (1)


После внесения пары изменений проблема не возникла в производственной среде. Самым значительным изменением было уменьшение размера моего фонового изображения / заставки. Другое изменение заключалось в том, что я увеличил значение атрибута Tomcat Connector connectionTimeout в 6 раз.

person Steve Swett    schedule 09.03.2016