Tomcat底层原理
一、Tomcat啟動時到底對我們的應(yīng)用程序做了什么?
當(dāng)我們把一個應(yīng)用程序的war包放到Tomcat的webapps目錄后,啟動Tomcat,然后就可以通過瀏覽器發(fā)送Http請求訪問該war包內(nèi)的Servlet了。
這個過程包括:
1、啟動Tomcat
2、Tomcat處理Http請求
而啟動Tomcat的目的就是為了處理Http請求。
一個Http請求通常是這樣子的:
http://localhost:8080/app/helloServlet
這個請求包括:
我們通俗點來理解這個Http請求就是:瀏覽器通過Http協(xié)議,請求localhost:8080上的myapp應(yīng)用內(nèi)的helloServlet。
Tomcat架構(gòu)俯視圖:
二、那分析這個Http請求有什么用呢?和Tomcat有什么關(guān)系?
當(dāng)然有關(guān)系,上面這個Http請求中的localhost:8080其實代表的就是Tomcat。我們一個應(yīng)用程序想要從外部接收網(wǎng)絡(luò)數(shù)據(jù),那就要綁定一個端口,這個跟TCP協(xié)議有關(guān)系,外部請求發(fā)送給該端口,就能被對于的程序所接收到。Tomcat也是這樣,所以,Tomcat默認(rèn)綁定的就是8080。
所以上面這個Http請求首先是被Tomcat接收到,然后去解析這個請求。
解析這個請求包括:
當(dāng)Tomcat把從8080端口獲取的請求解析完成后,它就應(yīng)該根據(jù)應(yīng)用名和Servlet名字去找Servlet實現(xiàn)類了,只有找到Servlet實現(xiàn)類才能真正執(zhí)行Servlet里面的doGet或doPost方法。
所以,這里又分兩步:
根據(jù)請求中的應(yīng)用名和Servlet名怎么找到對應(yīng)的Servlet實現(xiàn)類呢 怎么執(zhí)行Servlet實現(xiàn)類中跟請求對應(yīng)的方法呢首先關(guān)于第二點,很簡單:可以用***反射***
那么對于第一點該怎么實現(xiàn)呢?
其實也很簡單,要么默認(rèn),要么映射。
默認(rèn)的意思就是,請求中的Servlet名就是類名,這種可行,但是不好用,一個類還有包名的,萬一在一個應(yīng)用中,存在不同的包下存在名字相同的Servlet,這個時候就尷尬了。
所以最好的方式就是映射,一個Servlet名對應(yīng)一個Servlet實現(xiàn)類。這也就是為什么我們在定義Servlet時,一定要做一個mapping關(guān)系,不管是通過@WebServlet注解還是在web.xml中,都需要配置一個mapping才能被訪問到。
所以,Tomcat通過監(jiān)聽端口,獲取數(shù)據(jù),然后解析數(shù)據(jù),根據(jù)請求url找到對應(yīng)的Servlet實現(xiàn)類,然后通過反射執(zhí)行Servlet實現(xiàn)類中的方法。
這個流程并不難,如果我們自己實現(xiàn),30分鐘內(nèi)可能就能實現(xiàn)出來這么一個功能,這也是Tomcat的主線功能,那么Tomcat復(fù)雜在哪呢?
其實還是復(fù)雜在這條主線,這條主線再拆分一下就是兩步:
tomcat容器對比:
三、Servlet
我們在來細(xì)想一下這里的第二步:尋找并執(zhí)行Servlet
我們通常說Tomcat是一個Servlet容器,到底體現(xiàn)在哪,怎么體現(xiàn)的呢?
我們得先來理解一下Servlet,Servlet=Server+Applet,表達(dá)的意思就是運行在服務(wù)端的應(yīng)用程序,它跟運行在客戶端的應(yīng)用程序是相對的,運行在客戶端的應(yīng)用程序,可以隨著用戶的操作而發(fā)生變化,而運行在服務(wù)端的應(yīng)用程序,用戶是不能直接操作的,你只能通過發(fā)送網(wǎng)絡(luò)請求來操作它,這就是Servlet的由來。這也就是為什么Servlet規(guī)范里會定義ServletRequest,ServletResponse接口,都是跟請求相關(guān)的,所以,Servlet其實就為Java程序員方便處理請求和響應(yīng)的一種抽象,作為Java程序員,你只需要通過定義Servlet,然后接收到的ServletRequest對象就代表請求,而不用關(guān)心ServletRequest具體的實現(xiàn)類是什么,是誰實現(xiàn)的,都不用關(guān)系,只要知道這個對象代表一個請求即可。
所以誰來實現(xiàn)這些接口呢?大家自然就能想到了,是Tomcat、Jetty這些了。比如Tomcat中對應(yīng)的請求實現(xiàn)類是RequestFacade,我們在Servlet中進(jìn)行強制轉(zhuǎn)化是沒有問題的,比如這么寫:
我們重新理解了Servlet之后,就可以再來理解Tomcat了,Tomcat確實是一個容器,但是是一個分層的容器。因為一個Servlet代表一個功能,一個應(yīng)用中可以有多個Servlet,所以在Tomcat中存在Context容器,Context容器中收納Servlet,Context就代表應(yīng)用,也就是應(yīng)用上下文,然后我們的應(yīng)用是部署在主機上的,所以Tomcat中存在Host容器,一個Host容器中可以容納多個Context容器,再繼續(xù),我們可能是多個主機所提供的功能屬于同一范疇的,所以在Tomcat中,在Host之上還有一層Engine。所以我們說的容器包括:Engine、Host、Context。
而Tomcat除開實現(xiàn)了這個層級結(jié)構(gòu)之外,還提供了一些輔助功能。
既然現(xiàn)在容器分層了,所以一個請求在尋找Servlet的過程中,就會經(jīng)過每一層容器,那么每層容器在這個過程中都可以去做一些事情,并且允許用戶自定義這些事情,比如可以在Host層去指定:所有請求該Host的請求都將日志打印到hostname.log文件中,或者所有請求該Host的請求都由該Host添加一下參數(shù),其他容器也類似,這其實就是一種責(zé)任鏈模式,Tomcat中實現(xiàn)了這種模式,是通過:Pipeline和Valve實現(xiàn)的。
好,這是關(guān)于容器的分析。
接下來我們再來分析一下Tomcat是怎么解析請求的,上文我們分析了,Tomcat實際上就是把接收到的請求轉(zhuǎn)化成RequestFacade對象,最后把這個對象傳遞給Servlet。
那么Tomcat的數(shù)據(jù)從哪來的呢?上文中我們一直默認(rèn)Tomcat接收的是Http請求,那么這個Http請求是怎么到達(dá)Tomcat的呢?總不是憑空飛過來了的吧。
大家應(yīng)該想到了,是通過網(wǎng)線傳過來的,所以我們要分析Tomcat是怎么接收到Http請求,就要分析網(wǎng)絡(luò)傳輸協(xié)議了。
首先,我們?yōu)槭裁葱枰W(wǎng)絡(luò)傳輸協(xié)議呢?
網(wǎng)絡(luò)是用來傳輸數(shù)據(jù)的,比如現(xiàn)在主機A有一份數(shù)據(jù)要發(fā)送給主機B,那么主機A在擁有數(shù)據(jù)的同時還是要知道主機B的地址,也就是IP,所以現(xiàn)在相當(dāng)于主機A上要有:數(shù)據(jù)+對方IP地址
四、流程圖
Tomcat:
(1)Tomcat中只有一個Server,一個Server可以有多個Service,一個Service可以有多個Connector和一個Container;
(2) Server掌管著整個Tomcat的生死大權(quán);
(4)Service 是對外提供服務(wù)的;
(5)Connector用于接受請求并將請求封裝成Request和Response來具體處理;
(6)Container用于封裝和管理Servlet,以及具體處理request請求;
Connector:
四個子容器:
(1)Engine:引擎,用來管理多個站點,一個Service最多只能有一個Engine;
(2)Host:代表一個站點,也可以叫虛擬主機,通過配置Host就可以添加站點;
(3)Context:代表一個應(yīng)用程序,對應(yīng)著平時開發(fā)的一套程序,或者一個WEB-INF目錄以及下面的web.xml文件;
(4)Wrapper:每一Wrapper封裝著一個Servlet;
上述的包含關(guān)系或者說是父子關(guān)系,都可以在tomcat的conf目錄下的server.xml配置文件中看出,下圖是刪除了注釋內(nèi)容之后的一個完整的server.xml配置文件(Tomcat版本為8.0)
更多詳情
五、那么Tcp是用來干什么的呢?
回到上面的場景,主機A想把數(shù)據(jù)發(fā)送給主機B,那誰來保證數(shù)據(jù)能可靠的到達(dá)對方呢?這就是Tcp協(xié)議所要做的事情。
那Http協(xié)議又是怎么回事呢?我們上面接收的Ip,Tcp都是跟傳輸相關(guān)的,而Http是跟應(yīng)用相關(guān)的,是跟數(shù)據(jù)所表達(dá)的意義相關(guān)的,比如,主機A發(fā)一份數(shù)據(jù)給主機B之后,主機B接收到這份數(shù)據(jù)后,它要干什么呢?它要執(zhí)行什么動作呢?這就是Http協(xié)議所要表達(dá)的,通過在數(shù)據(jù)中增加一些有意義的元素,比如請求方法,這樣數(shù)據(jù)接收方能更快的根據(jù)Http協(xié)議解析數(shù)據(jù),完成對應(yīng)的動作。
解析的比較粗,因為這不是本文的重點,我的重點是,那么誰來實現(xiàn)Http協(xié)議,誰來實現(xiàn)Tcp協(xié)議呢?
答案是:操作系統(tǒng)來實現(xiàn)Tcp協(xié)議,比如Linux、Windows,運行在操作系統(tǒng)之上的其他應(yīng)用程序來實現(xiàn)Http協(xié)議,比如瀏覽器、Tomcat。
比如Linux的源碼中就有關(guān)于Tcp協(xié)議的實現(xiàn),包括三次握手,四次揮手,都是通過c語言來實現(xiàn)的。那么瀏覽器它依照Http協(xié)議定義好數(shù)據(jù)之后,怎么利用Tcp協(xié)議發(fā)送出去呢?Tomcat怎么利用Tcp協(xié)議接收數(shù)據(jù)呢?
瀏覽器能不能直接調(diào)用操作系統(tǒng)中實現(xiàn)TCP協(xié)議關(guān)于發(fā)送數(shù)據(jù)的函數(shù)呢?Tomcat能不能調(diào)用對應(yīng)接收數(shù)據(jù)的函數(shù)呢?
原理上是可以的,但是就像我們開發(fā)業(yè)務(wù)一樣,我們不會將真正實現(xiàn)業(yè)務(wù)的方法直接暴露給其他人調(diào)用,而會提供一個接口,操作系統(tǒng)也一樣,這個接口就是Socket。
所以Tomcat是通過Socket從操作系統(tǒng)獲取數(shù)據(jù)的,拿到數(shù)據(jù)后進(jìn)行解析。
而Tomcat從操作系統(tǒng)系統(tǒng)拿數(shù)據(jù)又有幾種方式,也就是IO模型:BIO,NIO,AIO,這些不同的模式Tomcat都是支持的,只需要在server.xml中進(jìn)行配置即可。
簡單記錄了一下關(guān)于Tomcat的隨筆,其實關(guān)于TSocket、IO模型可以寫得更多一點,限于篇幅,本篇就到此為止吧,下次繼續(xù)。
總結(jié)
以上是生活随笔為你收集整理的Tomcat底层原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每天一个linux命令(9):nl命令
- 下一篇: g4600黑苹果efi_Hackinto