Tomcat Container的管道机制:责任链模式
  • Tomcat总计架构图中Pipeline和Vavle

  • 我们在上文Engine中有一块Pipline没有解释:

  • 为什么Tomcat要引入Pipline呢?它要解决什么问题呢?

下文将向你详细阐述。

知识准备

在弄清楚管道机制前,你需要一些基础知识和其它软件设计中的应用场景。

责任链模式

管道机制在设计模式上属于责任链模式,如果你不理解,请参看如下文章:

责任链模式(Chain of responsibility pattern): 通过责任链模式, 你可以为某个请求创建一个对象链. 每个对象依序检查此请求并对其进行处理或者将它传给链中的下一个对象。

FilterChain

在软件开发的常接触的责任链模式是FilterChain,它体现在很多软件设计中:

  • 比如Spring Security框架中

  • 比如HttpServletRequest处理的过滤器中

当一个request过来的时候,需要对这个request做一系列的加工,使用责任链模式可以使每个加工组件化,减少耦合。也可以使用在当一个request过来的时候,需要找到合适的加工方式。当一个加工方式不适合这个request的时候,传递到下一个加工方法,该加工方式再尝试对request加工。

网上找了图,这里我们后文将通过Tomcat请求处理向你阐述。

Pipline机制

为什么要有管道机制?

在一个比较复杂的大型系统中,如果一个对象或数据流需要进行繁杂的逻辑处理,我们可以选择在一个大的组件中直接处理这些繁杂的业务逻辑, 这个方式虽然达到目的,但扩展性和可重用性较差, 因为可能牵一发而动全身。更好的解决方案是采用管道机制,用一条管道把多个对象(阀门部件)连接起来,整体看起来就像若干个阀门嵌套在管道中一样,而处理逻辑放在阀门上

Vavle接口设计

理解它的设计,第一步就是阀门设计

java

public interface Valve { // 因为需要传递给下个Valve处理,所以有next public Valve getNext(); public void setNext(Valve valve); // 设计这个方法,便于执行周期任务,比如重新加载组件。此方法将在该容器的类加载上下文中调用。 public void backgroundProcess(); // 这个方法很容易理解,阀门中处理的执行方法,传入Request和Response进行处理 public void invoke(Request request, Response response) throws IOException, ServletException; // 此阀门是否支持Servlet 3+ 异步的请求 public boolean isAsyncSupported(); }

Pipline接口设计

由于Pipline是为容器设计的,所以它在设计时加入了一个Containerd接口, 就是为了制定当前Pipline所属的容器:

java

public interface Contained { Container getContainer(); void setContainer(Container container); }

我们接着看下Pipline接口设计

java

public interface Pipeline extends Contained { // 基础的处理阀 public Valve getBasic(); public void setBasic(Valve valve); // 对节点(阀门)增删查 public void addValve(Valve valve); public Valve[] getValves(); public void removeValve(Valve valve); // 获取第一个节点,遍历的起点,所以需要有这方法 public Valve getFirst(); // 是否所有节点(阀门)都支持处理Servlet3异步处理 public boolean isAsyncSupported(); // 找到所有不支持Servlet3异步处理的阀门 public void findNonAsyncValves(Set<String> result); }

BaseVavle设计

由于Valve也是组件,需要生命周期管理,所以实现LifecycleMBeanBase,同时集成Contained和Valve

java

public abstract class ValveBase extends LifecycleMBeanBase implements Contained, Valve { protected static final StringManager sm = StringManager.getManager(ValveBase.class); //------------------------------------------------------ Constructor public ValveBase() { this(false); } public ValveBase(boolean asyncSupported) { this.asyncSupported = asyncSupported; } //------------------------------------------------------ Instance Variables /** * Does this valve support Servlet 3+ async requests? */ protected boolean asyncSupported; /** * The Container whose pipeline this Valve is a component of. */ protected Container container = null; /** * Container log */ protected Log containerLog = null; /** * The next Valve in the pipeline this Valve is a component of. */ protected Valve next = null; //-------------------------------------------------------------- Properties /** * Return the Container with which this Valve is associated, if any. */ @Override public Container getContainer() { return container; } /** * Set the Container with which this Valve is associated, if any. * * @param container The new associated container */ @Override public void setContainer(Container container) { this.container = container; } @Override public boolean isAsyncSupported() { return asyncSupported; } public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } /** * Return the next Valve in this pipeline, or <code>null</code> if this * is the last Valve in the pipeline. */ @Override public Valve getNext() { return next; } /** * Set the Valve that follows this one in the pipeline it is part of. * * @param valve The new next valve */ @Override public void setNext(Valve valve) { this.next = valve; } //---------------------------------------------------------- Public Methods /** * Execute a periodic task, such as reloading, etc. This method will be * invoked inside the classloading context of this container. Unexpected * throwables will be caught and logged. */ @Override public void backgroundProcess() { // NOOP by default } @Override protected void initInternal() throws LifecycleException { super.initInternal(); containerLog = getContainer().getLogger(); } /** * Start this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @Override protected synchronized void startInternal() throws LifecycleException { setState(LifecycleState.STARTING); } /** * Stop this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @Override protected synchronized void stopInternal() throws LifecycleException { setState(LifecycleState.STOPPING); } /** * Return a String rendering of this object. */ @Override public String toString() { return ToStringUtil.toString(this); } // -------------------- JMX and Registration -------------------- @Override public String getObjectNameKeyProperties() { StringBuilder name = new StringBuilder("type=Valve"); Container container = getContainer(); name.append(container.getMBeanKeyProperties()); int seq = 0; // Pipeline may not be present in unit testing Pipeline p = container.getPipeline(); if (p != null) { for (Valve valve : p.getValves()) { // Skip null valves if (valve == null) { continue; } // Only compare valves in pipeline until we find this valve if (valve == this) { break; } if (valve.getClass() == this.getClass()) { // Duplicate valve earlier in pipeline // increment sequence number seq ++; } } } if (seq > 0) { name.append(",seq="); name.append(seq); } String className = this.getClass().getName(); int period = className.lastIndexOf('.'); if (period >= 0) { className = className.substring(period + 1); } name.append(",name="); name.append(className); return name.toString(); } @Override public String getDomainInternal() { Container c = getContainer(); if (c == null) { return null; } else { return c.getDomain(); } } }

StandardPipline实现

里面方法很简单,就直接贴代码了。它必然是继承LifecycleBase同时实现Pipline.

贴个图方面你理解