2010-04-21 2 views
3

В Tomcat 5.0.x у вас была возможность установить useDirtyFlag = "false" на принудительное повторение сеанса после каждого запроса вместо проверки на вызовы set/removeAttribute.Есть ли опция useDirtyFlag для конфигурации кластера Tomcat 6?

<Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster" 
       managerClassName="org.apache.catalina.cluster.session.SimpleTcpReplicationManager" 
       expireSessionsOnShutdown="false" 
       **useDirtyFlag="false"** 
       doClusterLog="true" 
       clusterLogName="clusterLog"> ... 

Комментарии в server.xml заявил, что может быть использовано, чтобы сделать следующую работу:

<% 
    HashMap map = (HashMap)session.getAttribute("map"); 
    map.put("key","value"); 
%> 

т.е. изменить состояние объекта, который уже положили в сессии и вы можете быть уверены, что этот объект все еще реплицируется на другие узлы кластера.

В соответствии с документацией Tomcat 6 у вас есть только две опции «Менеджер» - DeltaManager & BackupManager ... ни один из них не позволяет эту опцию или что-то в этом роде. В ходе тестирования настройки по умолчанию:

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> 

, где вы получите DeltaManager по умолчанию, это, безусловно, ведет себя, как useDirtyFlag = «истина» (как я ожидал).

Так что мой вопрос - есть ли эквивалент в Tomcat 6?

Глядя на источник, я вижу реализацию менеджера «org.apache.catalina.ha.session.SimpleTcpReplicationManager», который имеет использование DirtyFlag, но комментарии javadoc в этом состоянии это «репликация сеанса Tomcat для Tomcat 4.0». Я не знаю, нормально ли это использовать - я не думаю, что это не упоминается в документации по конфигурации основного кластера.

ответ

4

я отправил по существу один и тот же вопрос, на кот-пользователи в списке рассылки и ответы на это наряду с некоторой информацией в TOMCAT Bugzilla ([43866]) привел меня к следующим выводам:

  1. Eсти нет эквивалента useDirtyFlag, если вы добавляете изменяемые (т.е. изменяющиеся) объекты в сеансе, вам нужно специальное кодированное решение.
  2. Tomcat ClusterValve, похоже, является подходящим местом для этого решения - подключился к механизму кластера, манипулировал атрибутами, чтобы он отображался в DeltaManager, который изменил все атрибуты сеанса. Это принудительно реплицирует весь сеанс.

Шаг 1: Написать ForceReplicationValve (расширяет ValveBase орудия ClusterValve)

я не буду включать весь класс, но ключ немного логики (вынимая протоколирование и InstanceOf проверки):

@Override 
public void invoke(Request request, Response response) 
     throws IOException, ServletException { 
    getNext().invoke(request, response); 
    Session session = request.getSessionInternal();   
    HttpSession deltaSession = (HttpSession) session; 
    for (Enumeration<String> names = deltaSession.getAttributeNames(); 
      names.hasMoreElements();) { 
     String name = names.nextElement(); 
     deltaSession.setAttribute(name, deltaSession.getAttribute(name)); 
    } 
} 

Шаг 2: Alter кластера конфигурации (в conf/server.xml)

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" 
      channelSendOptions="8">   
    <Valve className="org.apache.catalina.ha.tcp.ForceReplicationValve"/> 
    <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" 
      filter=".*\.gif;.*\.jpg;.*\.png;.*\.js;.*\.htm;.*\.html;.*\.txt;.*\.css;"/> 
    <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> 

Репликация сеанса всем узлам кластера будет происходить после каждого запроса.

Помимо этого: обратите внимание на установку channelSendOptions. Это заменяет replicationMode=asynchronous/synchronous/pooled от Tomcat 5.0.x.См. cluster documentation для возможных значений int.

Приложение: источник Полный клапан по запросу

package org.apache.catalina.ha.tcp; 

import java.io.IOException; 
import java.util.Enumeration; 
import java.util.LinkedList; 
import java.util.List; 

import javax.servlet.ServletException; 
import javax.servlet.http.HttpSession; 

import org.apache.catalina.Lifecycle; 
import org.apache.catalina.LifecycleException; 
import org.apache.catalina.LifecycleListener; 
import org.apache.catalina.Session; 
import org.apache.catalina.connector.Request; 
import org.apache.catalina.connector.Response; 
import org.apache.catalina.ha.CatalinaCluster; 
import org.apache.catalina.ha.ClusterValve; 
import org.apache.catalina.ha.session.ReplicatedSession; 
import org.apache.catalina.ha.session.SimpleTcpReplicationManager; 
import org.apache.catalina.util.LifecycleSupport; 
//import org.apache.catalina.util.StringManager; 
import org.apache.catalina.valves.ValveBase; 

/** 
* <p>With the {@link SimpleTcpReplicationManager} effectively deprecated, this allows 
* mutable objects to be replicated in the cluster by forcing the "dirty" status on 
* every request.</p> 
* 
* @author Jon Brisbin (via post on tomcat-users http://markmail.org/thread/rdo3drcir75dzzrq) 
* @author Kevin Jansz 
*/ 
public class ForceReplicationValve extends ValveBase implements Lifecycle, ClusterValve { 
    private static org.apache.juli.logging.Log log = 
     org.apache.juli.logging.LogFactory.getLog(ForceReplicationValve.class); 

    @SuppressWarnings("hiding") 
    protected static final String info = "org.apache.catalina.ha.tcp.ForceReplicationValve/1.0"; 

// this could be used if ForceReplicationValve messages were setup 
// in org/apache/catalina/ha/tcp/LocalStrings.properties 
//  
// /** 
//  * The StringManager for this package. 
//  */ 
// @SuppressWarnings("hiding") 
// protected static StringManager sm = 
//  StringManager.getManager(Constants.Package); 

    /** 
    * Not actually required but this must implement {@link ClusterValve} to 
    * be allowed to be added to the Cluster. 
    */ 
    private CatalinaCluster cluster = null ; 

    /** 
    * Also not really required, implementing {@link Lifecycle} to allow 
    * initialisation and shutdown to be logged. 
    */ 
    protected LifecycleSupport lifecycle = new LifecycleSupport(this);  


    /** 
    * Default constructor 
    */ 
    public ForceReplicationValve() { 
     super(); 
     if (log.isInfoEnabled()) { 
      log.info(getInfo() + ": created"); 
     } 
    } 

    @Override 
    public String getInfo() { 
     return info; 
    } 

    @Override 
    public void invoke(Request request, Response response) throws IOException, 
      ServletException { 

     getNext().invoke(request, response); 

     Session session = null; 
     try { 
      session = request.getSessionInternal(); 
     } catch (Throwable e) { 
      log.error(getInfo() + ": Unable to perform replication request.", e); 
     } 

     String context = request.getContext().getName(); 
     String task = request.getPathInfo(); 
     if(task == null) { 
      task = request.getRequestURI(); 
     } 
     if (session != null) { 
      if (log.isDebugEnabled()) { 
       log.debug(getInfo() + ": [session=" + session.getId() + ", instanceof=" + session.getClass().getName() + ", context=" + context + ", request=" + task + "]"); 
      } 
      if (session instanceof ReplicatedSession) { 
       // it's a SimpleTcpReplicationManager - can just set to dirty 
       ((ReplicatedSession) session).setIsDirty(true); 
       if (log.isDebugEnabled()) { 
        log.debug(getInfo() + ": [session=" + session.getId() + ", context=" + context + ", request=" + task + "] maked DIRTY"); 
       } 
      } else { 
       // for everything else - cycle all attributes 
       List cycledNames = new LinkedList(); 

       // in a cluster where the app is <distributable/> this should be 
       // org.apache.catalina.ha.session.DeltaSession - implements HttpSession 
       HttpSession deltaSession = (HttpSession) session; 
       for (Enumeration<String> names = deltaSession.getAttributeNames(); names.hasMoreElements();) { 
        String name = names.nextElement(); 
        deltaSession.setAttribute(name, deltaSession.getAttribute(name)); 

        cycledNames.add(name);      
       } 

       if (log.isDebugEnabled()) { 
        log.debug(getInfo() + ": [session=" + session.getId() + ", context=" + context + ", request=" + task + "] cycled atrributes=" + cycledNames + ""); 
       } 
      } 
     } else { 
      String id = request.getRequestedSessionId(); 
      log.warn(getInfo() + ": [session=" + id + ", context=" + context + ", request=" + task + "] Session not available, unable to send session over cluster."); 
     } 
    } 


    /* 
    * ClusterValve methods - implemented to ensure this valve is not ignored by Cluster 
    */ 

    public CatalinaCluster getCluster() { 
     return cluster; 
    } 

    public void setCluster(CatalinaCluster cluster) { 
     this.cluster = cluster; 
    } 


    /* 
    * Lifecycle methods - currently implemented just for logging startup 
    */ 

    /** 
    * Add a lifecycle event listener to this component. 
    * 
    * @param listener The listener to add 
    */ 
    public void addLifecycleListener(LifecycleListener listener) { 
     lifecycle.addLifecycleListener(listener); 
    } 

    /** 
    * Get the lifecycle listeners associated with this lifecycle. If this 
    * Lifecycle has no listeners registered, a zero-length array is returned. 
    */ 
    public LifecycleListener[] findLifecycleListeners() { 
     return lifecycle.findLifecycleListeners(); 
    } 

    /** 
    * Remove a lifecycle event listener from this component. 
    * 
    * @param listener The listener to remove 
    */ 
    public void removeLifecycleListener(LifecycleListener listener) { 
     lifecycle.removeLifecycleListener(listener); 
    } 

    public void start() throws LifecycleException { 
     lifecycle.fireLifecycleEvent(START_EVENT, null); 
     if (log.isInfoEnabled()) { 
      log.info(getInfo() + ": started"); 
     } 
    } 

    public void stop() throws LifecycleException { 
     lifecycle.fireLifecycleEvent(STOP_EVENT, null); 
     if (log.isInfoEnabled()) { 
      log.info(getInfo() + ": stopped"); 
     } 
    } 

} 
+0

[как к странице] (http://tomcat.apache.org/tomcat-6.0-doc/cluster- howto.html) говорит: «Для каждого запроса весь сеанс реплицируется, это позволяет использовать код, который изменяет атрибуты в сеансе без вызова setAttribute или removeAttribute для репликации». Разве это не работает или я не понял вопрос/ответ? –

+1

Кажется, что этот комментарий был добавлен после того, как я разместил этот вопрос ... он также говорит: «параметр конфигурации useDirtyFlag может использоваться для оптимизации количества раз, когда реплицируется сеанс». Это означает, что по умолчанию используется параметр 'useDirtyFlag', который отключен/false. Я не вижу никакой ссылки (в документах или коде), чтобы подтвердить это, хотя мое собственное тестирование (в прошлом году) не получило такого поведения. – kevinjansz

+1

Это похоже на ошибку в документации Tomcat. – fglez

2

Большое спасибо kevinjansz для обеспечения источника для ForceReplicationValve.

я приспособил его для Tomcat7, здесь это, если кто нуждается в этом:

package org.apache.catalina.ha.tcp; 

import java.io.IOException; 
import java.util.Enumeration; 
import java.util.LinkedList; 
import java.util.List; 

import javax.servlet.ServletException; 
import javax.servlet.http.HttpSession; 

import org.apache.catalina.Lifecycle; 
import org.apache.catalina.LifecycleException; 
import org.apache.catalina.LifecycleListener; 
import org.apache.catalina.Session; 
import org.apache.catalina.connector.Request; 
import org.apache.catalina.connector.Response; 
import org.apache.catalina.ha.CatalinaCluster; 
import org.apache.catalina.ha.ClusterValve; 
import org.apache.catalina.util.LifecycleSupport; 
import org.apache.catalina.valves.ValveBase; 
import org.apache.catalina.LifecycleState; 
// import org.apache.tomcat.util.res.StringManager; 

/** 
* <p>With the {@link SimpleTcpReplicationManager} effectively deprecated, this allows 
* mutable objects to be replicated in the cluster by forcing the "dirty" status on 
* every request.</p> 
* 
* @author Jon Brisbin (via post on tomcat-users http://markmail.org/thread/rdo3drcir75dzzrq) 
* @author Kevin Jansz 
*/ 
public class ForceReplicationValve extends ValveBase implements Lifecycle, ClusterValve { 
    private static org.apache.juli.logging.Log log = 
     org.apache.juli.logging.LogFactory.getLog(ForceReplicationValve.class); 

    @SuppressWarnings("hiding") 
    protected static final String info = "org.apache.catalina.ha.tcp.ForceReplicationValve/1.0"; 

// this could be used if ForceReplicationValve messages were setup 
// in org/apache/catalina/ha/tcp/LocalStrings.properties 
//  
// /** 
//  * The StringManager for this package. 
//  */ 
// @SuppressWarnings("hiding") 
// protected static StringManager sm = 
//  StringManager.getManager(Constants.Package); 

    /** 
    * Not actually required but this must implement {@link ClusterValve} to 
    * be allowed to be added to the Cluster. 
    */ 
    private CatalinaCluster cluster = null; 

    /** 
    * Also not really required, implementing {@link Lifecycle} to allow 
    * initialisation and shutdown to be logged. 
    */ 
    protected LifecycleSupport lifecycle = new LifecycleSupport(this);  


    /** 
    * Default constructor 
    */ 
    public ForceReplicationValve() { 
     super(); 
     if (log.isInfoEnabled()) { 
      log.info(getInfo() + ": created"); 
     } 
    } 

    @Override 
    public String getInfo() { 
     return info; 
    } 

    @Override 
    public void invoke(Request request, Response response) throws IOException, 
      ServletException { 

     getNext().invoke(request, response); 

     Session session = null; 
     try { 
      session = request.getSessionInternal(); 
     } catch (Throwable e) { 
      log.error(getInfo() + ": Unable to perform replication request.", e); 
     } 

     String context = request.getContext().getName(); 
     String task = request.getPathInfo(); 
     if(task == null) { 
      task = request.getRequestURI(); 
     } 
     if (session != null) { 
      if (log.isDebugEnabled()) { 
       log.debug(getInfo() + ": [session=" + session.getId() + ", instanceof=" + session.getClass().getName() + ", context=" + context + ", request=" + task + "]"); 
      } 
      //cycle all attributes 
      List<String> cycledNames = new LinkedList<String>(); 

      // in a cluster where the app is <distributable/> this should be 
      // org.apache.catalina.ha.session.DeltaSession - implements HttpSession 
      HttpSession deltaSession = (HttpSession) session; 
      for (Enumeration<String> names = deltaSession.getAttributeNames(); names.hasMoreElements();) { 
       String name = names.nextElement(); 
       deltaSession.setAttribute(name, deltaSession.getAttribute(name)); 

       cycledNames.add(name);      
      } 

      if (log.isDebugEnabled()) { 
       log.debug(getInfo() + ": [session=" + session.getId() + ", context=" + context + ", request=" + task + "] cycled atrributes=" + cycledNames + ""); 
      } 
     } else { 
      String id = request.getRequestedSessionId(); 
      log.warn(getInfo() + ": [session=" + id + ", context=" + context + ", request=" + task + "] Session not available, unable to send session over cluster."); 
     } 
    } 


    /* 
    * ClusterValve methods - implemented to ensure this valve is not ignored by Cluster 
    */ 

    public CatalinaCluster getCluster() { 
     return cluster; 
    } 

    public void setCluster(CatalinaCluster cluster) { 
     this.cluster = cluster; 
    } 


    /* 
    * Lifecycle methods - currently implemented just for logging startup 
    */ 

    /** 
    * Add a lifecycle event listener to this component. 
    * 
    * @param listener The listener to add 
    */ 
    public void addLifecycleListener(LifecycleListener listener) { 
     lifecycle.addLifecycleListener(listener); 
    } 

    /** 
    * Get the lifecycle listeners associated with this lifecycle. If this 
    * Lifecycle has no listeners registered, a zero-length array is returned. 
    */ 
    public LifecycleListener[] findLifecycleListeners() { 
     return lifecycle.findLifecycleListeners(); 
    } 

    /** 
    * Remove a lifecycle event listener from this component. 
    * 
    * @param listener The listener to remove 
    */ 
    public void removeLifecycleListener(LifecycleListener listener) { 
     lifecycle.removeLifecycleListener(listener); 
    } 

    protected synchronized void startInternal() throws LifecycleException { 
     setState(LifecycleState.STARTING); 
     if (log.isInfoEnabled()) { 
      log.info(getInfo() + ": started"); 
     } 
    } 

    protected synchronized void stopInternal() throws LifecycleException { 
     setState(LifecycleState.STOPPING); 
     if (log.isInfoEnabled()) { 
      log.info(getInfo() + ": stopped"); 
     } 
    } 

}