Проблема репликации сеанса Tomcat

TL; DR - имя узла в sessionId не обновляется до текущего имени узла в резервной копии, когда основной выходит из строя.

Версия Tomcat - apache-tomcat-7.0.50

У меня настроено два узла (2 экземпляра моего приложения в 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) Правильно ли я понимаю, что идентификатор сеанса должен быть обновлен в резервной копии вскоре после выхода из строя основного узла? Я прочитал документы 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>

Заранее спасибо!


person bub    schedule 07.10.2016    source источник


Ответы (1)


Не уверен, что вы немного путаете - например, ты говоришь

Когда пользователь входит в систему:

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

Разве Узлу 2 не следует использовать идентификатор сеанса, заканчивающийся на .node2?

Используемый идентификатор сеанса IMHO исходит из атрибута jvmRoute в <Engine> и прикрепляется к машине - когда узел 1 настроен с <Engine jvmRoute="node1">, это то, что знает узел 1. К сожалению, вы не цитируете конфигурацию вашего двигателя выше.

JvmRoute - это просто подсказка для балансировщика нагрузки, к какой машине следует выполнять маршрутизацию, поэтому она должна быть стабильной и надежной. Это может быть так же просто, как дополнительно дополнительно убедиться, что на вашем Узле 2 настроен jvmRoute="node2". Я никогда не видел другого поведения в tomcat.

Даже после того, как вы обновили вопрос, у меня возникло ощущение, что что-то странное - см. Цитируемую часть моего ответа, где Узел 1 и Узел 2 используют «узел1» в качестве индикатора. Это может быть виноватым (или забытой опечаткой). Рабочие, если вы используете Apache httpd, должны быть названы так же, как их имена в worker.properties (по крайней мере, они должны быть уникальными. См. https://tomcat.apache.org/connectors-doc/reference/worker.html)

person Olaf Kock    schedule 02.11.2016
comment
Привет @Olaf, спасибо за ответ, я обновлю вопрос конфигурацией движка. Атрибут jvmRoute имеет следующий вид: Узел 1 - узел1, Узел 2 - узел2. - person bub; 02.11.2016
comment
Привет, @Olaf, я добавил полную конфигурацию движка в server.xml к вопросу. Я использую липкий сеанс в моем apache worker.properties. Когда пользователь входит в систему, его запрос направляется на узел Node1, и все последующие запросы продолжают обслуживаться с узла 1. Когда jsessionid создается в узле node1, он реплицируется на узел2 (jsessionIdSample.node1). Я также пробовал это без развернутого приложения, т.е. просто войдя в диспетчер tomcat и заметив, что он реплицировал идентификатор сеанса с jvmroute основного узла (jsessionIdSample.node1). Разве этого не должно быть? - person bub; 02.11.2016
comment
Я ожидаю, что jvmroute изменит jsessionid, когда node1 отключен / теряет членство в многоадресной рассылке. В этом случае node2 будет основным, а jsessionid будет изменен на jsessionIdSample.node2. Это происходит, но только ПОСЛЕ обслуживания первого запроса. Бывший. Когда node1 выходит из строя, следующий запрос отправляется на node2 с идентификатором сеанса jsessionIdSample.node1 (что вызывает тайм-аут сеанса), но после обслуживания этого запроса идентификатор сеанса изменяется на jsessionIdSample.node2. Я ожидаю, что это изменение будет сделано, как только node1 потеряет членство в группе многоадресной рассылки. - person bub; 02.11.2016
comment
Привет, @Olaf, правильно ли я понимаю, как работает JvmRouteSessionIDBinderListener? - person bub; 09.11.2016
comment
Не глядя на реализацию, я бы ожидал, что первый запрос после выключения node1 по-прежнему будет содержать идентификатор сеанса node1 - потому что он хранится в cookie, и никто еще не изменил cookie. В ответе может быть новая директива Set-Cookie, которая изменит cookie, чтобы теперь иметь идентификатор node2. Я бы ожидал, что node2 найдет сеанс node1. Я признаю, что обычно я не использую репликацию сеансов в кластерах - поэтому, если есть проблемы, я с ними не сталкиваюсь. - person Olaf Kock; 09.11.2016
comment
@bub: проблема была решена, поскольку я столкнулся с той же проблемой? - person Harsh Kanakhara; 25.04.2017
comment
@HarshKanakhara: нет, это не решено. Скажите, пожалуйста, удалось ли вам решить эту проблему. Если я правильно понимаю, JvmRouteSessionIDBinderListener должен обновлять имя маршрута в идентификаторе сеанса, когда активный сеанс отключается, но это происходит не сразу, что приводит к аннулированию активного сеанса :( - person bub; 02.05.2017
comment
@bub: ошибка все еще остается. Я все еще не понимаю, как работает JvmRouteSessionIDBinderListener? - person Harsh Kanakhara; 02.05.2017
comment
@HarshKanakhara, то же самое здесь :(. Пожалуйста, поделитесь этим вопросом, чтобы мы могли найти ответ. - person bub; 04.05.2017