【问题标题】:Tomcat session replication issueTomcat会话复制问题
【发布时间】:2016-10-07 10:42:15
【问题描述】:

TL;DR - 当主节点宕机时,sessionId 中的节点名称不会更新为备份中的当前节点名称。

Tomcat 版本 - apache-tomcat-7.0.50

我设置了两个节点(我的应用程序在 2 个单独的 Tomcat 中的 2 个实例),带有会话复制配置(也使用粘性会话)。下面是来自 server.xml 的集群配置,它位于 Engine 标签内。两个节点都类似,除了端口号:

<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"/>
        <Receiver      className="org.apache.catalina.tribes.transport.nio.NioReceiver"
        address="auto"
        port="4050"
        autoBind="100"
        selectorTimeout="5000"
        maxThreads="6"/>

        <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
            <Transport     className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
        </Sender>
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
        <Interceptor      className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>\
</Channel>

<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

从 tomcat 管理器中,我可以看到登录时正在主节点中创建会话(例如:D042A0C5E380EB9E500224C87233119C.myNode1),并在备份中正确复制。

但是,一旦主节点出现故障,我希望备份节点中的 sessionId 会更新为当前节点名称,即:D042A0C5E380EB9E500224C87233119C.myNode2

例子:

用户登录时:

Node 1 - Primary - jsessionIdSample.node1 
Node 2 - Backup - jsessionIdSample.node1 

当一个节点 1 宕机时(预期)

Node 1 - - jsessionIdSample.node1 (NODE GOES DOWN) 
Node 2 - Primary - jsessionIdSample.node2 

但是发生了什么:

Node 1 - - jsessionIdSample.node1 (NODE DOWN) 
Node 2 - Backup - jsessionIdSample.node1

我有两个问题:

1) 我的理解是否应该在主节点关闭后不久在备份中更新 sessionID 是否正确?我阅读了 tomcat 文档,似乎应该这样做。

2) 如果应该,您能帮我配置一下吗?我已经尝试过其他关于 SO 的问题的解决方案,但它们似乎都不起作用。

编辑:根据建议添加完整的引擎配置:

节点 1

    <Engine name="Catalina" defaultHost="localhost" jvmRoute="node1">
  <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
              <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"/>
        <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                  address="228.0.0.4"
                  port="4005"
                  autoBind="100"
                  selectorTimeout="5000"
                  maxThreads="6"/>

        <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
          <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
        </Sender>
        <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"/>

      <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                tempDir="/tmp/war-temp/"
                deployDir="/tmp/war-deploy/"
                watchDir="/tmp/war-listen/"
                watchEnabled="false"/>
      <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>

节点 2

    <Engine name="Catalina" defaultHost="localhost" jvmRoute="node2">
  <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
              <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"/>
        <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                  address="228.0.0.4"
                  port="4010"
                  autoBind="100"
                  selectorTimeout="5000"
                  maxThreads="6"/>

        <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
          <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
        </Sender>
        <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"/>

      <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                tempDir="/tmp/war-temp/"
                deployDir="/tmp/war-deploy/"
                watchDir="/tmp/war-listen/"
                watchEnabled="false"/>
      <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>

提前致谢!

【问题讨论】:

    标签: java session tomcat tomcat7 session-replication


    【解决方案1】:

    不确定您是否有点混淆了 - 例如。你说

    当用户登录时:

    Node 1 - Primary - jsessionIdSample.node1 
    Node 2 - Backup -  jsessionIdSample.node1
    

    节点 2 不应该使用以 .node2 结尾的会话 id 吗?

    恕我直言,使用的会话 id 来自 &lt;Engine&gt; 中的 jvmRoute 属性并粘在机器上 - 当节点 1 配置为 &lt;Engine jvmRoute="node1"&gt; 时,这就是节点 1 知道的。不幸的是,您没有在上面引用您的引擎配置。

    jvmRoute 只是对负载均衡器的提示,即路由到哪台机器,因此它必须稳定可靠。可能很简单,只需添加额外的 extra 以确保您的节点 2 配置了 jvmRoute="node2"。我从未在 tomcat 中看到任何不同的行为。

    即使在您更新问题后,我也觉得有些奇怪 - 请参阅我的答案中引用的部分,其中节点 1 和节点 2 都使用“node1”作为指示符。这可能是罪魁祸首(或被遗忘的错字)。如果你使用的是 Apache httpd,workers 的命名必须和workers.properties 中的名称一样(至少它们必须是唯一的。参见https://tomcat.apache.org/connectors-doc/reference/workers.html

    【讨论】:

    • 嗨@Olaf,感谢您的回复,我将使用引擎配置更新问题。 jvmRoute属性如下:Node 1 - node1, Node 2 - node2
    • 嗨@Olaf,我已将server.xml 中的完整引擎配置添加到问题中。我在我的 apache worker.properties 中使用粘性会话。当用户登录时,他的请求被定向到 Node1,并且所有进一步的请求继续从节点 1 提供服务。当在 node1 中创建 jsessionid 时,将其复制到 node2 (jsessionIdSample.node1)。我也尝试过没有部署应用程序,即。只需登录到 tomcat 管理器,并注意到它使用主节点的 jvmroute (jsessionIdSample.node1) 复制了会话 ID。这不应该发生吗?
    • 当 node1 关闭/失去多播成员资格时,我希望 jvmroute 更改 jsessionid。在这种情况下,node2 将是主节点,jsessionid 将更改为 jsessionIdSample.node2。这种情况正在发生,但只有在第一个请求得到处理之后。前任。当 node1 宕机时,下一个请求被发送到 node2 ,会话 id 为 jsessionIdSample.node1 (这会导致会话超时),但在服务此请求后,会话 id 更改为 jsessionIdSample.node2。我希望在 node1 失去多播组的成员身份后立即进行此更改。
    • 嗨@Olaf,我对JvmRouteSessionIDBinderListener 工作原理的理解是否正确?
    • 不看实现,我希望 node1 关闭后的第一个请求仍然携带 node1 session-id - 因为它存储在 cookie 中并且没有人更改 cookie。有了响应,可以有一个新的 Set-Cookie 指令,它将 cookie 更改为现在具有 node2 id。不过,我希望 node2 能够找到 node1 会话。我承认我通常不在集群中使用会话复制——因此如果有问题,我不会遇到它们
    猜你喜欢
    • 1970-01-01
    • 2013-12-14
    • 1970-01-01
    • 1970-01-01
    • 2011-02-12
    • 2014-07-23
    • 2013-12-09
    • 2018-01-01
    • 1970-01-01
    相关资源
    最近更新 更多