本文主要目的是講解tomcat中的pipeline機制,涉及部分源碼分析
之前我們在前面的文章介紹過,tomcat中Container有4種,分別是Engine,Host,Context,Wrapper,這4個Container的實現類分別是StandardEngine,StandardHost,StandardContext,StandardWrapper。4種容器的關系是包含關系,Engine包含Host,Host包含Context,Context包含Wrapper,Wrapper則代表最基礎的一個Servlet。
之前在tomcat架構簡述那篇文章中介紹過,tomcat由Connector和Container兩部分組成,而當網絡請求過來的時候Connector先將請求包裝為Request,然后將Request交由Container進行處理,最終返回給請求方。而Container處理的第一層就是Engine容器,但是在tomcat中Engine容器不會直接調用Host容器去處理請求,那么請求是怎么在4個容器中流轉的,4個容器之間是怎么依次調用的,我們今天來講解下。
當請求到達Engine容器的時候,Engine並非是直接調用對應的Host去處理相關的請求,而是調用了自己的一個組件去處理,這個組件就叫做pipeline組件,跟pipeline相關的還有個也是容器內部的組件,叫做valve組件。
Pipeline的作用就如其中文意思一樣管道,可以把不同容器想象成一個獨立的個體,那么pipeline就可以理解為不同容器之間的管道,道路,橋梁。那Valve這個組件是什么東西呢?Valve也可以直接按照字面意思去理解為閥門。pipeline是通道,valve是閥門,他們兩有什么關系呢?

就像上圖那樣,每個管道上面都有閥門,Pipeline和Valve關系也是一樣的。Valve代表管道上的閥門,可以控制管道的流向,當然每個管道上可以有多個閥門。如果把Pipeline比作公路的話,那么Valve可以理解為公路上的收費站,車代表Pipeline中的內容,那么每個收費站都會對其中的內容做一些處理(收費,查證件等)。
好了舉例說完了,我們繼續回歸tomcat。在Catalina中,我們有4種容器,每個容器都有自己的Pipeline組件,每個Pipeline組件上至少會設定一個Valve(閥門),這個Valve我們稱之為BaseValve(基礎閥)。基礎閥的作用是連接當前容器的下一個容器(通常是自己的自容器),可以說基礎閥是兩個容器之間的橋梁。
Pipeline定義對應的接口Pipeline,標准實現了StandardPipeline。Valve定義對應的接口Valve,抽象實現類ValveBase,4個容器對應基礎閥門分別是StandardEngineValve,StandardHostValve,StandardContextValve,StandardWrapperValve。在實際運行中Pipeline,Valve運行機制如下圖。
在單個容器中Pipeline,Valve運行圖

Catalina中Pipeline,Valve運行圖

可以看到在同一個Pipeline上可以有多個Valve,每個Valve都可以做一些操作,無論是Pipeline還是Valve操作的都是Request和Response。而在容器之間Pipeline和Valve則起到了橋梁的作用,那么具體內部原理是什么,我們開始查看源碼。
Valve
public interface Valve {
public String getInfo();
public Valve getNext();
public void setNext(Valve valve);
public void backgroundProcess();
public void invoke(Request request, Response response) throws IOException, ServletException;
public void event(Request request, Response response, CometEvent event) throws IOException,ServletException;
public boolean isAsyncSupported();
}
先看Valve接口的方法定義,方法不是很多,這里只介紹setNext(),getNext()。在上面我們也看到了一個Pipeline上面可以有很多Valve,這些Valve存放的方式並非統一存放在Pipeline中,而是像一個鏈表一個接着一個。當你獲取到一個Valve實例的時候,調用getNext()方法即可獲取在這個Pipeline上的下個Valve實例。
Pipeline
//pipeline 接口
public interface Pipeline {
public Valve getBasic();
public void setBasic(Valve valve);
public void addValve(Valve valve);
public Valve[] getValves();
public void removeValve(Valve valve);
public Valve getFirst();
public boolean isAsyncSupported();
public Container getContainer();
public void setContainer(Container container);
}
可以看出Pipeline中很多的方法都是操作Valve的,包括獲取,設置,移除Valve,getFirst()返回的是Pipeline上的第一個Valve,而getBasic(),setBasic()則是獲取/設置基礎閥,我們都知道在Pipeline中,每個pipeline至少都有一個閥門,叫做基礎閥,而getBasic(),setBasic()則是操作基礎閥的。
StandardPipeline
public class StandardPipeline extends LifecycleBase implements Pipeline, Contained {
private static final Log log = LogFactory.getLog(StandardPipeline.class);
// ----------------------------------------------------------- Constructors
public StandardPipeline() {
this(null);
}
public StandardPipeline(Container container) {
super();
setContainer(container);
}
// ----------------------------------------------------- Instance Variables
protected Valve basic = null;
protected Container container = null;
protected static final String info = "org.apache.catalina.core.StandardPipeline/1.0";
protected Valve first = null;
//1111111111
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}
setState(LifecycleState.STARTING);
}
// ------------------------------------------------------- Pipeline Methods
//2222222222222222222222
@Override
public void setBasic(Valve valve) {
// Change components if necessary
Valve oldBasic = this.basic;
if (oldBasic == valve)
return;
// Stop the old component if necessary
if (oldBasic != null) {
if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {
try {
((Lifecycle) oldBasic).stop();
} catch (LifecycleException e) {
log.error("StandardPipeline.setBasic: stop", e);
}
}
if (oldBasic instanceof Contained) {
try {
((Contained) oldBasic).setContainer(null);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
}
}
// Start the new component if necessary
if (valve == null)
return;
if (valve instanceof Contained) {
((Contained) valve).setContainer(this.container);
}
if (getState().isAvailable() && valve instanceof Lifecycle) {
try {
((Lifecycle) valve).start();
} catch (LifecycleException e) {
log.error("StandardPipeline.setBasic: start", e);
return;
}
}
// Update the pipeline
Valve current = first;
while (current != null) {
if (current.getNext() == oldBasic) {
current.setNext(valve);
break;
}
current = current.getNext();
}
this.basic = valve;
}
//3333333333333333333
@Override
public void addValve(Valve valve) {
// Validate that we can add this Valve
if (valve instanceof Contained)
((Contained) valve).setContainer(this.container);
// Start the new component if necessary
if (getState().isAvailable()) {
if (valve instanceof Lifecycle) {
try {
((Lifecycle) valve).start();
} catch (LifecycleException e) {
log.error("StandardPipeline.addValve: start: ", e);
}
}
}
// Add this Valve to the set associated with this Pipeline
if (first == null) {
first = valve;
valve.setNext(basic);
} else {
Valve current = first;
while (current != null) {
if (current.getNext() == basic) {
current.setNext(valve);
valve.setNext(basic);
break;
}
current = current.getNext();
}
}
container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
}
//44444444444
@Override
public Valve[] getValves() {
ArrayList<Valve> valveList = new ArrayList<Valve>();
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
valveList.add(current);
current = current.getNext();
}
return valveList.toArray(new Valve[0]);
}
//5555555555555555
@Override
public void removeValve(Valve valve) {
Valve current;
if(first == valve) {
first = first.getNext();
current = null;
} else {
current = first;
}
while (current != null) {
if (current.getNext() == valve) {
current.setNext(valve.getNext());
break;
}
current = current.getNext();
}
if (first == basic) first = null;
if (valve instanceof Contained)
((Contained) valve).setContainer(null);
if (valve instanceof Lifecycle) {
// Stop this valve if necessary
if (getState().isAvailable()) {
try {
((Lifecycle) valve).stop();
} catch (LifecycleException e) {
log.error("StandardPipeline.removeValve: stop: ", e);
}
}
try {
((Lifecycle) valve).destroy();
} catch (LifecycleException e) {
log.error("StandardPipeline.removeValve: destroy: ", e);
}
}
container.fireContainerEvent(Container.REMOVE_VALVE_EVENT, valve);
}
//666666666666
@Override
public Valve getFirst() {
if (first != null) {
return first;
}
return basic;
}
}
在StandardPipeline標准實現類中我們看到了對Pipeline接口的實現,我們選了幾個比較重要的方法做源碼的解析。
方法1是startInternal()
//1111111111
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}
setState(LifecycleState.STARTING);
}
組件的start()方法,將first(第一個閥門)賦值給current變量,如果current為空,就將basic(也就是基礎閥)賦值給current,接下來如果一個標准的遍歷單向鏈表,調用每個對象的start()方法,最后將組件(pipeline)狀態設置為STARTING(啟動中)。
方法2
//2222222222222222222222
@Override
public void setBasic(Valve valve) {
// Change components if necessary
//如果已經有基礎閥(basic已經有值並且跟要設置的值一樣)那么直接return
Valve oldBasic = this.basic;
if (oldBasic == valve)
return;
// Stop the old component if necessary
//舊的基礎閥非空 那么調用其stop方法取消和對應container的關聯。(銷毀舊的基礎閥)
if (oldBasic != null) {
if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {
try {
((Lifecycle) oldBasic).stop();
} catch (LifecycleException e) {
log.error("StandardPipeline.setBasic: stop", e);
}
}
if (oldBasic instanceof Contained) {
try {
((Contained) oldBasic).setContainer(null);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
}
}
// Start the new component if necessary
//非空判斷
if (valve == null)
return;
//和Container進行關聯
if (valve instanceof Contained) {
((Contained) valve).setContainer(this.container);
}
//啟動新的閥門
if (getState().isAvailable() && valve instanceof Lifecycle) {
try {
((Lifecycle) valve).start();
} catch (LifecycleException e) {
log.error("StandardPipeline.setBasic: start", e);
return;
}
}
//遍歷閥門鏈表將新的閥門取代舊的閥門
// Update the pipeline
Valve current = first;
while (current != null) {
if (current.getNext() == oldBasic) {
current.setNext(valve);
break;
}
current = current.getNext();
}
//將基礎閥設置為新的閥門
this.basic = valve;
}
方法2是用來設置基礎閥的方法,這個方法在每個容器的構造函數中調用,代碼邏輯也比較簡單,稍微注意的地方就是閥門鏈表的遍歷。
方法3
//3333333333333333333
@Override
public void addValve(Valve valve) {
// Validate that we can add this Valve
// 驗證Valve 關聯Container
if (valve instanceof Contained)
((Contained) valve).setContainer(this.container);
// Start the new component if necessary
// 驗證組件狀態,如果對的話 啟動需要添加的Valve,調用start方法。
if (getState().isAvailable()) {
if (valve instanceof Lifecycle) {
try {
((Lifecycle) valve).start();
} catch (LifecycleException e) {
log.error("StandardPipeline.addValve: start: ", e);
}
}
}
//如果 first變量為空,將valve賦值給first變量,並且設置 valve的下一個閥門為基礎閥
//之所以這樣是因為,如果first為空說明這個容器只有一個基礎閥,所以此次添加的閥門肯定是第一個非基礎閥閥門
// Add this Valve to the set associated with this Pipeline
if (first == null) {
first = valve;
valve.setNext(basic);
} else {
//否則 遍歷閥門鏈表,將要被添加的閥門設置在 基礎閥之前。
Valve current = first;
while (current != null) {
if (current.getNext() == basic) {
current.setNext(valve);
valve.setNext(basic);
break;
}
current = current.getNext();
}
}
//container觸發添加閥門事件
container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
}
這方法是像容器中添加Valve,在server.xml解析的時候也會調用該方法,具體代碼可以到Digester相關的文章中尋找。
方法4
//44444444444
@Override
public Valve[] getValves() {
ArrayList<Valve> valveList = new ArrayList<Valve>();
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
valveList.add(current);
current = current.getNext();
}
return valveList.toArray(new Valve[0]);
}
獲取所有的閥門,其實就是將閥門鏈表添加到一個集合內,最后轉成數組返回。
方法5
//5555555555555555
@Override
public void removeValve(Valve valve) {
Valve current;
//如果first 是需要被移除的valve 那么將first的下一個閥門賦值給first
//並且current 賦值null,否則current 賦值first
if(first == valve) {
first = first.getNext();
current = null;
} else {
current = first;
}
//遍歷閥門鏈表 查找需要被移除的閥門
//如果之前first是被移除的話 current = null是不會進入該循環
while (current != null) {
if (current.getNext() == valve) {
current.setNext(valve.getNext());
break;
}
current = current.getNext();
}
//如果first(此時已經指向下一個閥門)此時 == 基礎閥,那么first置空
//從這里可以看出來 first指的是第一個閥門,即使整個container只有一個基礎閥門也不會指向基礎閥。
//first嚴格定義是 除了基礎閥的第一個閥門。
if (first == basic) first = null;
//驗證需要被移除的閥門 取消container關聯
if (valve instanceof Contained)
((Contained) valve).setContainer(null);
//調用閥門的生命周期 stop destroy 方法。
if (valve instanceof Lifecycle) {
// Stop this valve if necessary
if (getState().isAvailable()) {
try {
((Lifecycle) valve).stop();
} catch (LifecycleException e) {
log.error("StandardPipeline.removeValve: stop: ", e);
}
}
try {
((Lifecycle) valve).destroy();
} catch (LifecycleException e) {
log.error("StandardPipeline.removeValve: destroy: ", e);
}
}
//觸發container的移除valve事件。
container.fireContainerEvent(Container.REMOVE_VALVE_EVENT, valve);
}
方法666666
//666666666666
@Override
public Valve getFirst() {
if (first != null) {
return first;
}
return basic;
}
在方法5中我們也看到了,first指向的是容器第一個非基礎閥門的閥門,從方法6中也可以看出來,first在只有一個基礎閥的時候並不會指向基礎閥,因為如果指向基礎閥的話就不需要判斷非空然后返回基礎閥了,這是個需要注意的點!
關於tomcat的pipeline機制就講那么多,其實涉及的非常基礎,最關鍵的pipeline的invoke()方法也沒有看,主要invoke()方法和其他的點比較重復,准備還是放到請求在容器中的流轉講解!敬請期待!
