2010-04-21 2 views
3

Dans Tomcat 5.0.x, vous pouviez définir useDirtyFlag = "false" sur pour forcer la réplication de la session après chaque requête plutôt que de rechercher les appels set/removeAttribute.Existe-t-il une option useDirtyFlag pour la configuration du cluster 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"> ... 

Les commentaires dans le server.xml a déclaré ceci peut être utilisé pour faire les travaux suivants:

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

-à-dire changer l'état d'un objet qui a déjà été mis à la session et vous pouvez être sûr que cet objet est toujours répliqué sur les autres nœuds du cluster.

Selon la documentation de Tomcat 6, vous n'avez que deux options "Manager" - DeltaManager & BackupManager ... aucun de ces éléments ne semble autoriser cette option. Dans mon test de la configuration par défaut:

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

où vous obtenez le DeltaManager par défaut, il se comporte certainement comme useDirtyFlag = « true » (comme je pense).

Donc, ma question est: y a-t-il un équivalent dans Tomcat 6?

En regardant la source, je peux voir une implémentation de gestionnaire "org.apache.catalina.ha.session.SimpleTcpReplicationManager" qui a le useDirtyFlag mais les commentaires de javadoc dans cet état c'est "Tomcat Session Replication for Tomcat 4.0". Je ne sais pas si c'est bon à utiliser - je ne pense pas que ce n'est pas mentionné dans la documentation de la configuration du cluster principal.

Répondre

4

Je posté essentiellement la même question sur la tomcat-users liste de diffusion et les réponses à ce ainsi que quelques informations dans le bugzilla tomcat ([43866]) m'a conduit aux conclusions suivantes:

  1. Il y a pas équivalent à useDirtyFlag, si vous mettez des objets mutables (c'est-à-dire qui changent) dans la session, vous avez besoin d'une solution codée personnalisée. Un Tomcat ClusterValve semble être un endroit efficace pour cette solution - branchez-vous dans le mécanisme de cluster, manipulez les attributs pour faire apparaître à DeltaManager que tous les attributs de la session ont changé. Cela force la réplication de toute la session.

Etape 1: Ecrire les ForceReplicationValve (Prolonge ValveBase met en œuvre ClusterValve)

Je ne vais pas inclure toute la classe, mais le bit clé de la logique (prenant l'enregistrement et la vérification 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)); 
    } 
} 

Étape 2: Modifier la configuration du cluster (en 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> 

La réplication de la session sur tous les nœuds de cluster se produit désormais après chaque requête.

À côté: Notez le paramètre channelSendOptions. Cela remplace le replicationMode=asynchronous/synchronous/pooled de Tomcat 5.0.x.Voir le cluster documentation pour les valeurs int possibles.

Annexe: source complet Valve comme demandé

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

Le ([comment à la page] http://tomcat.apache.org/tomcat-6.0-doc/cluster- howto.html) dit "Pour chaque requête, toute la session est répliquée, cela permet au code qui modifie les attributs de la session sans appeler setAttribute ou removeAttribute d'être répliqué". Cela ne fonctionne-t-il pas ou ai-je mal compris la question/réponse? –

+1

Il semble que ce commentaire ait été ajouté après que j'ai posté cette question ... il dit aussi "un paramètre de configuration useDirtyFlag peut être utilisé pour optimiser le nombre de fois qu'une session est répliquée". Cela implique qu'il existe un 'useDirtyFlag' désactivé/false par défaut. Je ne vois aucune référence (dans les docs ou le code) pour confirmer ceci - mes propres tests (l'année dernière) n'ont pas eu ce comportement. – kevinjansz

+1

Cela ressemble à une erreur dans la documentation Tomcat. – fglez

2

Un grand merci à kevinjansz pour fournir la source de ForceReplicationValve.

J'ajusté pour tomcat7, ici il est si quelqu'un en a besoin:

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"); 
     } 
    } 

} 
Questions connexes