Tomcat中容器的pipeline機制


本文主要目的是講解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由ConnectorContainer兩部分組成,而當網絡請求過來的時候Connector先將請求包裝為Request,然后將Request交由Container進行處理,最終返回給請求方。而Container處理的第一層就是Engine容器,但是在tomcat中Engine容器不會直接調用Host容器去處理請求,那么請求是怎么在4個容器中流轉的,4個容器之間是怎么依次調用的,我們今天來講解下。

當請求到達Engine容器的時候,Engine並非是直接調用對應的Host去處理相關的請求,而是調用了自己的一個組件去處理,這個組件就叫做pipeline組件,跟pipeline相關的還有個也是容器內部的組件,叫做valve組件。

Pipeline的作用就如其中文意思一樣管道,可以把不同容器想象成一個獨立的個體,那么pipeline就可以理解為不同容器之間的管道,道路,橋梁。那Valve這個組件是什么東西呢?Valve也可以直接按照字面意思去理解為閥門。pipeline是通道,valve是閥門,他們兩有什么關系呢?

就像上圖那樣,每個管道上面都有閥門,PipelineValve關系也是一樣的。Valve代表管道上的閥門,可以控制管道的流向,當然每個管道上可以有多個閥門。如果把Pipeline比作公路的話,那么Valve可以理解為公路上的收費站,車代表Pipeline中的內容,那么每個收費站都會對其中的內容做一些處理(收費,查證件等)。

好了舉例說完了,我們繼續回歸tomcat。在Catalina中,我們有4種容器,每個容器都有自己的Pipeline組件,每個Pipeline組件上至少會設定一個Valve(閥門),這個Valve我們稱之為BaseValve(基礎閥)。基礎閥的作用是連接當前容器的下一個容器(通常是自己的自容器),可以說基礎閥是兩個容器之間的橋梁。

Pipeline定義對應的接口Pipeline,標准實現了StandardPipelineValve定義對應的接口Valve,抽象實現類ValveBase,4個容器對應基礎閥門分別是StandardEngineValve,StandardHostValve,StandardContextValve,StandardWrapperValve。在實際運行中Pipeline,Valve運行機制如下圖。

在單個容器中Pipeline,Valve運行圖

Catalina中Pipeline,Valve運行圖

可以看到在同一個Pipeline上可以有多個Valve,每個Valve都可以做一些操作,無論是Pipeline還是Valve操作的都是RequestResponse。而在容器之間PipelineValve則起到了橋梁的作用,那么具體內部原理是什么,我們開始查看源碼。

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機制就講那么多,其實涉及的非常基礎,最關鍵的pipelineinvoke()方法也沒有看,主要invoke()方法和其他的點比較重復,准備還是放到請求在容器中的流轉講解!敬請期待!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM