10

这个错误一直让我发疯。我们有一台运行 Apache 和 Tomcat 的服务器,为多个不同的站点提供服务。通常服务器运行良好,但有时会在为人们提供错误页面的地方发生错误 -其他人请求的页面!

线索:

  • 正在传递的页面是其他用户最近请求的页面,否则会正确传递。众所周知,两个同时请求被交换。据我所知,错误传递的页面都没有超过几分钟。
  • 它只影响 Tomcat 提供的文件。图像等静态文件不受影响。
  • 它不会一直发生。当它发生时,它会发生在每个人身上。
  • 它似乎发生在需求高峰期。然而,需求还不是很高——它肯定在 Apache 可以应付的范围内。
  • 重新启动 Tomcat 修复它,但只有几分钟。重新启动 Apache 修复了它,但只有几分钟。
  • 服务器运行 Apache 2 和 Tomcat 6,在 Gentoo 上使用 Java 6 VM。与 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、参数、会话 cookie 等等。人们正在返回显示“您以 John 身份登录”的页面,而他们显然不是。


更新:

根据几个人的建议,我将向 Tomcat 服务的页面添加以下 HTTP 标头以禁用所有形式的缓存:

Cache-Control: no-store
Vary: *

希望这些标头不仅会受到 Apache 的尊重,而且还会受到任何其他可能阻碍的缓存或代理的尊重。不幸的是,我没有办法故意重现这个错误,所以我只能等着看它是否会再次出现。

我注意到包含以下标题 - 它们可以以任何方式相关吗?

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

更新:

显然这在我睡着的时候又发生了,但现在已经停止发生了,我醒着看到它。同样,我可以看到日志中没有任何有用的信息,因此我不知道实际发生了什么或如何防止它。

是否有任何额外的信息可以放入 Apache 或 Tomcat 的日志中以使其更易于诊断?


更新:

由于这种情况再次发生了几次,我们改变了 Apache 连接到 Tomcat 的方式,看看它是否会影响事情。我们使用mod_jk这样的指令:

JkMount /portal ajp13

我们现在切换到 using 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。


更新:

该死!问题刚刚回来。我有一段时间没看到它了,所以我开始认为我们终于把它整理好了。我讨厌海森虫。

4

11 回答 11

6

这可能是您的 servlet 的线程安全性吗?

您的 servlet 是否在实例成员中存储任何信息。

例如,像下面这样简单的事情可能会导致与线程相关的问题:

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 是由多个线程访问的,所以不能保证 action 实例成员不会被其他人的请求破坏,最终返回错误的页面。

这个问题的简单解决方案是在实例成员中使用局部变量:

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 Pages,如果您为您的视图分派给它们?

于 2008-10-29T12:14:51.370 回答
3

检查您的标头是否允许在没有正确VaryHTTP 标头的情况下进行缓存(例如,如果您使用会话 cookie 并允许缓存,则您需要在VaryHTTP 标头中为 cookie 标头输入一个条目,或者缓存/代理可能会提供缓存版本的一个用户到另一个用户的页面)。

问题可能不在于您的 Web 服务器上的缓存,而在于另一层缓存(在您的 Web 服务器前面的反向代理上,或在用户附近的代理上)。如果客户端是 NAT,它们也可能位于透明代理之后(并且,为了使调试更加困难,透明代理可能被配置为在标头中不可见)。

于 2008-10-29T23:00:11.927 回答
2

问题的 8 次更新稍后再使用一个问题来测试/复制,尽管对于公共站点来说可能很困难(或昂贵)。

您可以在网站上启用 https。这至少会清除沿途的任何其他代理缓存。看到有一些被遗忘的负载均衡器或公司缓存会干扰您的流量,这将是很糟糕的。

对于公共站点,这意味着密钥上的受信任证书,因此会涉及一些资金。对于测试自签名密钥可能就足够了。此外,请检查是否不涉及解密和重新加密流量的透明代理。(它们很容易被检测到,因为它们不能使用与原始服务器相同的证书/密钥)

于 2009-04-19T07:06:09.640 回答
2

尽管您确实提到在您的设置中未启用 mod_cache,但对于其他可能在启用 mod_cache 时遇到相同问题的其他人(即使在静态内容上),解决方案是确保在 Set-Cookie HTTP 标头上启用以下指令:

CacheIgnoreHeaders Set-Cookie

mod_cache 的原因将缓存可能提供给其他用户的 Set-Cookie 标头。然后,这会将会话 ID 从上次填充缓存的用户泄漏给另一个用户。

于 2011-01-24T19:25:15.983 回答
1

我遇到了这个问题,它真的让我发疯了。我不知道为什么,但我解决了它关闭 http.conf 上的 Keep Alive

保持活动状态

保持活动关闭

我的应用程序没有使用 keepalive 功能,所以它对我来说效果很好。

于 2009-01-26T07:08:57.463 回答
1

试试这个:

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
于 2009-04-19T05:59:35.807 回答
1

看看这个网站,它描述了 mod_jk 的一个问题。在查看一个非常相似的问题时,我偶然发现了您的帖子。基本上解决方法是升级到较新版本的 mod_jk。我还没有机会在我们的服务器中实施更改,但是我明天会尝试一下,看看它是否有帮助。

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

于 2009-06-17T01:42:52.900 回答
0

我不是专家,但这可能是一些奇怪的网络地址转换问题吗?

于 2008-10-29T17:28:30.520 回答
0

我们将 Apache 从使用 AJP 代理切换到使用 HTTP 代理。到目前为止,它似乎已经解决了这个问题,或者至少大大减少了它 - 几个月来没有报告过这个问题,并且从那时起该应用程序的使用量有所增加。

更改在 Apache 的 httpd.conf 中。开始于mod_jk

JkMount /portal ajp13

我们切换到mod_proxy_ajp

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

然后终于直截了当mod_proxy

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

您需要确保将 Tomcat 设置为在端口 8080 上提供 HTTP 服务。请记住,如果您正在服务/,则需要/在代理的两侧都包含,否则它会开始哭泣:

ProxyPass / http://localhost:8080/
于 2009-11-16T17:49:16.460 回答
0

这可能根本不是缓存问题。尝试增加 apache2.conf 中的 MaxClients 参数。如果它太低(默认为 150?),Apache 开始对请求进行排队。当它决定通过 mod_proxy 为排队的请求提供服务时,它会拉出一个错误的页面(或者可能只是在进行所有排队时压力很大)。

于 2010-03-26T20:35:43.100 回答
0

你确定这是其他人请求的页面还是没有参数的页面?如果你的connectionTimeout在apache后面的tomcat服务器上的server.xml上太短,你可能会得到奇怪的错误,将其增加到更大的数字:

默认配置:

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

改变:

  <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="2000000"
               redirectPort="8443" />
于 2012-07-07T14:11:30.480 回答