2010-04-21 33 views
3

在Tomcat 5.0.x中,您可以在每次请求后设置useDirtyFlag =“false”到强制复制会话,而不是检查set/removeAttribute调用。Tomcat 6集群配置有没有useDirtyFlag选项?

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

即改变已经已经放到session对象的状态和您可以确定该对象仍然被复制到集群中的其他节点。

根据Tomcat 6的文档,你只有两个“经理”选项 - DeltaManager & BackupManager ...这些都没有允许这个选项或类似的东西。在我的测试,默认设置:

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

你在哪里得到默认器DeltaManager,它肯定表现为useDirtyFlag =“真”(如我期望)。

所以我的问题是 - 有没有在Tomcat 6中的等价物?

看看源代码,我可以看到一个管理器实现“org.apache.catalina.ha.session.SimpleTcpReplicationManager”,它具有useDirtyFlag,但是在这种状态下的javadoc注释是“Tomcat 4.0的Tomcat会话复制”。 。我不知道这是否可以使用 - 我猜并不是因为它在主集群配置文档中没有提及。

回答

4

我本质上张贴的tomcat-users邮件列表上同样的问题,并在Tomcat的Bugzilla的一些信息沿着这个响应([43866])使我得出以下结论:

  1. 有没有等同于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:改变集群配置(在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设置。这将从Tomcat 5.0.x中取代replicationMode=asynchronous/synchronous/pooled。有关可能的int值,请参见cluster documentation

附录:全阀源的要求

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的代码”。这不正常吗?或者我误解了问题/答案? – 2011-10-20 09:28:09

+1

看来这条评论是在我发布这个问题之后添加的......它还说“useDirtyFlag配置参数可用于优化会话复制的次数”。这意味着默认情况下有'useDirtyFlag'关闭/ false。我看不到任何参考文献(在文档或代码中)来确认这一点 - 我自己的测试(去年)没有得到这种行为。 – kevinjansz 2011-10-23 22:45:09

+1

它看起来像Tomcat文档中的错误。 – fglez 2012-11-29 17:53:41

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

}