本文主要目的是講解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()
方法和其他的點比較重復,准備還是放到請求在容器中的流轉講解!敬請期待!