Tomcat:網(wǎng)絡(luò)請(qǐng)求原理分析

一、Http請(qǐng)求過(guò)程總覽

瀏覽器請(qǐng)求

http://localhost/test/index.jsp

image.png
  1. 用戶(hù)點(diǎn)擊網(wǎng)頁(yè)內(nèi)容,請(qǐng)求被發(fā)送到本機(jī)端口8080,被在那里監(jiān)聽(tīng)的Coyote HTTP/1.1 Connector獲得。

  2. Connector將Request包裝成ServletRequest給它所在的Service的Engine來(lái)處理宜肉,并等待Engine的回應(yīng)。

  3. Engine獲得請(qǐng)求localhost/test/index.jsp,匹配所有的虛擬主機(jī)Host腌且。

  4. Engine匹配到名為localhost的Host(即使匹配不到也把請(qǐng)求交給該Host處理,因?yàn)樵揌ost被定義為該Engine的默認(rèn)主機(jī))榛瓮,名為localhost的Host獲得請(qǐng)求/test/index.jsp铺董,匹配它所擁有的所有的Context。Host匹配到路徑為/test的Context(如果匹配不到就把該請(qǐng)求交給路徑名為""的Context去處理)禀晓。

  5. path="/test"的Context獲得請(qǐng)求/index.jsp精续,在它的mapping table中尋找出對(duì)應(yīng)的Servlet。Context匹配到URL PATTERN為*.jsp的Servlet,對(duì)應(yīng)于JspServlet類(lèi)粹懒。

  6. 構(gòu)造HttpServletRequest對(duì)象和HttpServletResponse對(duì)象重付,作為參數(shù)調(diào)用JspServlet的doGet()或doPost().執(zhí)行業(yè)務(wù)邏輯、數(shù)據(jù)存儲(chǔ)等程序凫乖。

  7. Context把執(zhí)行完之后的HttpServletResponse對(duì)象返回給Host确垫。

  8. Host把HttpServletResponse對(duì)象返回給Engine弓颈。

  9. Engine把HttpServletResponse對(duì)象返回Connector,Connector把ServletResponse對(duì)象封裝成Response删掀。

  10. Connector把Response返回給客戶(hù)Browser翔冀。

二、具體組件及源碼分析

2.1 外部網(wǎng)絡(luò)請(qǐng)求

2.1.1 Connector

用戶(hù)在瀏覽器中輸入一個(gè)URL地址之后瀏覽器將發(fā)起一個(gè)Http的請(qǐng)求披泪,通過(guò)網(wǎng)絡(luò)傳輸數(shù)據(jù)纤子,請(qǐng)求到我們的Tomcat服務(wù)器中,Tomcat使用Connector接收Socket請(qǐng)求數(shù)據(jù)并通過(guò)Coyote鏈接器封裝底層網(wǎng)絡(luò)通信款票,為Catalina容器提供了統(tǒng)一的接口控硼。

image.png

在Coyote中,Tomcat支持一下3種協(xié)議:

  1. HTTP/1.1協(xié)議:目前最常用的訪(fǎng)問(wèn)協(xié)議
  2. AJP協(xié)議:Apache提供的一種協(xié)議徽职,用于和Apache HTTP Server集成象颖。
  3. HTTP/2.0協(xié)議:下一代HTTP協(xié)議,自Tomcat8.5版本開(kāi)始支持姆钉。

針對(duì)HTTP和AJP協(xié)議说订,Coyote又按照I/O方式分別提供了不同的方案。

  1. BIO:同步阻塞I/O模式潮瓶,數(shù)據(jù)的讀取寫(xiě)入必須阻塞在一個(gè)線(xiàn)程內(nèi)等待其完成陶冷,Tomcat早期網(wǎng)絡(luò)請(qǐng)求的默認(rèn)實(shí)現(xiàn)方式(自8.5版本開(kāi)始,已經(jīng)移除)
  2. NIO:同步非阻塞的I/O模型(當(dāng)前Tomcat的默認(rèn)實(shí)現(xiàn))
  3. NIO2:異步非阻塞I/O模型
  4. APR:Apache可移植運(yùn)行庫(kù)毯辅。

2.1.2 NioEndpoint

我們以NioEndpoint為例埂伦,來(lái)看一下Tomcat是如何實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求收發(fā)的。


/**
* Defaults to using HTTP/1.1 NIO implementation.
* 默認(rèn)使用NIO實(shí)現(xiàn)的http/1.1協(xié)議
*/
public Connector() {
  this("HTTP/1.1");
}


public Connector(String protocol) {
    boolean apr = AprStatus.getUseAprConnector() && AprStatus.isInstanceCreated()
            && AprLifecycleListener.isAprAvailable();
    ProtocolHandler p = null;
    try {
        //根據(jù)指定的協(xié)議思恐,創(chuàng)建不同的處理器
        p = ProtocolHandler.create(protocol, apr);
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    }
    //省略部分代碼...
}

我們發(fā)現(xiàn)Tomcat默認(rèn)使用HTTP/1.1協(xié)議創(chuàng)建Http11NioProtocol沾谜,它默認(rèn)使用NioEndpoint

public Http11NioProtocol() {
    super(new NioEndpoint());
}

當(dāng)Connector執(zhí)行init()的同時(shí)運(yùn)行了protocolHandler.init()最終運(yùn)行了NioEndpoint.init(),最終就是啟動(dòng)了一個(gè)ServerSocket并綁定監(jiān)聽(tīng)端口胀莹。

protected void initServerSocket() throws Exception {
  //省略部分代碼
  serverSock = ServerSocketChannel.open();
    socketProperties.setProperties(serverSock.socket());
    InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
    serverSock.bind(addr, getAcceptCount());
}

當(dāng)Connector執(zhí)行start()方法時(shí)基跑,最終執(zhí)行了:NioEndpoint.startInternal()方法。該方法內(nèi)部啟動(dòng)了一個(gè)Poller線(xiàn)程描焰,一個(gè)Acceptor線(xiàn)程媳否。其中,Acceptor用于監(jiān)聽(tīng)客戶(hù)端連接荆秦,并將Socket連接放入待處理事件緩存池篱竭。Poller循環(huán)從待處理的事件緩存隊(duì)列中拿到請(qǐng)求交給線(xiàn)程池處理。Poller將Socket請(qǐng)求包裝成為:SocketProcessor對(duì)象步绸,執(zhí)行processor.process()方法掺逼,最終調(diào)用:getAdapter().service(request, response);這里的getAdapter即CoyoteAdapter,最終找到容器中管道方法的第一個(gè)閥門(mén)方法瓤介,發(fā)起調(diào)用坪圾,此時(shí)晓折,網(wǎng)絡(luò)請(qǐng)求正式進(jìn)入我們?nèi)萜髦小?/p>

// Calling the container 調(diào)用容器
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

TIPS 如果想要詳細(xì)了解Tomcat接收請(qǐng)求的流程圖可以查看官網(wǎng)提供的資料

http://tomcat.apache.org/tomcat-9.0-doc/architecture/requestProcess/request-process.png

2.2 容器內(nèi)部請(qǐng)求路徑匹配

public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
        throws Exception {
        //將coyote鏈接器中的Request,Response轉(zhuǎn)換為我們常用的HttpServletRequest,HttpServletResponse
    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);

    //省略部分代碼...

        // 匹配請(qǐng)求路徑兽泄,將當(dāng)前request請(qǐng)求到匹配到的servlet上
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {
            //check valves if we support async
            request.setAsyncSupported(
                    connector.getService().getContainer().getPipeline().isAsyncSupported());
            // Calling the container 調(diào)用容器
            connector.getService().getContainer().getPipeline().getFirst().invoke(
                    request, response);
     // 省略部分代碼...
}

在Tomcat啟動(dòng)的時(shí)候 Service持有了一個(gè)Mapper對(duì)象漓概,它借助MapperListener在應(yīng)用啟動(dòng)的過(guò)程中,將Engine病梢,Host胃珍,Context,Wrapper路徑都搜集了起來(lái)蜓陌。當(dāng)一個(gè)請(qǐng)求過(guò)來(lái)的時(shí)候它會(huì)去當(dāng)前 Service中去匹配,最終將請(qǐng)求執(zhí)行到我們StandardWrapperValve觅彰。此時(shí)會(huì)創(chuàng)建ApplicationFilterChain過(guò)濾器鏈;通過(guò)調(diào)用過(guò)濾器內(nèi)部方法internalDoFilter;最終調(diào)用:javax.servlet.http.HttpServlet.service()方法,請(qǐng)求到應(yīng)用中钮热!

三填抬、本文小結(jié)

我們通過(guò)一個(gè)Http請(qǐng)求路徑分析了Tomcat是使用Socket接收網(wǎng)絡(luò)請(qǐng)求,使用Coyote轉(zhuǎn)換我們的網(wǎng)絡(luò)協(xié)議參數(shù)隧期,包裝為ServletRequest和ServletResponse飒责,使用匹配請(qǐng)求路徑的方式最終找到我們應(yīng)用中的Servlet地址。
至此仆潮,可以說(shuō)我們對(duì)Tomcat的主要功能基本上已經(jīng)了解的七七八八了宏蛉,后續(xù)我們將會(huì)陸續(xù)的分享一些工作中常用的小技巧。


程序員的核心競(jìng)爭(zhēng)力其實(shí)還是技術(shù)性置,因此對(duì)技術(shù)還是要不斷的學(xué)習(xí)拾并,關(guān)注 “IT巔峰技術(shù)” 公眾號(hào) ,該公眾號(hào)內(nèi)容定位:中高級(jí)開(kāi)發(fā)鹏浅、架構(gòu)師嗅义、中層管理人員等中高端崗位服務(wù)的,除了技術(shù)交流外還有很多架構(gòu)思想和實(shí)戰(zhàn)案例隐砸。

作者是 《 消息中間件 RocketMQ 技術(shù)內(nèi)幕》 一書(shū)作者芥喇,同時(shí)也是 “RocketMQ 上海社區(qū)”聯(lián)合創(chuàng)始人,曾就職于拼多多凰萨、德邦等公司,現(xiàn)任上市快遞公司架構(gòu)負(fù)責(zé)人械馆,主要負(fù)責(zé)開(kāi)發(fā)框架的搭建胖眷、中間件相關(guān)技術(shù)的二次開(kāi)發(fā)和運(yùn)維管理、混合云及基礎(chǔ)服務(wù)平臺(tái)的建設(shè)霹崎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末珊搀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子尾菇,更是在濱河造成了極大的恐慌境析,老刑警劉巖囚枪,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異劳淆,居然都是意外死亡链沼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)沛鸵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)括勺,“玉大人,你說(shuō)我怎么就攤上這事曲掰〖埠矗” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵栏妖,是天一觀(guān)的道長(zhǎng)乱豆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)吊趾,這世上最難降的妖魔是什么宛裕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮趾徽,結(jié)果婚禮上续滋,老公的妹妹穿的比我還像新娘。我一直安慰自己孵奶,他們只是感情好疲酌,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著了袁,像睡著了一般朗恳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上载绿,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天粥诫,我揣著相機(jī)與錄音,去河邊找鬼崭庸。 笑死怀浆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的怕享。 我是一名探鬼主播执赡,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼函筋!你這毒婦竟也來(lái)了沙合?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤跌帐,失蹤者是張志新(化名)和其女友劉穎首懈,沒(méi)想到半個(gè)月后绊率,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡究履,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年滤否,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挎袜。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡顽聂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盯仪,到底是詐尸還是另有隱情紊搪,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布全景,位于F島的核電站耀石,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏爸黄。R本人自食惡果不足惜滞伟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炕贵。 院中可真熱鬧梆奈,春花似錦、人聲如沸称开。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鳖轰。三九已至清酥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蕴侣,已是汗流浹背焰轻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昆雀,地道東北人辱志。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像狞膘,于是被迫代替她去往敵國(guó)和親揩懒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 概述 Tomcat是一個(gè)JSP/Servlet容器客冈。其作為Servlet容器,有三種工作模式:獨(dú)立的Servlet...
    jiangmo閱讀 2,227評(píng)論 0 13
  • Tomcat組件 概述:核心組件:Connector 和 Container(以夫妻為例介紹)稳强。多個(gè) Connec...
    zhanglbjames閱讀 3,511評(píng)論 4 8
  • 在之前的專(zhuān)題spring源碼解讀系列中场仲,我們分析了spring的源碼和悦,詳細(xì)分析了spring的ioc和aop的實(shí)現(xiàn)...
    默寫(xiě)流年閱讀 850評(píng)論 0 0
  • WEB服務(wù)器 只要Web上的Server都叫Web Server,但是大家分工不同渠缕,解決的問(wèn)題也不同鸽素,所以根據(jù)We...
    Rick617閱讀 15,527評(píng)論 1 13
  • 1、Tomcat的缺省端口是多少亦鳞,怎么修改馍忽? 1)找到Tomcat目錄下的conf文件夾 2)進(jìn)入conf文件夾里...
    曹元_閱讀 279評(píng)論 0 3