是否有用于Tomcat 6群集配置的useDirtyFlag选项?

在Tomcat 5.0.x中,您可以设置useDirtyFlag =“false”以在每次请求后强制复制会话,而不是检查set / removeAttribute调用。

 ... 

server.xml中的注释表明这可以用于进行以下工作:

  

即更改已放入会话的对象的状态,并确保此对象仍可复制到群集中的其他节点。

根据Tomcat 6文档,您只有两个“管理器”选项 – DeltaManager和BackupManager ……这些选项似乎都不允许此选项或任何类似选项。 在我的测试中默认设置:

   

默认情况下,你得到DeltaManager,它肯定表现为useDirtyFlag =“true”(正如我所料)。

所以我的问题是 – Tomcat 6中是否有相同的内容?

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

我在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 names = deltaSession.getAttributeNames(); names.hasMoreElements(); ) { String name = names.nextElement(); deltaSession.setAttribute(name, deltaSession.getAttribute(name)); } } 

第2步:更改集群配置(在conf/server.xml

        

现在,每次请求后都会将会话复制到所有群集节点。

旁白:注意channelSendOptions设置。 这将替换Tomcat 5.0.x中的replicationMode=asynchronous/synchronous/pooled poo。 有关可能的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; /** * 

With the {@link SimpleTcpReplicationManager} effectively deprecated, this allows * mutable objects to be replicated in the cluster by forcing the "dirty" status on * every request.

* * @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 this should be // org.apache.catalina.ha.session.DeltaSession - implements HttpSession HttpSession deltaSession = (HttpSession) session; for (Enumeration 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"); } } }

非常感谢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; /** * 

With the {@link SimpleTcpReplicationManager} effectively deprecated, this allows * mutable objects to be replicated in the cluster by forcing the "dirty" status on * every request.

* * @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 cycledNames = new LinkedList(); // in a cluster where the app is this should be // org.apache.catalina.ha.session.DeltaSession - implements HttpSession HttpSession deltaSession = (HttpSession) session; for (Enumeration 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"); } } }