在 Apache HTTP Server 2.2.21 和 Tomcat 7.0.23 上使用 mod_proxy_ajp 实现负载平衡和集群

在 Apache HTTP Server 2.2.21 和 Tomcat 7.0.23 上使用 mod_proxy_ajp 实现负载平衡和集群

我一直在努力使用这些组合来实现负载平衡和集群工作:

  • Apache HTTP Server 2.2.21(httpd-2.2.21-win32-x86-openssl-0.9.8r)使用 mod_proxy_ajp 并启用了粘性会话。
  • Apache Tomcat 7.0.23(apache-tomcat-7.0.23-windows-x64)
  • JDK 7 更新 2(jdk-7u2-windows-x64)
  • Windows 7 64 位
  • Spring 3.1

我读过的一些链接:

这是我的配置:

httpd配置文件

# Required Modules
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule status_module modules/mod_status.so

# Reverse Proxy
<Proxy balancer://mybalancer>
    BalancerMember ajp://localhost:8301 route=s1
    BalancerMember ajp://localhost:8302 route=s2
    BalancerMember ajp://localhost:8303 route=s3
</Proxy>
ProxyPass / balancer://mybalancer/ stickysession=JSESSIONID|jsessionid

# Forward Proxy
ProxyRequests Off

<Proxy *>
    Order deny,allow
    Deny from none
    Allow from localhost
</Proxy>

# Balancer-manager, for monitoring
<Location /balancer-manager>
    SetHandler balancer-manager

    Order deny,allow
    Deny from none
    Allow from localhost
</Location> 

每个 tomcat 的 server.xml(仅在端口号上有所不同)

<Server port="8001" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
    <Listener className="org.apache.catalina.core.JasperListener" />
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

    <GlobalNamingResources>
        <Resource name="UserDatabase" auth="Container"
            type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved"
            factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" />
    </GlobalNamingResources>

    <Service name="Catalina">
        <Connector port="8101" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8201" />

        <Connector port="8301" protocol="AJP/1.3" redirectPort="8201" />

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

            <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
                <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" 
                    notifyListenersOnReplication="true" />
                <Channel className="org.apache.catalina.tribes.group.GroupChannel">
                    <Membership className="org.apache.catalina.tribes.membership.McastService"
                        address="228.0.0.4" port="45564" frequency="500" dropTime="3000" />
                    <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
                        <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" />
                    </Sender>
                    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                        address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6" />
                    <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
                    <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor" />
                </Channel>
                <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter="" />
                <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
                <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener" />
                <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" />
            </Cluster> 

            <Realm className="org.apache.catalina.realm.LockOutRealm">
                <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" />
            </Realm>
            <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
                <Valve className="org.apache.catalina.valves.AccessLogValve"
                    directory="logs" prefix="localhost_access_log." suffix=".txt"
                    pattern="%h %l %u %t &quot;%r&quot; %s %b" />
            </Host>
        </Engine>
    </Service>

</Server>

端口编号(如果部署在同一台服务器中,请确保没有实例使用相同的端口)

  • 80 -> Apache HTTP 服务器端口
  • 80xx -> Tomcat 服务器关闭端口
  • 81xx -> Tomcat 连接器端口 (HTTP)
  • 82xx -> Tomcat SSL 重定向端口
  • 83xx -> Tomcat AJP 端口
  • 40xx -> NioReceiver 的 Tomcat tcp 接收端口

Spring 控制器

@Controller
@RequestMapping("/login")
public class LoginController {
    @RequestMapping(method=RequestMethod.GET)
    public String show(@ModelAttribute("user") User user, HttpServletRequest request) {
        user.setUsername("YUSUF");

        HttpSession session = request.getSession();
        Integer tambah = (Integer) session.getAttribute("tambah");
        if(tambah == null) tambah = new Integer(1);
        else tambah = new Integer(tambah.intValue() + 1);
        session.setAttribute("tambah", tambah);

        return "login";
    }
}

登录页面

    <div class="mainFooter">
        Tambah = ${sessionScope.tambah}
        <br>
        ID = ${pageContext.session.id}
    </div>

到目前为止,负载平衡部分正在运行,但会话复制尚未运行。
基本上,我想要的是,如果我在登录页面上不断按刷新,变量“tambah”将递增并保留在会话中。如果当前 tomcat 节点关闭,会话将被复制到下一个 tomcat 节点,数据不会消失。但发生了以下情况:

登录屏幕:

Tambah = 39 
ID = C1D59C8CA5D10EB98C1DE08AC618204D.s1 

我关闭了 tomcat1 并保持 tomcat2 和 tomcat3 运行,这是登录屏幕:

Tambah = 1
ID = A83KJFO38FK30FJDL40FLREI39FKDKGD.s2

似乎故障转移不起作用,会话未复制,并且应用程序创建了新会话。有人能帮我指出正确的方向吗?

谢谢

编辑:我已经解决了这个问题,感谢@Shane Madden,这是我所做的更改:

<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                        address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6" />

对于每个 Tomcat,tcp 接收端口必须不同(如果在同一台服务器上运行),例如使用 4001、4002、4003 等。

答案1

感谢@Shane Madden的评论,我重新阅读了 tomcat 集群文档http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html,尤其是在这一部分 -> “如果您的 Tomcat 实例在同一台机器上运行,请确保每个实例的 tcpListenPort 属性都是唯一的,在大多数情况下,Tomcat 足够智能,可以通过自动检测 4000-4100 范围内的可用端口自行解决这个问题”。

我所做的更改是在每个 Tomcat 实例的 server.xml 中,确保每个端口都不同(例如 4001、4002、4003):

<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"  
  address="auto" port="4001" autoBind="100" 
  selectorTimeout="5000" maxThreads="6" />

瞧!负载平衡和集群正在工作(当然,使用最基本的配置)。我希望这篇文章可以帮助其他人完成初始设置。

答案2

我有一个类似的软件堆栈(apache 2.2.15 和 tomcat 7.0.65),但所有 linux 和我都遇到了平衡器状态页面的以下问题:ProxyPass 指令没有进入状态页面

ProxyPass / balancer://mybalancer/ stickysession=JSESSIONID|jsessionid

将 /balancer-manager 请求重定向到其中一个 tomcat(因此收到 tomvat 错误页面)。我通过将 ProxyPass 设置为比 / 更严格的内容来解决了这个问题

ProxyPass /myapp balancer://mybalancer/myapp stickysession=JSESSIONID|jsessionid

但不确定这是否是正确的解决方法?

相关内容