Tomcat容器結構及Pipeline機制 -我們到底能走多遠系列(13)

來源:互聯網
上載者:User
 我們到底能走多遠系列(13)

扯淡:

  有機會有時間的話,我是會看那個職來職往的電視節目,個人覺得比其他一些娛樂節目對自己有協助一些,我主要關注的還是現在人們的價值觀,因為只有瞭解別人的價值觀,也就是別人想要的,才可以更好的和他們溝通交流,從而達到目的。想必大家都聽過,勵志牛人說過:想要別人給你想要的,談論他想要的。

  有時候,“慎言”是很好的習慣,多聆聽,多觀察,就像《聞香識女人》中的台詞一樣:day we stop looking, is the day we die.

主題:

  直接學習tomcat的valve好像有點突兀,所以還是先去瞭解下tomcat的一個核心的組件container
container從上一個組件connector手上接過解析好的內部request,根據request來進行一系列的邏輯操作,直到調用到請求的servlet,然後組裝好response,返回給clint。
  整個大流程還是清晰明了的。畢竟我們大概瞭解了tomcat的輸入和輸出也能猜出它裡面的一些必要的操作。

先來看看container的分類吧:
Engine
Host
Context
Wrapper
它們各自的實作類別分別是StandardEngine, StandardHost, StandardContext, and StandardWrapper,他們都在tomcat的org.apache.catalina.core包下。

它們之間的關係,可以查看tomcat的server.xml也能明白(根據節點父子關係),這麼比喻吧:除了Wrapper最小,不能包含其他container外,Context內可以有零或多個Wrapper,Host可以擁有零或多個Host,Engine可以有零到多個Host。

在tomcat6裡這些Standard 的 container都是直接繼承抽象類別:org.apache.catalina.core.ContainerBase:

 

ContainerBase這個類中的一些方法名我們可以看到各個container之間包含和被包含的操作是如何?的,這些都是他提供的方法:

public void addChild(Container child)public Container findChild(String name)public Container[] findChildren()public ObjectName[] getChildren()public Container getParent()public ObjectName getParentName()public void removeChild(Container child)

當然一個container的實現是複雜的,先跳過,未來定有機會回來學習的。

Pipeline的機制:

它的結構和實現是非常值得我們學習和借鑒的。

來看看來自網路的流程圖(很不錯的圖):

多好的圖啊,單看圖你就可以瞭解大概的tomcat啟動並執行情況了,哈哈。

首先要瞭解的是每一種container都有一個自己的StandardValve
上面四個container對應的四個是:
StandardEngineValve
StandardContextValve
StandardHostValve
StandardWrapperValve

有人把vavle比作filter,而Pipeline比作filter chain,其實已經很恰當了。這裡就說下我的理解:

開端:
在CoyoteAdapter的service方法裡,由下面這一句就進入Container的。
connector.getContainer().getPipeline().getFirst().invoke(request, response);  
是的,這就是進入container迷宮的大門,歡迎來到Container。

Pipeline(注意上面結構圖的pipeline的連線):

Pipeline就像一個工廠中的生產線,負責調配工人(valve)的位置,valve則是生產線上負責不同操作的工人。
一個生產線的完成需要兩步:
1,把原料運到工人邊上
2,工人完成自己負責的部分

而tomcat的Pipeline實現是這樣的:
1,在生產線上的第一個工人拿到生產原料後,二話不說就人給下一個工人,下一個工人模仿第一個工人那樣扔給下一個工人,直到最後一個工人,而最後一個工人被安排為上面提過的StandardValve,他要完成的工作居然是把生產資料運給自己包含的container的Pipeline上去。
2,四個container就相當於有四個生產線(Pipeline),四個Pipeline都這麼幹,直到最後的StandardWrapperValve拿到資源開始調用servlet。完成後返回來,一步一步的valve按照剛才丟生產原料是的順序的倒序一次執行。如此才完成了tomcat的Pipeline的機制。

結束:

所有的vavle執行完畢後,整個響應的也就結束了。

關於Pipeline我們來看下源碼:

一個StandardValve

來自org.apache.catalina.core.StandardEngineValve的invoke方法:

public final void invoke(Request request, Response response)        throws IOException, ServletException {        // 拿到自己包含的host        Host host = request.getHost();        if (host == null) {            response.sendError                (HttpServletResponse.SC_BAD_REQUEST,                 sm.getString("standardEngine.noHost",                               request.getServerName()));            return;        }        // 最為自己pipeline上最後一位工人,負責把原料運給系一個pipeline        // 這是把所有pipeline串聯起來的關鍵        host.getPipeline().getFirst().invoke(request, response);    }

一個普通的valve

來自org.apache.catalina.valves.ErrorReportValveinvoke方法:

public void invoke(Request request, Response response)        throws IOException, ServletException {        // 已經來就把原料丟給下一個vavle執行,這是把所有vavle串聯成一個pipeline的關鍵        getNext().invoke(request, response);        // 等到上面的valve全部執行好,才開始自己的真正工作:        Throwable throwable =            (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);        if (response.isCommitted()) {            return;        }        if (throwable != null) {            // The response is an error            response.setError();            // Reset the response (if possible)            try {                response.reset();            } catch (IllegalStateException e) {                ;            }            response.sendError                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);        }        response.setSuspended(false);        try {            report(request, response, throwable);        } catch (Throwable tt) {            ;        }    }

 

以上就描述了Pipeline機制,不知道你是不是會疑問(反正我是有了):vavle是在servlet前執行還是後執行,看上面源碼的例子好像是在後執行哦? 其實是不一定的,以RemoteAddrValve為例子:

RemoteAddrValve可以根據ip地址限制訪問,這個在文章後面附上了一些來自網路的配置步驟。

RemoteAddrValve源碼:

package org.apache.catalina.valves;import java.io.IOException;import javax.servlet.ServletException;import org.apache.catalina.connector.Request;import org.apache.catalina.connector.Response;public final class RemoteAddrValve    extends RequestFilterValve {    // ----------------------------------------------------- Instance Variables    /**     * The descriptive information related to this implementation.     */    private static final String info =        "org.apache.catalina.valves.RemoteAddrValve/1.0";    // ------------------------------------------------------------- Properties    /**     * Return descriptive information about this Valve implementation.     */    public String getInfo() {        return (info);    }    // --------------------------------------------------------- Public Methods    /**     * 會調用invoke方法     */    public void invoke(Request request, Response response)        throws IOException, ServletException {        // 調用了父類的process方法,我們就去看下父類的情況        process(request.getRequest().getRemoteAddr(), request, response);    }}

上面父類的process方法

    protected void process(String property,                           Request request, Response response)        throws IOException, ServletException {        // 在移入下一個valve是做了判斷 isAllowed(property)這個方法就是用來限制IP地址的        if (isAllowed(property)) {            getNext().invoke(request, response);            return;        }        // 限制了訪問,都發Error了        response.sendError(HttpServletResponse.SC_FORBIDDEN);    }

 

只有指定的主機或IP地址才可以訪問部署在Tomcat下的應用。Tomcat提供了兩個參數供你配置:RemoteHostValve 和RemoteAddrValve,前者用於限制主機名稱,後者用於限制IP地址。

通過配置這兩個參數,可以讓你過濾來自請求的主機或IP地址,並允許或拒絕哪些主機 IP

一、全域設定,對Tomcat下所有應用生效

server.xml中添加下面一行,重啟伺服器即可:

<Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.*" deny=""/>

注意:此行放在</Host>之前。

例:

1,只允許192.168.1.10訪問:

<Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.10" deny=""/>

2,只允許192.168.1.*網段訪問:

<ValveclassName="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.*" deny=""/>

3,只允許192.168.1.10、192.168.1.30訪問:

<Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.10,192.168.1.30" deny=""/>

4,根據主機名稱進行限制:

<Valve className="org.apache.catalina.valves.RemoteHostValve"allow="abc.com" deny=""/>

二、局部設定,僅對具體的應用生效根據項目配置情況進行設定:

1,使用conf目錄下xml檔案進行配置${tomcat_root}\conf\proj_1.xml
2,直接在server.xml中進行設定${tomcat_root}\conf\server.xml
在上述檔案對應項目的</Context>前增加下面一行:

<Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.*" deny=""/>

總結:

1,Pipeline的機制就像可配置方法的隊列,類似鏈表的實現方式。有用,靠譜。

2,valve配置也是tomcat配置中的比部分,具有使用價值。

 

讓我們繼續前行

----------------------------------------------------------------------

 

努力不一定成功,但不努力肯定不會成功。
共勉

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.