Apache / Tomcat错误 - 正在传递错误的页面

时间:2008-10-29 12:03:34

标签: apache tomcat ajp

这个错误一直让我疯狂。我们有一台运行Apache和Tomcat的服务器,服务于多个不同的站点。通常情况下,服务器运行正常,但有时会在人们被错误的页面提供错误 - 其他人要求的页面

线索:

  • 正在递送的页面是另一个用户最近请求的页面,否则将正确传递。众所周知,要同时交换两个请求。据我所知,没有错误传送的页面超过几分钟。
  • 它仅影响Tomcat提供的文件。像图像这样的静态文件不受影响。
  • 它不会一直发生。当它发生时,它会发生在每个人身上。
  • 似乎在需求高峰期发生。然而,需求还不是很高 - 它肯定在Apache可以应付的范围内。
  • 重新启动Tomcat修复它,但只有几分钟。重新启动Apache修复它,但只有几分钟。
  • 服务器在Gentoo上使用Java 6 VM运行Apache 2和Tomcat 6。连接是使用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同样没有启用缓存选项,我可以找到。

toolkit's suggestion很好,但在这种情况下不合适。是什么让我相信错误不在我自己的代码中是因为它不仅仅是一些正在传输的值 - 它是整个请求,包括URL,参数,会话cookie,整个事情。人们正在回复说“你以约翰身份登录”,当他们显然不是。


更新

根据几个人的建议,我将向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

我们现在切换到使用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 个答案:

答案 0 :(得分:5)

它可能是你的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是由多个线程访问的,所以不能保证动作实例成员不会被某个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 Pages吗?

答案 1 :(得分:3)

检查您的标头是否允许缓存而没有正确的Vary HTTP标头(例如,如果您使用会话cookie,并允许缓存,则需要在Cookie标头的Vary HTTP标头中输入一个条目或者缓存/代理可以为一个用户提供给另一个用户的页面的缓存版本。)

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

答案 2 :(得分:2)

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

您可以在网站上启用https。这至少会消除任何其他代理缓存。如果发现有一些被遗忘的负载均衡器或公司缓存干扰您的流量,那就太糟糕了。

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

答案 3 :(得分:2)

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

CacheIgnoreHeaders Set-Cookie

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

答案 4 :(得分:1)

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

KeepAlive On

KeepAlive Off

我的应用程序不使用keepalive功能,所以它对我来说非常好。

答案 5 :(得分: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

答案 6 :(得分:1)

看看这个网站,它描述了mod_jk的一个问题。在查看一个非常类似的问题时,我遇到了你的帖子。基本上修复是升级到较新版本的mod_jk。我还没有机会在我们的服务器上实现更改,但我明天会尝试这个,看它是否有帮助。

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

答案 7 :(得分:0)

我不是专家,但可能是一些奇怪的Network Address Translation问题吗?

答案 8 :(得分: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/

答案 9 :(得分:0)

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

答案 10 :(得分: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" />