Ошибка Apache / Tomcat-неправильные страницы доставляются

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

подсказки:

  • доставляемые страницы-это те, которые другой пользователь запрашивал недавно, и в противном случае доставляются правильно. Это известно уже два года. одновременные запросы на обмен. Насколько я могу судить, ни одна из неправильно доставленных страниц не старше нескольких минут.
  • это влияет только на файлы, которые обслуживаются Tomcat. Статические файлы, такие как изображения не изменяются.
  • это не происходит все время. Когда это происходит, это происходит для всех.
  • кажется, что это происходит во время пикового спроса. Тем не менее, спрос пока не очень высок - это, безусловно, хорошо в пределах с чем Apache может справиться.
  • перезапуск Tomcat исправил это, но только на несколько минут. Перезапуск Apache исправил это, но только на несколько минут.
  • сервер работает под управлением Apache 2 и Tomcat 6, используя Java 6 VM на Gentoo. Соединение с AJP13, и JkMount директивы <VirtualHost> блоки являются правильными.
  • ни в одном из файлов журнала нет ничего полезного.

далее информация:

Apache не имеет какой-либо формы кэширования включен. Все записи, связанные с кэшированием в httpd.conf и связанный импорт говорят, например:

<IfDefine CACHE>
  LoadModule cache_module modules/mod_cache.so
</IfDefine>

в то время как параметры Apache не включают этот флаг:

APACHE2_OPTS="-D DEFAULT_VHOST -D INFO -D LANGUAGE -D SSL -D SSL_DEFAULT_VHOST -D PHP5 -D JK"

Tomcat также не имеет параметров кэширования, которые я могу найти.

инструментарий предложение было хорошо, но не подходит в этом случае. Что заставляет меня верить, что ошибка не может быть в моем собственном коде в том, что это не просто несколько значений, которые передаются - это весь запрос, включая URL, параметры, куки-файлы сеанса, все это. Люди получают страницы назад, говоря: "вы вошли в систему как Джон", когда они явно не являются.


обновление:

основываясь на предложениях нескольких людей, я собираюсь добавить следующие HTTP-заголовки на страницы Tomcat, чтобы отключить все формы кэширование:

Cache-Control: no-store
Vary: *

Надеюсь, эти заголовки будут уважаться не только Apache, но и любыми другими кэшами или прокси, которые могут быть на пути. К сожалению, у меня нет возможности преднамеренно воспроизвести эту ошибку, поэтому мне просто придется подождать и посмотреть, появится ли она снова.

я замечаю, что следующие заголовки включены - могут ли они быть связаны в любом способ?

Connection: Keep-Alive
Keep-Alive: timeout=5, max=66

обновление:

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

есть ли дополнительная информация, которую я могу поместить в журналы Apache или Tomcat, чтобы сделать это проще диагностировать?


обновление:

поскольку это произошло снова пару раз, мы изменили способ подключения Apache к Tomcat, чтобы узнать, влияет ли это на вещи. Мы использовали mod_jk С такой директивой:

JkMount /portal ajp13

теперь мы переключились на использование mod_proxy_ajp, например:

ProxyPass /portal ajp://localhost:8009/portal

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


обновление:

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


обновление:

мы только что получили ошибку снова прошлой ночью на сайте, используя mod_proxy_ajp, так ясно, что не решил его -mod_jk не было источником проблемы. Я собираюсь попробовать анонимное предложение отключить постоянные соединения:

KeepAlive Off

если это также не удастся, я буду достаточно отчаянным, чтобы начать расследование GlassFish.


обновление:

черт возьми! Проблема только что вернулась. Я не видел его некоторое время, поэтому я начал думать, что мы, наконец, разобрались. Я ненавижу heisenbugs.

11 ответов


может ли это быть потокобезопасность ваших сервлетов?

ваши сервлеты хранят любую информацию в членах экземпляра.

например, что-то простое, как следующее, может вызвать проблемы, связанные с потоками:

public class MyServlet ... {
    private String action;

    public void doGet(...) {
         action = request.getParameter("action");
         processAction(response);
    }

    public void processAction(...) {
         if (action.equals("foo")) {
             // send foo page
         } else if (action.equals("bar")) {
             // send bar page
         }
     }
}

поскольку serlvet доступен несколькими потоками, нет никакой гарантии, что член экземпляра действия не будет заблокирован кем-то запросом elses и в конечном итоге отправит неправильную страницу назад.

простое решение эта проблема заключается в использовании локальных переменных insead членов экземпляра:

public class MyServlet ... {
    public void doGet(...) {
         String action = request.getParameter("action");
         processAction(action, response);
    }

    public void processAction(...) {
         if (action.equals("foo")) {
             // send foo page
         } else if (action.equals("bar")) {
             // send bar page
         }
     }
}

Примечание: это распространяется и на страницы JavaServer, если вы отправляли им свои представления?


Проверьте, позволяют ли ваши заголовки кэшировать без правильного Vary HTTP-заголовок (Если вы используете куки-файлы сеанса, например, и разрешаете кэширование, вам нужна запись в Vary HTTP-заголовок для заголовка cookie или кэш / прокси могут служить кэшированной версией страницы, предназначенной для одного пользователя другому пользователю).

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


8 обновления вопроса позже еще одна проблема для тестирования / воспроизведения, хотя это может быть сложно (или дорого) для публичных сайтов.

вы можете включить HTTPS на сайтах. Это, по крайней мере, уничтожит любые другие кэши прокси по пути. Было бы плохо видеть, что есть некоторые забытые loadbalancers или тайники компании на пути, которые мешают вашему трафику.

для публичных сайтов это будет означать доверенные сертификаты на ключах, поэтому некоторые деньги будут заниматься. Для проверки самоподписанных ключей может быть достаточно. Кроме того, проверьте, что нет прозрачного прокси-сервера, который расшифровывает и повторно шифрует трафик. (они легко обнаруживаются, поскольку они не могут использовать тот же сертификат/ключ, что и исходный сервер)


хотя вы упомянули, что mod_cache не был включен в вашей установке, для других, кто мог столкнуться с той же проблемой с mod_cache включен (даже на статическом содержимом), решение состоит в том, чтобы убедиться, что следующая директива включена в HTTP-заголовке Set-Cookie:

CacheIgnoreHeaders Set-Cookie

причина в том, что mod_cache кэширует заголовок Set-Cookie, который может быть подан другим пользователям. Затем это приведет к утечке идентификатора сеанса от пользователя,который последним заполнял кэш другому.


У меня была эта проблема, и это действительно сводило меня с ума. Я не знаю, почему, но я решил отключить Keep Alive на http.conf

с

KeepAlive On

до

KeepAlive Off

мое приложение не использует функцию keepalive, поэтому он работал очень хорошо для меня.


попробуйте это:

response.setHeader("Cache-Control", "no-cache"); //HTTP 1.1
response.setHeader("Pragma", "no-cache"); //HTTP 1.0
response.setDateHeader("Expires", 0); //prevents caching at the proxy server

посмотрите на этот сайт, он описывает проблему с mod_jk. Я подошел к вашему сообщению, глядя на очень похожую проблему. В основном исправление заключается в обновлении до более новой версии mod_jk. У меня еще не было возможности реализовать изменения на нашем сервере, но я собираюсь попробовать это завтра и посмотреть, поможет ли это.

http://securitytracker.com/alerts/2009/Apr/1022001.html


Я не специалист, но это может быть какой-то странный Преобразование Сетевых Адресов вопрос?


мы переключили Apache с проксирования с AJP на проксирование с HTTP. До сих пор он, похоже, решил проблему или, по крайней мере, значительно уменьшил ее - проблема не сообщалось в течение нескольких месяцев, и использование приложения увеличилось с тех пор.

изменение в httpd Apache.conf. Начав с mod_jk:

JkMount /portal ajp13

мы перешли к mod_proxy_ajp:

ProxyPass /portal ajp://localhost:8009/portal

затем, наконец, прямо mod_proxy:

ProxyPass /portal http://localhost:8080/portal

вам нужно будет убедиться Tomcat настроен для обслуживания HTTP на порту 8080. И помните, что если вы служите / необходимо указать / С обеих сторон прокси или он начинает плакать:

ProxyPass / http://localhost:8080/

Это может быть не проблема кэширования на всех. Попробуйте увеличить параметр MaxClients в apache2.conf. Если он слишком низкий (150 по умолчанию?), Apache начинает очередь запросов. Когда он решает обслуживать запрос в очереди через mod_proxy, он вытаскивает неправильную страницу (или, может быть, это просто подчеркнуто, делая все очереди).


вы уверены, что это страница, которую кто-то просил или страница без параметров?, вы можете получить странные ошибки, если ваш connectionTimeout слишком короткий на сервере.xml на сервере tomcat за apache, увеличьте его до большего числа:

конфигурация по умолчанию:

  <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

изменено:

  <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="2000000"
               redirectPort="8443" />