Apache + Tomcat - 粘性会话和负载平衡问题

Posted

技术标签:

【中文标题】Apache + Tomcat - 粘性会话和负载平衡问题【英文标题】:Apache + Tomcat - Problems with sticky sessions and load balancing 【发布时间】:2012-03-12 16:25:39 【问题描述】:

我在使用 Apache mod_proxy_balancer 时遇到了一些关于粘性会话的问题。

我们用 Java 开发了一个 restful web 服务,在 Tomcat 上运行。实际的后端使用 Acegi 安全性和 Auth Basic 身份验证。

架构是(对不起,我是新用户,我不能发布图片):

     --------------------
     |Java Reverse Proxy|
     --------------------
            |
     --------------------
     |Apache load balancer|
     --------------------               
            |
    --------|--------
    |               |
--------        --------    
|tomcat1|       |tomcat2|
--------        --------    

我们有这个“Java 反向代理”来执行各种业务。它还在 Tomcat(Tomcat1、Tomcat2)上进行 Basic Auth 身份验证。

最终用户调用 url 如下:http:///a/b?username=foo&password=bar&session=xxx

然后反向代理将请求代理到 Apache,将凭据作为基本身份验证令牌发送。

最终用户拥有三个不同的 url:

http://<java reverse proxy domain>/service1
http://<java reverse proxy domain>/service2
http://<java reverse proxy domain>/service3

只有 service1 和 service2 受 Acegi 保护。 service3 可以匿名访问(这是一项要求)。

我们在 Apache 中有以下配置来执行负载平衡:

<Proxy balancer://cluster>
    Header set Cache-Control no-cache
    Header set Pragma no-cache
    BalancerMember http://xxx:9671 route=server1
    BalancerMember http://xxx:9672 route=server2
</Proxy>

ProxyPreserveHost On
ProxyPass / balancer://cluster/ stickysession=JSESSIONID
ProxyPassReverse / balancer://cluster/ stickysession=JSESSIONID

在第一次调用service1时,JSESSIONID被返回给用户,然后他将这个会话信息作为请求的一部分发送(在查询字符串,会话参数中)

为了在后端 tomcat(tomcat1、tomcat2)中维护会话状态,java 反向代理从查询字符串中获取会话并将其作为 JSESSIONID cookie 发送到代理 tomcat。

对于受基本身份验证保护的 URL,一切正常。但是当用户调用第三个 url(这是公开的)时,Apache 并没有正确执行负载平衡。

例如,当我调用服务 1 或 2 时,我得到以下 Apache 日志:

[Wed Feb 22 10:48:52 2012] [debug] mod_proxy_balancer.c(280): proxy: BALANCER: Found value "3FB8F8135173BBBE78E5E4BBD6F5C8FB" for stickysession JSESSIONID
[Wed Feb 22 10:48:52 2012] [debug] mod_proxy_balancer.c(1003): proxy: Entering byrequests for BALANCER (balancer://cluster)
[Wed Feb 22 10:48:52 2012] [debug] mod_proxy_balancer.c(1046): proxy: byrequests selected worker "http://xxx:9672" : busy 0 : lbstatus 1

这很好,因为请求是针对 tomcat2。

但是当我调用 service3 时,我得到:

[Wed Feb 22 10:49:27 2012] [debug] mod_proxy_balancer.c(280): proxy: BALANCER: Found value "3FB8F8135173BBBE78E5E4BBD6F5C8FB" for stickysession JSESSIONID
[Wed Feb 22 10:49:27 2012] [debug] mod_proxy_balancer.c(1003): proxy: Entering byrequests for BALANCER (balancer://cluster)
[Wed Feb 22 10:49:27 2012] [debug] mod_proxy_balancer.c(1046): proxy: byrequests selected worker "http://xxx:9671" : busy 0 : lbstatus 0

如您所见,即使 JSESSIONID cookie 相同,Apache 也会将请求发送到错误的 tomcat(此处为 tomcat1)。

可能是因为 service3 的 url 不需要 Auth Basic 身份验证,而 service1 和 service2 需要?

我很确定我做错了什么,但我已经环顾了很长时间,但我无法让它发挥作用。

非常感谢您的帮助。

谢谢

【问题讨论】:

【参考方案1】:

我没有在您的 JSESSIONID 上看到 jvmRoute 后缀。 mod_proxy 使用 jvmRoute 将粘性会话正确路由到您的 Tomcat 实例。 jvmRoute 在您的 tomcat 服务器配置中声明(其中每个服务器实例都有自己唯一的 jvmRoute 标识符。

【讨论】:

没错,在我提供的示例中,我忘记了 jvmRoute。但即使在 server.xml 中设置也不能解决问题。【参考方案2】:

也许这会有所帮助。这是我在网络服务器上的配置:

<Proxy balancer://hybriscluster>
BalancerMember ajp://tomcatServer1:8009 route=tomcat1 keepalive=On ping=5 max=200 ttl=120
BalancerMember ajp://tomcatServer2:8009 route=tomcat2 keepalive=On ping=5 max=200 ttl=120
ProxySet stickysession=JSESSIONID|jsessionid lbmethod=byrequests timeout=60
</Proxy>

在tomcat Server 1的server.xml中配置:

<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1"">

【讨论】:

【参考方案3】:

我遇到了同样的问题并通过修改以下行解决了它 -

ProxyPass /test balancer://mycluster stickysession=JSESSIONID|jsessionid scolonpathdelim=On
<Proxy balancer://mycluster>
BalancerMember http://192.168.1.2:80 route=node1
BalancerMember http://192.168.1.3:80 route=node2
</Proxy>

请注意配置scolonpathdelim=On 参考——http://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html

【讨论】:

以上是关于Apache + Tomcat - 粘性会话和负载平衡问题的主要内容,如果未能解决你的问题,请参考以下文章

Apache和Tomcat 配置负载均衡(mod-proxy方式)-粘性session

在 tomcat 实例之间共享会话(不使用粘性会话)

生产环境中的Tomcat集群/负载均衡性能

apache server和tomcat集群配置二:垂直负载

无法在 AWS 中获得粘性会话?

粘性会话和会话复制