Tomcat中容器的pipeline机制
本文主要目的是講解tomcat中的pipeline機(jī)制,涉及部分源碼分析
之前我們?cè)谇懊娴奈恼陆榻B過(guò),tomcat中Container有4種,分別是Engine,Host,Context,Wrapper,這4個(gè)Container的實(shí)現(xiàn)類分別是StandardEngine,StandardHost,StandardContext,StandardWrapper。4種容器的關(guān)系是包含關(guān)系,Engine包含Host,Host包含Context,Context包含Wrapper,Wrapper則代表最基礎(chǔ)的一個(gè)Servlet。
之前在tomcat架構(gòu)簡(jiǎn)述那篇文章中介紹過(guò),tomcat由Connector和Container兩部分組成,而當(dāng)網(wǎng)絡(luò)請(qǐng)求過(guò)來(lái)的時(shí)候Connector先將請(qǐng)求包裝為Request,然后將Request交由Container進(jìn)行處理,最終返回給請(qǐng)求方。而Container處理的第一層就是Engine容器,但是在tomcat中Engine容器不會(huì)直接調(diào)用Host容器去處理請(qǐng)求,那么請(qǐng)求是怎么在4個(gè)容器中流轉(zhuǎn)的,4個(gè)容器之間是怎么依次調(diào)用的,我們今天來(lái)講解下。
當(dāng)請(qǐng)求到達(dá)Engine容器的時(shí)候,Engine并非是直接調(diào)用對(duì)應(yīng)的Host去處理相關(guān)的請(qǐng)求,而是調(diào)用了自己的一個(gè)組件去處理,這個(gè)組件就叫做pipeline組件,跟pipeline相關(guān)的還有個(gè)也是容器內(nèi)部的組件,叫做valve組件。
Pipeline的作用就如其中文意思一樣管道,可以把不同容器想象成一個(gè)獨(dú)立的個(gè)體,那么pipeline就可以理解為不同容器之間的管道,道路,橋梁。那Valve這個(gè)組件是什么東西呢?Valve也可以直接按照字面意思去理解為閥門(mén)。pipeline是通道,valve是閥門(mén),他們兩有什么關(guān)系呢?
就像上圖那樣,每個(gè)管道上面都有閥門(mén),Pipeline和Valve關(guān)系也是一樣的。Valve代表管道上的閥門(mén),可以控制管道的流向,當(dāng)然每個(gè)管道上可以有多個(gè)閥門(mén)。如果把Pipeline比作公路的話,那么Valve可以理解為公路上的收費(fèi)站,車代表Pipeline中的內(nèi)容,那么每個(gè)收費(fèi)站都會(huì)對(duì)其中的內(nèi)容做一些處理(收費(fèi),查證件等)。
好了舉例說(shuō)完了,我們繼續(xù)回歸tomcat。在Catalina中,我們有4種容器,每個(gè)容器都有自己的Pipeline組件,每個(gè)Pipeline組件上至少會(huì)設(shè)定一個(gè)Valve(閥門(mén)),這個(gè)Valve我們稱之為BaseValve(基礎(chǔ)閥)。基礎(chǔ)閥的作用是連接當(dāng)前容器的下一個(gè)容器(通常是自己的自容器),可以說(shuō)基礎(chǔ)閥是兩個(gè)容器之間的橋梁。
Pipeline定義對(duì)應(yīng)的接口Pipeline,標(biāo)準(zhǔn)實(shí)現(xiàn)了StandardPipeline。Valve定義對(duì)應(yīng)的接口Valve,抽象實(shí)現(xiàn)類ValveBase,4個(gè)容器對(duì)應(yīng)基礎(chǔ)閥門(mén)分別是StandardEngineValve,StandardHostValve,StandardContextValve,StandardWrapperValve。在實(shí)際運(yùn)行中Pipeline,Valve運(yùn)行機(jī)制如下圖。
在單個(gè)容器中Pipeline,Valve運(yùn)行圖
Catalina中Pipeline,Valve運(yùn)行圖
可以看到在同一個(gè)Pipeline上可以有多個(gè)Valve,每個(gè)Valve都可以做一些操作,無(wú)論是Pipeline還是Valve操作的都是Request和Response。而在容器之間Pipeline和Valve則起到了橋梁的作用,那么具體內(nèi)部原理是什么,我們開(kāi)始查看源碼。
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()。在上面我們也看到了一個(gè)Pipeline上面可以有很多Valve,這些Valve存放的方式并非統(tǒng)一存放在Pipeline中,而是像一個(gè)鏈表一個(gè)接著一個(gè)。當(dāng)你獲取到一個(gè)Valve實(shí)例的時(shí)候,調(diào)用getNext()方法即可獲取在這個(gè)Pipeline上的下個(gè)Valve實(shí)例。
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的,包括獲取,設(shè)置,移除Valve,getFirst()返回的是Pipeline上的第一個(gè)Valve,而getBasic(),setBasic()則是獲取/設(shè)置基礎(chǔ)閥,我們都知道在Pipeline中,每個(gè)pipeline至少都有一個(gè)閥門(mén),叫做基礎(chǔ)閥,而getBasic(),setBasic()則是操作基礎(chǔ)閥的。
StandardPipeline
public class StandardPipeline extends LifecycleBase implements Pipeline, Contained {private static final Log log = LogFactory.getLog(StandardPipeline.class);// ----------------------------------------------------------- Constructorspublic StandardPipeline() {this(null); }public StandardPipeline(Container container) {super();setContainer(container); }// ----------------------------------------------------- Instance Variablesprotected 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 anyValve 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 necessaryValve oldBasic = this.basic;if (oldBasic == valve)return;// Stop the old component if necessaryif (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 necessaryif (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 pipelineValve 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 Valveif (valve instanceof Contained)((Contained) valve).setContainer(this.container);// Start the new component if necessaryif (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 Pipelineif (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 necessaryif (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@Overridepublic Valve getFirst() {if (first != null) {return first;}return basic;} }在StandardPipeline標(biāo)準(zhǔn)實(shí)現(xiàn)類中我們看到了對(duì)Pipeline接口的實(shí)現(xiàn),我們選了幾個(gè)比較重要的方法做源碼的解析。
方法1是startInternal()
//1111111111 @Override protected synchronized void startInternal() throws LifecycleException {// Start the Valves in our pipeline (including the basic), if anyValve 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(第一個(gè)閥門(mén))賦值給current變量,如果current為空,就將basic(也就是基礎(chǔ)閥)賦值給current,接下來(lái)如果一個(gè)標(biāo)準(zhǔn)的遍歷單向鏈表,調(diào)用每個(gè)對(duì)象的start()方法,最后將組件(pipeline)狀態(tài)設(shè)置為STARTING(啟動(dòng)中)。
方法2
//2222222222222222222222 @Override public void setBasic(Valve valve) {// Change components if necessary //如果已經(jīng)有基礎(chǔ)閥(basic已經(jīng)有值并且跟要設(shè)置的值一樣)那么直接return Valve oldBasic = this.basic; if (oldBasic == valve)return;// Stop the old component if necessary //舊的基礎(chǔ)閥非空 那么調(diào)用其stop方法取消和對(duì)應(yīng)container的關(guān)聯(lián)。(銷毀舊的基礎(chǔ)閥) 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進(jìn)行關(guān)聯(lián) if (valve instanceof Contained) {((Contained) valve).setContainer(this.container); } //啟動(dòng)新的閥門(mén) if (getState().isAvailable() && valve instanceof Lifecycle) {try {((Lifecycle) valve).start();} catch (LifecycleException e) {log.error("StandardPipeline.setBasic: start", e);return;} } //遍歷閥門(mén)鏈表將新的閥門(mén)取代舊的閥門(mén) // Update the pipeline Valve current = first; while (current != null) {if (current.getNext() == oldBasic) {current.setNext(valve);break;}current = current.getNext(); } //將基礎(chǔ)閥設(shè)置為新的閥門(mén) this.basic = valve;}方法2是用來(lái)設(shè)置基礎(chǔ)閥的方法,這個(gè)方法在每個(gè)容器的構(gòu)造函數(shù)中調(diào)用,代碼邏輯也比較簡(jiǎn)單,稍微注意的地方就是閥門(mén)鏈表的遍歷。
方法3
//3333333333333333333 @Override public void addValve(Valve valve) {// Validate that we can add this Valve// 驗(yàn)證Valve 關(guān)聯(lián)Containerif (valve instanceof Contained)((Contained) valve).setContainer(this.container);// Start the new component if necessary// 驗(yàn)證組件狀態(tài),如果對(duì)的話 啟動(dòng)需要添加的Valve,調(diào)用start方法。if (getState().isAvailable()) {if (valve instanceof Lifecycle) {try {((Lifecycle) valve).start();} catch (LifecycleException e) {log.error("StandardPipeline.addValve: start: ", e);}}}//如果 first變量為空,將valve賦值給first變量,并且設(shè)置 valve的下一個(gè)閥門(mén)為基礎(chǔ)閥//之所以這樣是因?yàn)?#xff0c;如果first為空說(shuō)明這個(gè)容器只有一個(gè)基礎(chǔ)閥,所以此次添加的閥門(mén)肯定是第一個(gè)非基礎(chǔ)閥閥門(mén)// Add this Valve to the set associated with this Pipelineif (first == null) {first = valve;valve.setNext(basic);} else {//否則 遍歷閥門(mén)鏈表,將要被添加的閥門(mén)設(shè)置在 基礎(chǔ)閥之前。Valve current = first;while (current != null) {if (current.getNext() == basic) {current.setNext(valve);valve.setNext(basic);break;}current = current.getNext();}}//container觸發(fā)添加閥門(mén)事件container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve); }這方法是像容器中添加Valve,在server.xml解析的時(shí)候也會(huì)調(diào)用該方法,具體代碼可以到Digester相關(guān)的文章中尋找。
方法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]);}獲取所有的閥門(mén),其實(shí)就是將閥門(mén)鏈表添加到一個(gè)集合內(nèi),最后轉(zhuǎn)成數(shù)組返回。
方法5
//5555555555555555 @Override public void removeValve(Valve valve) {Valve current;//如果first 是需要被移除的valve 那么將first的下一個(gè)閥門(mén)賦值給first//并且current 賦值null,否則current 賦值firstif(first == valve) {first = first.getNext();current = null;} else {current = first;}//遍歷閥門(mén)鏈表 查找需要被移除的閥門(mén)//如果之前first是被移除的話 current = null是不會(huì)進(jìn)入該循環(huán)while (current != null) {if (current.getNext() == valve) {current.setNext(valve.getNext());break;}current = current.getNext();}//如果first(此時(shí)已經(jīng)指向下一個(gè)閥門(mén))此時(shí) == 基礎(chǔ)閥,那么first置空//從這里可以看出來(lái) first指的是第一個(gè)閥門(mén),即使整個(gè)container只有一個(gè)基礎(chǔ)閥門(mén)也不會(huì)指向基礎(chǔ)閥。//first嚴(yán)格定義是 除了基礎(chǔ)閥的第一個(gè)閥門(mén)。if (first == basic) first = null;//驗(yàn)證需要被移除的閥門(mén) 取消container關(guān)聯(lián)if (valve instanceof Contained)((Contained) valve).setContainer(null);//調(diào)用閥門(mén)的生命周期 stop destroy 方法。if (valve instanceof Lifecycle) {// Stop this valve if necessaryif (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);}}//觸發(fā)container的移除valve事件。container.fireContainerEvent(Container.REMOVE_VALVE_EVENT, valve); }方法666666
//666666666666 @Override public Valve getFirst() {if (first != null) {return first;}return basic; }在方法5中我們也看到了,first指向的是容器第一個(gè)非基礎(chǔ)閥門(mén)的閥門(mén),從方法6中也可以看出來(lái),first在只有一個(gè)基礎(chǔ)閥的時(shí)候并不會(huì)指向基礎(chǔ)閥,因?yàn)槿绻赶蚧A(chǔ)閥的話就不需要判斷非空然后返回基礎(chǔ)閥了,這是個(gè)需要注意的點(diǎn)!
關(guān)于tomcat的pipeline機(jī)制就講那么多,其實(shí)涉及的非常基礎(chǔ),最關(guān)鍵的pipeline的invoke()方法也沒(méi)有看,主要invoke()方法和其他的點(diǎn)比較重復(fù),準(zhǔn)備還是放到請(qǐng)求在容器中的流轉(zhuǎn)講解!敬請(qǐng)期待!
轉(zhuǎn)載于:https://www.cnblogs.com/coldridgeValley/p/5816414.html
總結(jié)
以上是生活随笔為你收集整理的Tomcat中容器的pipeline机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 图片懒加载解决方案 lazyload.j
- 下一篇: 1.常用字符对象方法