0 系列目錄#
本篇文章將給大家講述Servlet容器中請(qǐng)求處理的過程,在給本篇文章起標(biāo)題時(shí)掂林,一直在“應(yīng)用服務(wù)器”與“Servlet容器”這兩者之間拿捏不定臣缀,主要是因?yàn)橐逦膮^(qū)分開這兩者的關(guān)系:Servlet容器可以說是應(yīng)用服務(wù)器的一個(gè)子集。又由于本文的初衷是講述大家平常使用比較多的Servlet為主泻帮,所以,給本篇就起了《Servlet容器請(qǐng)求處理》的名字计寇。
先說下在整個(gè)WEB請(qǐng)求處理過程中锣杂,本篇文章講述的是哪個(gè)流程模塊。為直觀明了番宁,先上一張圖元莫,紅色部分為本章所述模塊:
所講述的請(qǐng)求流程模塊,大家已經(jīng)很清楚了蝶押。那怎么給大家去講的更清晰踱蠢,大家理解的更容易呢?當(dāng)然是棋电,帶著問題去學(xué)習(xí)茎截,吸收或許會(huì)更快些啦。:)
開篇之前赶盔,給大家提以下幾個(gè)問題企锌,這些問題是本文的主體思路(也是個(gè)人學(xué)習(xí)路線):
WEB服務(wù)器那么多,Apache于未、Tomcat撕攒、Nginx陡鹃、Jetty、Resin抖坪,名詞那么多萍鲸,HTTP Server、Application Server擦俐、Web Server脊阴、Servlet Container,他們是什么捌肴?之間關(guān)系是什么蹬叭?區(qū)別又在哪?
CGI状知、WSGI秽五、Servlet、JSP饥悴、FastCGI等等坦喘,他們是什么?他們之間區(qū)別又在哪?和上面WEB服務(wù)器之間關(guān)系是什么?
Servlet生命周期及工作原理是什么飞蹂?
HTTP Request進(jìn)入到Tomcat中執(zhí)行咸灿,請(qǐng)求處理流程如何?如何找到對(duì)應(yīng)的Application并進(jìn)行請(qǐng)求處理销部?
1 WEB服務(wù)器#
只要Web上的Server都叫Web Server,但是大家分工不同,解決的問題也不同蓖救,所以根據(jù)Web Server提供的功能,每個(gè)Web Server的名字也會(huì)不一樣印屁。
按功能分類循捺,Web Server可以分為:
|- Web Server
|- Http Server
|- Application Server
|- Servlet Container
|- CGI Server
|- ......
1.1 Http Server##
HTTP Server本質(zhì)上也是一種應(yīng)用程序——它通常運(yùn)行在服務(wù)器之上,綁定服務(wù)器的IP地址并監(jiān)聽某一個(gè)tcp端口來接收并處理HTTP請(qǐng)求雄人,這樣客戶端(一般來說是IE, Firefox从橘,Chrome這樣的瀏覽器)就能夠通過HTTP協(xié)議來獲取服務(wù)器上的網(wǎng)頁(HTML格式)、文檔(PDF格式)础钠、音頻(MP4格式)恰力、視頻(MOV格式)等等資源。下圖描述的就是這一過程:
一個(gè)HTTP Server關(guān)心的是HTTP協(xié)議層面的傳輸和訪問控制珍坊,所以在Apache/Nginx上你可以看到代理牺勾、負(fù)載均衡等功能。
- 客戶端通過HTTP Server訪問服務(wù)器上存儲(chǔ)的靜態(tài)資源(HTML文件阵漏、圖片文件等等)驻民。
- 通過CGI/Servlet技術(shù)翻具,也可以將處理過的動(dòng)態(tài)內(nèi)容通過HTTP Server分發(fā),但是一個(gè)HTTP Server始終只是把服務(wù)器上的文件如實(shí)的通過HTTP協(xié)議傳輸給客戶端回还。
HTTP Server中經(jīng)常使用的是Apache裆泳、Nginx兩種,HTTP Server主要用來做靜態(tài)內(nèi)容服務(wù)柠硕、代理服務(wù)器工禾、負(fù)載均衡等。直面外來請(qǐng)求轉(zhuǎn)發(fā)給后面的應(yīng)用服務(wù)(Tomcat蝗柔,django什么的)闻葵。
|- Http Server
|- Apache
|- Nginx
1.1.1 Apache HTTP服務(wù)器###
Apache HTTP服務(wù)器是一個(gè)模塊化的服務(wù)器,可以運(yùn)行在幾乎所有廣泛使用的計(jì)算機(jī)平臺(tái)上癣丧。Apache支持模塊多槽畔,性能穩(wěn)定,Apache本身是靜態(tài)解析胁编,適合靜態(tài)HTML厢钧、圖片等,但可以通過擴(kuò)展腳本嬉橙、模塊等支持動(dòng)態(tài)頁面等早直。
Apache可以支持PHPcgiperl,但是要使用Java的話市框,你需要Tomcat在Apache后臺(tái)支撐霞扬,將Java請(qǐng)求由Apache轉(zhuǎn)發(fā)給Tomcat處理。
1.1.2 Nginx HTTP服務(wù)器###
Nginx是一個(gè)高性能的HTTP和反向代理服務(wù)器枫振,同時(shí)也是一個(gè)IMAP/POP3/SMTP代理服務(wù)器祥得。
其特點(diǎn)是占有內(nèi)存少,并發(fā)能力強(qiáng)蒋得。Nginx代碼完全用C語言從頭寫成。
具有很高的穩(wěn)定性乒疏。其它HTTP服務(wù)器额衙,當(dāng)遇到訪問的峰值,或者有人惡意發(fā)起慢速連接時(shí)怕吴,也很可能會(huì)導(dǎo)致服務(wù)器物理內(nèi)存耗盡頻繁交換窍侧,失去響應(yīng),只能重啟服務(wù)器转绷。例如當(dāng)前apache一旦上到200個(gè)以上進(jìn)程伟件,web響應(yīng)速度就明顯非常緩慢了。
而Nginx采取了分階段資源分配技術(shù)议经,使得它的CPU與內(nèi)存占用率非常低斧账。Nginx官方表示保持10000個(gè)沒有活動(dòng)的連接谴返,它只占2.5M內(nèi)存,所以類似DOS這樣的攻擊對(duì)nginx來說基本上是毫無用處的咧织。就穩(wěn)定性而言嗓袱,Nginx比Lighthttpd更勝一籌。
1.1.3 Nginx與Apache比較###
Nginx相對(duì)于Apache的優(yōu)點(diǎn):
- 輕量級(jí)习绢,同樣啟動(dòng)WEB服務(wù)渠抹,比Apache占用更少的內(nèi)存以及資源;
- 抗并發(fā)性能高闪萄,核心區(qū)別在于Apache是同步多進(jìn)程模型梧却,一個(gè)連接對(duì)應(yīng)一個(gè)進(jìn)程。Nginx是異步的败去,多個(gè)連接(萬級(jí)別)可以對(duì)應(yīng)一個(gè)進(jìn)程放航;
- Nginx模塊較少,配置簡(jiǎn)單为迈,所以Nginx可以將資源用在數(shù)據(jù)處理以及進(jìn)程上面三椿,Apache模塊較多比較全,相對(duì)穩(wěn)定葫辐,但在內(nèi)存資源上消耗比較大搜锰;
- Nginx可以在不間斷的情況下進(jìn)行軟件版本的升級(jí);
- Nginx處理靜態(tài)頁面性能比apache高3倍多耿战;
選擇高并發(fā)高性能就選擇Nginx蛋叼,如果要穩(wěn)定,選擇Apache剂陡,主要根據(jù)服務(wù)器要面臨的需求而定狈涮。
當(dāng)然,兩者也可以組合使用:
- Nginx放前端+apache放后端+MYSQL+PHP:可以提高服務(wù)器負(fù)載能力
- Nginx處理靜態(tài)頁面請(qǐng)求如MP3鸭栖,GIF.JPG.JS歌馍,apache處理動(dòng)態(tài)頁面請(qǐng)求,充分結(jié)合了二者的優(yōu)勢(shì)晕鹊;
1.2 Application Server##
Application Server 是一個(gè)應(yīng)用執(zhí)行的服務(wù)器松却。它首先需要支持開發(fā)語言的 Runtime(對(duì)于 Tomcat 來說,就是 Java)溅话,保證應(yīng)用能夠在應(yīng)用服務(wù)器上正常運(yùn)行晓锻。其次,需要支持應(yīng)用相關(guān)的規(guī)范飞几,例如類庫砚哆、安全方面的特性。與HTTP Server相比屑墨,Application Server能夠動(dòng)態(tài)的生成資源并返回到客戶端躁锁。
|- Application Server
|- Tomcat
|- Jetty
當(dāng)初在Apache Server開發(fā)時(shí)還未出現(xiàn)Servlet的概念纷铣,所以Apache不能內(nèi)置支持Servlet。實(shí)際上灿里,除了Apache关炼,其他許多HTTP Server軟件都不能直接支持Servlet。為了支持Servlet匣吊,通常要單獨(dú)開發(fā)程序儒拂,這種程序一般稱為服務(wù)器小程序容器(Servlet Container),有時(shí)也叫做服務(wù)器小程序引擎(Servlet Engine)色鸳。它是Web服務(wù)器或應(yīng)用程序服務(wù)器的一部分社痛,用于在發(fā)送的請(qǐng)求和響應(yīng)之上提供網(wǎng)絡(luò)服務(wù),解碼基于MIME的請(qǐng)求命雀,格式化基于MIME的響應(yīng)蒜哀,它在Servlet的生命周期內(nèi)包容和管理Servlet,是一個(gè)實(shí)時(shí)運(yùn)行的外殼程序吏砂。運(yùn)行時(shí)由Web服務(wù)器軟件處理一般請(qǐng)求撵儿,并把Servlet調(diào)用傳遞給“容器”來處理。
比如狐血,對(duì)于 Tomcat 來說淀歇,就是需要提供 JSP/Sevlet 運(yùn)行需要的標(biāo)準(zhǔn)類庫、Interface 等匈织。為了方便浪默,應(yīng)用服務(wù)器往往也會(huì)集成 HTTP Server 的功能,但是不如專業(yè)的 HTTP Server 那么強(qiáng)大缀匕,所以Application Server往往是運(yùn)行在 HTTP Server 的背后纳决,執(zhí)行應(yīng)用,將動(dòng)態(tài)的內(nèi)容轉(zhuǎn)化為靜態(tài)的內(nèi)容之后乡小,通過 HTTP Server 分發(fā)到客戶端阔加。
Tomcat運(yùn)行在JVM之上,它和HTTP服務(wù)器一樣满钟,綁定IP地址并監(jiān)聽TCP端口掸哑,同時(shí)還包含以下指責(zé):
- 管理Servlet程序的生命周期;
- 將URL映射到指定的Servlet進(jìn)行處理零远;
- 與Servlet程序合作處理HTTP請(qǐng)求——根據(jù)HTTP請(qǐng)求生成HttpServletRequest/Response對(duì)象并傳遞給Servlet進(jìn)行處理,將Servlet中的HttpServletResponse對(duì)象生成的內(nèi)容返回給瀏覽器厌蔽;
所以 Tomcat 屬于是一個(gè)「Application Server」牵辣,但是更準(zhǔn)確的來說,是一個(gè)「Servlet/JSP」應(yīng)用的容器(Ruby/Python 等其他語言開發(fā)的應(yīng)用也無法直接運(yùn)行在 Tomcat 上)奴饮。
1.2.1 Servlet容器工作模式###
按照工作模式的不同纬向,Servlet容器可以分為以下3類:
- 獨(dú)立運(yùn)行的Servlet容器
在這種模式下择浊,Servlet容器作為構(gòu)成Web服務(wù)器的一部分而存在。當(dāng)使用基于Java的Web服務(wù)器時(shí)逾条,就屬于這種情況琢岩。這種方式是Tomcat的默認(rèn)模式,然而大多數(shù)Web服務(wù)器并不是基于Java的师脂,所以就產(chǎn)生了下面的兩種其他類型担孔。
- 內(nèi)置的Servlet容器
Servlet容器由Web服務(wù)器插件和Java容器兩部分組成。采用這種方式時(shí)吃警,Web服務(wù)器插件需要在某個(gè)Web服務(wù)器內(nèi)部地址空間中打開一個(gè)JVM(Java虛擬機(jī))糕篇,在此JVM上加載Java容器并運(yùn)行Servlet。如果客戶端調(diào)用Servlet酌心,Web服務(wù)器插件首先獲得此請(qǐng)求的控制并將它傳遞(使用JNI技術(shù))給Java容器拌消,然后Java容器把此請(qǐng)求交給Servlet來處理。這種方式運(yùn)行速度較快安券,并且能夠提供良好的性能墩崩,適用于單進(jìn)程、多線程服務(wù)器侯勉,但是在伸縮性方面存在不足鹦筹。
- 外置的Servlet容器
采用這種方式時(shí),Servlet容器運(yùn)行在Web服務(wù)器外部地址空間壳鹤。先由Web服務(wù)器插件在某個(gè)Web服務(wù)器外部地址空間打開一個(gè)JVM(Java虛擬機(jī))盛龄,然后加載Java容器來運(yùn)行Servlet。Web服務(wù)器插件和JVM之間使用IPC(進(jìn)程間通信)機(jī)制(通常是TCP/IPSockets)芳誓。如果客戶端調(diào)用Servlet余舶,Web服務(wù)器插件首先獲得此請(qǐng)求的控制并將它傳遞(使用IPC技術(shù))給Java容器,然后Java容器把此請(qǐng)求交給Servlet來處理锹淌。這種方式對(duì)客戶端請(qǐng)求的處理速度不如內(nèi)置Servlet那樣快匿值,但是在其他方面(如可伸縮性、穩(wěn)定性等)具有優(yōu)勢(shì)赂摆。
Tomcat屬于Servlet容器挟憔,其工作模式也分為上述3種烟号,所以Tomcat既可被用作獨(dú)立運(yùn)行的Servlet引擎(便于開發(fā)和調(diào)試)绊谭,又可作為一個(gè)需要增強(qiáng)功能的Web服務(wù)器(如當(dāng)前的Apache、IIS和Netscape服務(wù)器)插件汪拥。在配置Tomcat之前达传,就需要確定采用哪種工作模式,工作模式(1)比較簡(jiǎn)單,直接安裝Tomcat即可宪赶,工作模式(2)和(3)有些復(fù)雜宗弯,除了安裝Tomcat、Web服務(wù)器之外搂妻,還需要安裝連接兩者的中間連接件蒙保。
1.2.2 Apache與Tomcat整合使用###
雖然Tomcat也可以認(rèn)為是HTTP服務(wù)器,但通常它仍然會(huì)和Apache/Nginx配合在一起使用:
動(dòng)靜態(tài)資源分離——運(yùn)用Nginx的反向代理功能分發(fā)請(qǐng)求:所有動(dòng)態(tài)資源的請(qǐng)求交給Tomcat欲主,而靜態(tài)資源的請(qǐng)求(例如圖片邓厕、視頻、CSS岛蚤、JavaScript文件等)則直接由Nginx返回到瀏覽器邑狸,這樣能大大減輕Tomcat的壓力;
負(fù)載均衡——當(dāng)業(yè)務(wù)壓力增大時(shí)涤妒,可能一個(gè)Tomcat的實(shí)例不足以處理单雾,那么這時(shí)可以啟動(dòng)多個(gè)Tomcat實(shí)例進(jìn)行水平擴(kuò)展,而Nginx的負(fù)載均衡功能可以把請(qǐng)求通過算法分發(fā)到各個(gè)不同的實(shí)例進(jìn)行處理她紫;
整合的好處:
- 如果客戶端請(qǐng)求的是靜態(tài)頁面硅堆,則只需要Apache服務(wù)器響應(yīng)請(qǐng)求。
- 如果客戶端請(qǐng)求動(dòng)態(tài)頁面贿讹,則是Tomcat服務(wù)器響應(yīng)請(qǐng)求渐逃。
- 因?yàn)镴SP是服務(wù)器端解釋代碼的,這樣整合就可以減少Tomcat的服務(wù)開銷民褂。
2 什么是CGI#
如上文所述茄菊,HTTP服務(wù)器是一個(gè)很簡(jiǎn)單的東西,并不負(fù)責(zé)動(dòng)態(tài)網(wǎng)頁的構(gòu)建赊堪,只能轉(zhuǎn)發(fā)靜態(tài)網(wǎng)頁面殖。事物總是不斷發(fā)展,網(wǎng)站也越來越復(fù)雜哭廉,所以出現(xiàn)動(dòng)態(tài)技術(shù)脊僚。同時(shí)Apache也說,它能支持perl遵绰,生成動(dòng)態(tài)網(wǎng)頁辽幌。這個(gè)支持perl,其實(shí)是Apache越位了椿访,做了一件額外的事情乌企。
既然HTTP Server自己不能做,外包給別人吧成玫,但是要與第三做個(gè)約定逛犹,我給你什么端辱,然后你給我什么,就是握把請(qǐng)求參數(shù)發(fā)送給你虽画,然后我接收你的處理結(jié)果給客戶端。那這個(gè)約定就是Common Gateway Interface荣病,簡(jiǎn)稱CGI码撰。
CGI全稱是“通用網(wǎng)關(guān)接口”(Common Gateway Interface),是HTTP服務(wù)器與你的或其它機(jī)器上的程序進(jìn)行“交談”的一種工具个盆,其程序須運(yùn)行在網(wǎng)絡(luò)服務(wù)器上脖岛,是一種根據(jù)請(qǐng)求信息動(dòng)態(tài)產(chǎn)生響應(yīng)內(nèi)容的接口協(xié)議。CGI可以用任何一種語言編寫颊亮,只要這種語言具有標(biāo)準(zhǔn)輸入柴梆、輸出和環(huán)境變量。如php终惑,perl绍在,tcl等。
通過CGI雹有,HTTP Server可以將根據(jù)請(qǐng)求不同啟動(dòng)不同的外部程序偿渡,并將請(qǐng)求內(nèi)容轉(zhuǎn)發(fā)給該程序,在程序執(zhí)行結(jié)束后霸奕,將執(zhí)行結(jié)果作為回應(yīng)返回給客戶端溜宽。也就是說,對(duì)于每個(gè)請(qǐng)求质帅,都要產(chǎn)生一個(gè)新的進(jìn)程進(jìn)行處理适揉。因?yàn)槊總€(gè)進(jìn)程都會(huì)占有很多服務(wù)器的資源和時(shí)間,這就導(dǎo)致服務(wù)器無法同時(shí)處理很多的并發(fā)請(qǐng)求煤惩。另外CGI程序都是與操作系統(tǒng)平臺(tái)相關(guān)的嫉嘀,雖然在互聯(lián)網(wǎng)爆發(fā)的初期,CGI為開發(fā)互聯(lián)網(wǎng)應(yīng)用做出了很大的貢獻(xiàn)盟庞,但是隨著技術(shù)的發(fā)展吃沪,開始逐漸衰落。
所以什猖,CGI的定義是:外部應(yīng)用程序與HTTP 服務(wù)器之間的接口協(xié)議票彪。
2.1 CGI工作原理##
HTTP Server與CGI程序請(qǐng)求處理流程:
HTTP服務(wù)器將根據(jù)CGI程序的類型決定數(shù)據(jù)向CGI程序的傳送方式,一般來講是通過標(biāo)準(zhǔn)輸入/輸出流和環(huán)境變量來與CGI程序間傳遞數(shù)據(jù)不狮。 如下圖所示:
CGI程序通過標(biāo)準(zhǔn)輸入(STDIN)和標(biāo)準(zhǔn)輸出(STDOUT)來進(jìn)行輸入輸出降铸。此外CGI程序還通過環(huán)境變量來得到輸入,操作系統(tǒng)提供了許多環(huán)境變量摇零,它們定義了程序的執(zhí)行環(huán)境推掸,應(yīng)用程序可以存取它們。HTTP服務(wù)器和CGI接口又另外設(shè)置了一些環(huán)境變量,用來向CGI程序傳遞一些重要的參數(shù)谅畅。CGI的GET方法還通過環(huán)境變量QUERY-STRING向CGI程序傳遞Form中的數(shù)據(jù)登渣。
2.2 CGI環(huán)境變量##
下面是一些常用的CGI環(huán)境變量:
每當(dāng)客戶請(qǐng)求CGI的時(shí)候,HTTP服務(wù)器就請(qǐng)求操作系統(tǒng)生成一個(gè)新的CGI解釋器進(jìn)程(如php-cgi.exe)毡泻,CGI的一個(gè)進(jìn)程則處理完一個(gè)請(qǐng)求后退出胜茧,下一個(gè)請(qǐng)求來時(shí)再創(chuàng)建新進(jìn)程。當(dāng)然仇味,這樣在訪問量很少?zèng)]有并發(fā)的情況也行呻顽。可是當(dāng)訪問量增大丹墨,并發(fā)存在廊遍,這種方式就不適合了。于是就有了FastCGI贩挣。
3 什么是FastCGI#
FastCGI像是一個(gè)常駐(long-live)型的CGI喉前,它可以一直執(zhí)行著,只要激活后揽惹,不會(huì)每次都要花費(fèi)時(shí)間去fork一次(這是CGI最為人詬病的fork-and-execute 模式)被饿。它還支持分布式的運(yùn)算, 即 FastCGI 程序可以在網(wǎng)站服務(wù)器以外的主機(jī)上執(zhí)行并且接受來自其它網(wǎng)站服務(wù)器來的請(qǐng)求。
FastCGI是語言無關(guān)的搪搏、可伸縮架構(gòu)的CGI開放擴(kuò)展狭握,其主要行為是將CGI解釋器進(jìn)程保持在內(nèi)存中并因此獲得較高的性能。眾所周知疯溺,CGI解釋器的反復(fù)加載是CGI性能低下的主要原因论颅,如果CGI解釋器保持在內(nèi)存中并接受FastCGI進(jìn)程管理器調(diào)度,則可以提供良好的性能囱嫩、伸縮性恃疯、Fail- Over特性等等。
3.1 FastCGI工作原理##
- HTTP Server啟動(dòng)時(shí)載入FastCGI進(jìn)程管理器(IIS ISAPI或Apache Module)墨闲;
- FastCGI進(jìn)程管理器自身初始化今妄,啟動(dòng)多個(gè)CGI解釋器進(jìn)程(可見多個(gè)php-cgi)并等待來自HTTP Server的連接;
- 當(dāng)客戶端請(qǐng)求到達(dá)HTTP Server時(shí)鸳碧,F(xiàn)astCGI進(jìn)程管理器選擇并連接到一個(gè)CGI解釋器盾鳞。HTTP Server將CGI環(huán)境變量和標(biāo)準(zhǔn)輸入發(fā)送到FastCGI子進(jìn)程php-cgi;
- FastCGI子進(jìn)程完成處理后將標(biāo)準(zhǔn)輸出和錯(cuò)誤信息從同一連接返回HTTP Server瞻离。當(dāng)FastCGI子進(jìn)程關(guān)閉連接時(shí)腾仅,請(qǐng)求便告處理完成。FastCGI子進(jìn)程接著等待并處理來自FastCGI進(jìn)程管理器(運(yùn)行在HTTP Server中)的下一個(gè)連接套利。在CGI模式中推励,php-cgi在此便退出了鹤耍。
在上述情況中,你可以想象CGI通常有多慢验辞。每一個(gè)Web請(qǐng)求PHP都必須重新解析php.ini稿黄、重新載入全部擴(kuò)展并重初始化全部數(shù)據(jù)結(jié)構(gòu)。使用FastCGI跌造,所有這些都只在進(jìn)程啟動(dòng)時(shí)發(fā)生一次抛猖。一個(gè)額外的好處是,持續(xù)數(shù)據(jù)庫連接(Persistent database connection)可以工作鼻听。
3.2 FastCGI與CGI特點(diǎn)##
- 如CGI,F(xiàn)astCGI也具有語言無關(guān)性联四;
- 如CGI撑碴,F(xiàn)astCGI在進(jìn)程中的應(yīng)用程序,獨(dú)立于核心web服務(wù)器運(yùn)行朝墩,提供了一個(gè)比API更安全的環(huán)境醉拓。(API把應(yīng)用程序的代碼與核心的web服務(wù)器鏈接在一起,這意味著在一個(gè)錯(cuò)誤的API的應(yīng)用程序可能會(huì)損壞其他應(yīng)用程序或核心服務(wù)器; 惡意的API的應(yīng)用程序代碼甚至可以竊取另一個(gè)應(yīng)用程序或核心服務(wù)器的密鑰收苏。)
- FastCGI技術(shù)目前支持語言有:C/C++亿卤、Java、Perl鹿霸、Tcl排吴、Python、SmallTalk懦鼠、Ruby等钻哩。相關(guān)模塊在Apache, ISS, Lighttpd等流行的服務(wù)器上也是可用的。
- 如CGI肛冶,F(xiàn)astCGI的不依賴于任何Web服務(wù)器的內(nèi)部架構(gòu)街氢,因此即使服務(wù)器技術(shù)的變化, FastCGI依然穩(wěn)定不變。
4 什么是PHP-CGI#
PHP-CGI是PHP自帶的FastCGI管理器睦袖。PHP-CGI的不足:
- PHP-CGI變更php.ini配置后珊肃,需重啟PHP-CGI才能讓新的php-ini生效,不可以平滑重啟馅笙;
- 直接殺死PHP-CGI進(jìn)程伦乔,php就不能運(yùn)行了。(PHP-FPM和Spawn-FCGI就沒有這個(gè)問題延蟹,守護(hù)進(jìn)程會(huì)平滑從新生成新的子進(jìn)程评矩。)
5 什么是PHP-FPM#
PHP-FPM是一個(gè)PHP FastCGI管理器,是只用于PHP的阱飘,使用PHP-FPM來控制PHP-CGI的FastCGI進(jìn)程斥杜,它負(fù)責(zé)管理一個(gè)進(jìn)程池虱颗,來處理來自Web服務(wù)器的請(qǐng)求≌嵛梗可以在 http://php-fpm.org/download 下載得到忘渔。
相對(duì)Spawn-FCGI,PHP-FPM在CPU和內(nèi)存方面的控制都更勝一籌缰儿,而且前者很容易崩潰畦粮,必須用crontab進(jìn)行監(jiān)控,而PHP-FPM則沒有這種煩惱乖阵。
PHP-FPM提供了更好的PHP進(jìn)程管理方式宣赔,可以有效控制內(nèi)存和進(jìn)程、可以平滑重載PHP配置瞪浸,比Spawn-FCGI具有更多優(yōu)點(diǎn)儒将,所以被PHP官方收錄了。在PHP 5.3.3中可以直接使用PHP-FPM了对蒲。
在./configure的時(shí)候帶 –enable-fpm參數(shù)即可開啟PHP-FPM钩蚊。
5.1 PHP-FPM工作原理##
Apache+PHP配合使用,會(huì)在Apache配置下面一段:
LoadModule php5_module C:/php/php5apache2_2.dll
當(dāng)PHP需要在Apache服務(wù)器下運(yùn)行時(shí)蹈矮,一般來說砰逻,它可以模塊的形式集成,此時(shí)模塊的作用是接收Apache傳遞過來的PHP文件請(qǐng)求泛鸟,并處理這些請(qǐng)求蝠咆,然后將處理后的結(jié)果返回給Apache。如果我們?cè)贏pache啟動(dòng)前在其配置文件中配置好了PHP模塊谈况,PHP模塊通過注冊(cè)apache2的ap_hook_post_config掛鉤勺美,在Apache啟動(dòng)的時(shí)候啟動(dòng)此模塊以接受PHP文件的請(qǐng)求。
Apache的Hook機(jī)制是指:Apache允許模塊(包括內(nèi)部模塊和外部模塊碑韵,例如mod_php5.so赡茸,mod_perl.so等)將自定義的函數(shù)注入到請(qǐng)求處理循環(huán)中。換句話說祝闻,模塊可以在Apache的任何一個(gè)處理階段中掛接(Hook)上自己的處理函數(shù)占卧,從而參與Apache的請(qǐng)求處理過程。mod_php5.so/php5apache2.dll就是將所包含的自定義函數(shù)联喘,通過Hook機(jī)制注入到Apache中华蜒,在Apache處理流程的各個(gè)階段負(fù)責(zé)處理php請(qǐng)求。
有人測(cè)試Nginx+PHP-FPM在高并發(fā)情況下可能會(huì)達(dá)到Apache+mod_php5的5~10倍豁遭,現(xiàn)在Nginx+PHP-FPM使用的人越來越多叭喜。
6 什么是Spawn-FCGI#
Spawn-FCGI是一個(gè)通用的FastCGI管理服務(wù)器,它是lighttpd中的一部份蓖谢,很多人都用Lighttpd的Spawn-FCGI進(jìn)行FastCGI模式下的管理工作捂蕴,不過有不少缺點(diǎn)譬涡。而PHP-FPM的出現(xiàn)多少緩解了一些問題,但PHP-FPM有個(gè)缺點(diǎn)就是要重新編譯啥辨,這對(duì)于一些已經(jīng)運(yùn)行的環(huán)境可能有不小的風(fēng)險(xiǎn)(refer)涡匀。
Spawn-FCGI目前已經(jīng)獨(dú)成為一個(gè)項(xiàng)目,更加穩(wěn)定一些溉知,也給很多Web 站點(diǎn)的配置帶來便利陨瘩。已經(jīng)有不少站點(diǎn)將它與nginx搭配來解決動(dòng)態(tài)網(wǎng)頁。
6.1 PHP-FPM與Spawn-CGI對(duì)比##
PHP-FPM级乍、Spawn-FCGI都是守護(hù)PHP-CGI的進(jìn)程管理器舌劳。
PHP-FPM的使用非常方便,配置都是在PHP-FPM.ini的文件內(nèi)玫荣,而啟動(dòng)蒿囤、重啟都可以從php/sbin/PHP-FPM中進(jìn)行。更方便的是修改php.ini后可以直接使用PHP-FPM reload進(jìn)行加載崇决,無需殺掉進(jìn)程就可以完成php.ini的修改加載。使用PHP-FPM可以使PHP有不小的性能提升底挫。PHP-FPM控制的進(jìn)程CPU回收的速度比較慢恒傻,內(nèi)存分配的很均勻。
Spawn-FCGI控制的進(jìn)程CPU下降的很快建邓,而內(nèi)存分配的比較不均勻盈厘。有很多進(jìn)程似乎未分配到,而另外一些卻占用很高官边》惺郑可能是由于進(jìn)程任務(wù)分配的不均勻?qū)е碌摹6@也導(dǎo)致了總體響應(yīng)速度的下降注簿。而PHP-FPM合理的分配契吉,導(dǎo)致總體響應(yīng)的提到以及任務(wù)的平均。
7 什么是Servlet#
Servlet最初是在1995年由James Gosling提出的诡渴,因?yàn)槭褂迷摷夹g(shù)需要復(fù)雜的Web服務(wù)器支持捐晶,所以當(dāng)時(shí)并沒有得到重視,也就放棄了妄辩。后來隨著Web應(yīng)用復(fù)雜度的提升惑灵,并要求提供更高的并發(fā)處理能力,Servlet被重新?lián)炱鹧垡⒃贘ava平臺(tái)上得到實(shí)現(xiàn)英支,現(xiàn)在提起Servlet,指的都是Java Servlet哮伟。Java Servlet要求必須運(yùn)行在Web服務(wù)器當(dāng)中干花,與Web服務(wù)器之間屬于分工和互補(bǔ)關(guān)系妄帘。確切的說,在實(shí)際運(yùn)行的時(shí)候Java Servlet與Web服務(wù)器會(huì)融為一體把敢,如同一個(gè)程序一樣運(yùn)行在同一個(gè)Java虛擬機(jī)(JVM)當(dāng)中寄摆。與CGI不同的是,Servlet對(duì)每個(gè)請(qǐng)求都是單獨(dú)啟動(dòng)一個(gè)線程修赞,而不是進(jìn)程婶恼。這種處理方式大幅度地降低了系統(tǒng)里的進(jìn)程數(shù)量速缨,提高了系統(tǒng)的并發(fā)處理能力专钉。另外因?yàn)镴ava Servlet是運(yùn)行在虛擬機(jī)之上的,也就解決了跨平臺(tái)問題午衰。如果沒有Servlet的出現(xiàn)割择,也就沒有互聯(lián)網(wǎng)的今天眷篇。
在Servlet出現(xiàn)之后,隨著使用范圍的擴(kuò)大荔泳,人們發(fā)現(xiàn)了它的一個(gè)很大的一個(gè)弊端蕉饼。那就是為了能夠輸出HTML格式內(nèi)容,需要編寫大量重復(fù)代碼玛歌,造成不必要的重復(fù)勞動(dòng)昧港。為了解決這個(gè)問題,基于Servlet技術(shù)產(chǎn)生了JavaServet Pages技術(shù)支子,也就是JSP创肥。Servlet和JSP兩者分工協(xié)作,Servlet側(cè)重于解決運(yùn)算和業(yè)務(wù)邏輯問題值朋,JSP則側(cè)重于解決展示問題叹侄。Servlet與JSP一起為Web應(yīng)用開發(fā)帶來了巨大的貢獻(xiàn),后來出現(xiàn)的眾多Java Web應(yīng)用開發(fā)框架都是基于這兩種技術(shù)的昨登,更確切的說趾代,都是基于Servlet技術(shù)的。
7.1 Servlet生命周期##
作為一名專業(yè)編程人員丰辣,您碰到的大多數(shù) Java servlet 都是為響應(yīng) Web 應(yīng)用程序上下文中的 HTTP 請(qǐng)求而設(shè)計(jì)的稽坤。因此,javax.servlet 和 javax.servlet.http 包中特定于 HTTP 的類是您應(yīng)該關(guān)心的糯俗。對(duì)于Servlet容器(Tomcat)與HttpServlet是怎樣進(jìn)行交互的呢尿褪,看下類圖:
Servlet的框架是由兩個(gè)Java包組成的:javax.servlet與javax.servlet.http。在javax.servlet包中定義了所有的Servlet類都必須實(shí)現(xiàn)或者擴(kuò)展的通用接口和類得湘。在javax.servlet.http包中定義了采用Http協(xié)議通信的HttpServlet類杖玲。Servlet的框架的核心是javax.servlet.Servlet接口,所有的Servlet都必須實(shí)現(xiàn)這個(gè)接口淘正。在Servlet接口中定義了5個(gè)方法摆马,其中3個(gè)方法代表了Servlet的生命周期:
- init(ServletConfig)方法:負(fù)責(zé)初始化Servlet對(duì)象臼闻,在Servlet的生命周期中,該方法執(zhí)行一次囤采;該方法執(zhí)行在單線程的環(huán)境下述呐,因此開發(fā)者不用考慮線程安全的問題;
- service(ServletRequest req,ServletResponse res)方法:負(fù)責(zé)響應(yīng)客戶的請(qǐng)求蕉毯;為了提高效率乓搬,Servlet規(guī)范要求一個(gè)Servlet實(shí)例必須能夠同時(shí)服務(wù)于多個(gè)客戶端請(qǐng)求,即service()方法運(yùn)行在多線程的環(huán)境下代虾,Servlet開發(fā)者必須保證該方法的線程安全性进肯;
- destroy()方法:當(dāng)Servlet對(duì)象退出生命周期時(shí),負(fù)責(zé)釋放占用的資源棉磨;
編程注意事項(xiàng)說明:
- 當(dāng)Server Thread線程執(zhí)行Servlet實(shí)例的init()方法時(shí)江掩,所有的Client Service Thread線程都不能執(zhí)行該實(shí)例的service()方法,更沒有線程能夠執(zhí)行該實(shí)例的destroy()方法乘瓤,因此Servlet的init()方法是工作在單線程的環(huán)境下环形,開發(fā)者不必考慮任何線程安全的問題。
- 當(dāng)服務(wù)器接收到來自客戶端的多個(gè)請(qǐng)求時(shí)衙傀,服務(wù)器會(huì)在單獨(dú)的Client Service Thread線程中執(zhí)行Servlet實(shí)例的service()方法服務(wù)于每個(gè)客戶端斟赚。此時(shí)會(huì)有多個(gè)線程同時(shí)執(zhí)行同一個(gè)Servlet實(shí)例的service()方法,因此必須考慮線程安全的問題差油。
- 請(qǐng)大家注意,雖然service()方法運(yùn)行在多線程的環(huán)境下任洞,并不一定要同步該方法蓄喇。而是要看這個(gè)方法在執(zhí)行過程中訪問的資源類型及對(duì)資源的訪問方式。分析如下:
如果service()方法沒有訪問Servlet的成員變量也沒有訪問全局的資源比如靜態(tài)變量交掏、文件妆偏、數(shù)據(jù)庫連接等,而是只使用了當(dāng)前線程自己的資源盅弛,比如非指向全局資源的臨時(shí)變量钱骂、request和response對(duì)象等。該方法本身就是線程安全的挪鹏,不必進(jìn)行任何的同步控制见秽。
如果service()方法訪問了Servlet的成員變量,但是對(duì)該變量的操作是只讀操作讨盒,該方法本身就是線程安全的解取,不必進(jìn)行任何的同步控制。
如果service()方法訪問了Servlet的成員變量返顺,并且對(duì)該變量的操作既有讀又有寫禀苦,通常需要加上同步控制語句蔓肯。
如果service()方法訪問了全局的靜態(tài)變量,如果同一時(shí)刻系統(tǒng)中也可能有其它線程訪問該靜態(tài)變量振乏,如果既有讀也有寫的操作蔗包,通常需要加上同步控制語句。
如果service()方法訪問了全局的資源慧邮,比如文件调限、數(shù)據(jù)庫連接等,通常需要加上同步控制語句赋咽。
在創(chuàng)建一個(gè) Java servlet 時(shí)旧噪,一般需要子類 HttpServlet。該類中的方法允許您訪問請(qǐng)求和響應(yīng)包裝器(wrapper)脓匿,您可以用這個(gè)包裝器來處理請(qǐng)求和創(chuàng)建響應(yīng)淘钟。大多數(shù)程序員都知道Servlet的生命周期,簡(jiǎn)單的概括這就分為四步:
Servlet類加載--->實(shí)例化--->服務(wù)--->銷毀陪毡;
創(chuàng)建Servlet對(duì)象的時(shí)機(jī):
- 默認(rèn)情況下米母,在Servlet容器啟動(dòng)后:客戶首次向Servlet發(fā)出請(qǐng)求,Servlet容器會(huì)判斷內(nèi)存中是否存在指定的Servlet對(duì)象毡琉,如果沒有則創(chuàng)建它铁瞒,然后根據(jù)客戶的請(qǐng)求創(chuàng)建HttpRequest、HttpResponse對(duì)象桅滋,從而調(diào)用Servlet對(duì)象的service方法慧耍;
- Servlet容器啟動(dòng)時(shí):當(dāng)web.xml文件中如果<servlet>元素中指定了<load-on-startup>子元素時(shí),Servlet容器在啟動(dòng)web服務(wù)器時(shí)丐谋,將按照順序創(chuàng)建并初始化Servlet對(duì)象芍碧;
- Servlet的類文件被更新后,重新創(chuàng)建Servlet号俐。Servlet容器在啟動(dòng)時(shí)自動(dòng)創(chuàng)建Servlet泌豆,這是由在web.xml文件中為Servlet設(shè)置的<load-on-startup>屬性決定的。從中我們也能看到同一個(gè)類型的Servlet對(duì)象在Servlet容器中以單例的形式存在吏饿;
注意:在web.xml文件中踪危,某些Servlet只有
<serlvet>
元素,沒有<servlet-mapping>
元素猪落,這樣我們無法通過url的方式訪問這些Servlet贞远,這種Servlet通常會(huì)在<servlet>
元素中配置一個(gè)<load-on-startup>
子元素,讓容器在啟動(dòng)的時(shí)候自動(dòng)加載這些Servlet并調(diào)用init(ServletConfig config)方法來初始化該Servlet笨忌。其中方法參數(shù)config中包含了Servlet的配置信息兴革,比如初始化參數(shù),該對(duì)象由服務(wù)器創(chuàng)建。
銷毀Servlet對(duì)象的時(shí)機(jī):
Servlet容器停止或者重新啟動(dòng):Servlet容器調(diào)用Servlet對(duì)象的destroy方法來釋放資源杂曲。以上所講的就是Servlet對(duì)象的生命周期庶艾。那么Servlet容器如何知道創(chuàng)建哪一個(gè)Servlet對(duì)象?Servlet對(duì)象如何配置擎勘?實(shí)際上這些信息是通過讀取web.xml配置文件來實(shí)現(xiàn)的咱揍。
<servlet>
<!-- Servlet對(duì)象的名稱 -->
<servlet-name>action<servlet-name>
<!-- 創(chuàng)建Servlet對(duì)象所要調(diào)用的類 -->
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<!-- 參數(shù)名稱 -->
<param-name>config</param-name>
<!-- 參數(shù)值 -->
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<!-- Servlet容器啟動(dòng)時(shí)加載Servlet對(duì)象的順序 -->
<load-on-startup>2</load-on-startup>
</servlet>
<!-- 要與servlet中的servlet-name配置節(jié)內(nèi)容對(duì)應(yīng) -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<!-- 客戶訪問的Servlet的相對(duì)URL路徑 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
當(dāng)Servlet容器啟動(dòng)的時(shí)候讀取<servlet>配置節(jié)信息,根據(jù)<servlet-class>配置節(jié)信息創(chuàng)建Servlet對(duì)象棚饵,同時(shí)根據(jù)<init-param>配置節(jié)信息創(chuàng)建HttpServletConfig對(duì)象煤裙,然后執(zhí)行Servlet對(duì)象的init方法,并且根據(jù)<load-on-startup>配置節(jié)信息來決定創(chuàng)建Servlet對(duì)象的順序噪漾,如果此配置節(jié)信息為負(fù)數(shù)或者沒有配置硼砰,那么在Servlet容器啟動(dòng)時(shí),將不加載此Servlet對(duì)象欣硼。當(dāng)客戶訪問Servlet容器時(shí)题翰,Servlet容器根據(jù)客戶訪問的URL地址,通過<servlet-mapping>配置節(jié)中的<url-pattern>配置節(jié)信息找到指定的Servlet對(duì)象诈胜,并調(diào)用此Servlet對(duì)象的service方法豹障。
在整個(gè)Servlet的生命周期過程中,創(chuàng)建Servlet實(shí)例焦匈、調(diào)用實(shí)例的init()和destroy()方法都只進(jìn)行一次血公,當(dāng)初始化完成后,Servlet容器會(huì)將該實(shí)例保存在內(nèi)存中缓熟,通過調(diào)用它的service()方法累魔,為接收到的請(qǐng)求服務(wù)。下面給出Servlet整個(gè)生命周期過程的UML序列圖够滑,如圖所示:
如果需要讓Servlet容器在啟動(dòng)時(shí)即加載Servlet垦写,可以在web.xml文件中配置<load-on-startup>元素。
7.2 Servlet工作原理##
上面描述了Servlet的生命周期版述,接著我們描述一下Tomcat與Servlet是如何工作的,首先看下面的時(shí)序圖:
- Web Client 向Servlet容器(Tomcat)發(fā)出Http請(qǐng)求寞冯;
- Servlet容器接收Web Client的請(qǐng)求渴析;
- Servlet容器創(chuàng)建一個(gè)HttpRequest對(duì)象,將Web Client請(qǐng)求的信息封裝到這個(gè)對(duì)象中吮龄;
- Servlet容器創(chuàng)建一個(gè)HttpResponse對(duì)象俭茧;
- Servlet容器調(diào)用HttpServlet對(duì)象的service方法,把HttpRequest對(duì)象與HttpResponse對(duì)象作為參數(shù)傳給 HttpServlet對(duì)象漓帚;
- HttpServlet調(diào)用HttpRequest對(duì)象的有關(guān)方法母债,獲取Http請(qǐng)求信息;
- HttpServlet調(diào)用HttpResponse對(duì)象的有關(guān)方法,生成響應(yīng)數(shù)據(jù)毡们;
- Servlet容器把HttpServlet的響應(yīng)結(jié)果傳給Web Client迅皇;
7.3 CGI與Servlet比較##
CGI應(yīng)用開發(fā)比較困難,因?yàn)樗蟪绦騿T有處理參數(shù)傳遞的知識(shí)衙熔,這不是一種通用的技能登颓。CGI不可移植,為某一特定平臺(tái)編寫的CGI應(yīng)用只能運(yùn)行于這一環(huán)境中红氯。每一個(gè)CGI應(yīng)用存在于一個(gè)由客戶端請(qǐng)求激活的進(jìn)程中框咙,并且在請(qǐng)求被服務(wù)后被卸載。這種模式將引起很高的內(nèi)存痢甘、CPU開銷喇嘱,而且在同一進(jìn)程中不能服務(wù)多個(gè)客戶。
Servlet對(duì)CGI的最主要優(yōu)勢(shì)在于一個(gè)Servlet被客戶端發(fā)送的第一個(gè)請(qǐng)求激活塞栅,然后它將繼續(xù)運(yùn)行于后臺(tái)者铜,等待以后的請(qǐng)求。每個(gè)請(qǐng)求將生成一個(gè)新的線程构蹬,而不是一個(gè)完整的進(jìn)程王暗。多個(gè)客戶能夠在同一個(gè)進(jìn)程中同時(shí)得到服務(wù)。一般來說庄敛,Servlet進(jìn)程只是在Web Server卸載時(shí)被卸載俗壹。
Servlet提供了Java應(yīng)用程序的所有優(yōu)勢(shì)——可移植、穩(wěn)健藻烤、易開發(fā)绷雏。使用Servlet Tag技術(shù),Servlet能夠生成嵌于靜態(tài)HTML頁面中的動(dòng)態(tài)內(nèi)容怖亭。
綜上涎显,Servlet處于服務(wù)器進(jìn)程中,它通過多線程方式運(yùn)行其service方法兴猩,一個(gè)實(shí)例可以服務(wù)于多個(gè)請(qǐng)求期吓,并且其實(shí)例一般不會(huì)銷毀。 而CGI對(duì)每個(gè)請(qǐng)求都產(chǎn)生新的進(jìn)程倾芝,服務(wù)完成后就銷毀讨勤,所以效率上低于Servlet。
CGI與Servlet的對(duì)比:
對(duì)比一:當(dāng)用戶瀏覽器發(fā)出一個(gè)Http/CGI的請(qǐng)求晨另,或者說調(diào)用一個(gè)CGI程序的時(shí)候潭千,服務(wù)器端就要新啟用一個(gè)進(jìn)程(而且是每次都要調(diào)用),調(diào)用CGI程序越多(特別是訪問量高的時(shí)候)借尿,就要消耗系統(tǒng)越多的處理時(shí)間刨晴,只剩下越來越少的系統(tǒng)資源屉来,對(duì)于用戶來說,只能是漫長(zhǎng)的等待服務(wù)器端的返回頁面了狈癞,這對(duì)于電子商務(wù)激烈發(fā)展的今天來說茄靠,不能不說是一種技術(shù)上的遺憾。
而Servlet充分發(fā)揮了服務(wù)器端的資源并高效的利用亿驾。每次調(diào)用Servlet時(shí)并不是新啟用一個(gè)進(jìn)程嘹黔,而是在一個(gè)Web服務(wù)器的進(jìn)程中共享和分離線程,而線程最大的好處在于可以共享一個(gè)數(shù)據(jù)源莫瞬,使系統(tǒng)資源被有效利用儡蔓。
對(duì)比二:傳統(tǒng)的CGI程序,不具備平臺(tái)無關(guān)性特征疼邀,系統(tǒng)環(huán)境發(fā)生變化喂江,CGI程序就要癱瘓,而Servlet具備Java的平臺(tái)無關(guān)性旁振,在系統(tǒng)開發(fā)過程中保持了系統(tǒng)的可擴(kuò)展性获询、高效性。
對(duì)比三:傳統(tǒng)技術(shù)中拐袜,一般大都為二層的系統(tǒng)架構(gòu)吉嚣,即Web服務(wù)器+數(shù)據(jù)庫服務(wù)器,導(dǎo)致網(wǎng)站訪問量大的時(shí)候蹬铺,無法克服CGI程序與數(shù)據(jù)庫建立連接時(shí)速度慢的瓶頸尝哆,從而死機(jī)、數(shù)據(jù)庫死鎖現(xiàn)象頻繁發(fā)生甜攀。而Servlet有連接池的概念秋泄,它可以利用多線程的優(yōu)點(diǎn),在系統(tǒng)緩存中事先建立好若干與數(shù)據(jù)庫的連接规阀,到時(shí)候若想和數(shù)據(jù)庫打交道可以隨時(shí)跟系統(tǒng)"要"一個(gè)連接即可恒序,反應(yīng)速度可想而知。
8 Tomcat工作原理#
Tomcat 的結(jié)構(gòu)很復(fù)雜谁撼,但是 Tomcat 也非常的模塊化歧胁,找到了 Tomcat 最核心的模塊,您就抓住了 Tomcat 的“七寸”厉碟。下面是 Tomcat 的總體結(jié)構(gòu)圖:
從上圖可以看出Tomcat的核心是兩個(gè)組件:連接器(Connector)和容器(Container)喊巍。Connector組件是負(fù)責(zé)生成請(qǐng)求對(duì)象和響應(yīng)對(duì)象的,Tomcat默認(rèn)的是HttpConnector墨榄,負(fù)責(zé)根據(jù)收到的Http請(qǐng)求報(bào)文生成Request對(duì)象和Response對(duì)象玄糟,并把這兩個(gè)對(duì)象傳遞給Container勿她,然后根據(jù)Response中的內(nèi)容生成相應(yīng)的HTTP報(bào)文袄秩。
Container是容器的父接口,所有子容器都必須實(shí)現(xiàn)這個(gè)接口,簡(jiǎn)單來說就是服務(wù)器部署的項(xiàng)目是運(yùn)行在Container中的之剧。Container里面的項(xiàng)目獲取到Connector傳遞過來對(duì)應(yīng)的的Request對(duì)象和Response對(duì)象進(jìn)行相應(yīng)的操作郭卫。
Connector可以根據(jù)不同的設(shè)計(jì)和應(yīng)用場(chǎng)景進(jìn)行替換。一個(gè)Container可以選擇對(duì)應(yīng)多個(gè)Connector背稼。多個(gè)Connector和一個(gè)Container就形成了一個(gè)Service贰军,有了Service就可以對(duì)外提供服務(wù)了。
Tomcat要為一個(gè)Servlet的請(qǐng)求提供服務(wù)蟹肘,需要做三件事:
- 創(chuàng)建一個(gè)request對(duì)象并填充那些有可能被所引用的Servlet使用的信息词疼,如參數(shù),頭部帘腹、cookies贰盗、查詢字符串等。一個(gè)request對(duì)象就是javax.servlet.ServletRequest或javax.servlet.http.ServletRequest接口的一個(gè)實(shí)例阳欲。
- 創(chuàng)建一個(gè)response對(duì)象舵盈,所引用的servlet使用它來給客戶端發(fā)送響應(yīng)。一個(gè)response對(duì)象是javax.servlet.ServletResponse或javax.servlet.http.ServletResponse接口的一個(gè)實(shí)例球化。
- 調(diào)用servlet的service方法秽晚,并傳入request和response對(duì)象。這里servlet會(huì)從request對(duì)象取值筒愚,給response寫值赴蝇。
- 根據(jù)servlet返回的response生成相應(yīng)的HTTP響應(yīng)報(bào)文。
既然我們已經(jīng)抓到Tomcat的“七寸”锨能,兩個(gè)核心組件:連接器(Connector)和容器(Container)扯再,那這樣從連接器(Connector)入手,來看下Tomcat處理HTTP請(qǐng)求的流程址遇。
很多開源應(yīng)用服務(wù)器都是集成tomcat作為web container的熄阻,而且對(duì)于tomcat的servlet container這部分代碼很少改動(dòng)。這樣倔约,這些應(yīng)用服務(wù)器的性能基本上就取決于Tomcat處理HTTP請(qǐng)求的connector模塊的性能秃殉。
8.1 Connector種類##
Tomcat源碼中與connector相關(guān)的類位于org.apache.coyote包中,Connector分為以下幾類:
Http Connector浸剩,基于HTTP協(xié)議钾军,負(fù)責(zé)建立HTTP連接。它又分為BIO Http Connector與NIO Http Connector兩種绢要,后者提供非阻塞IO與長(zhǎng)連接Comet支持吏恭。
AJP Connector,基于AJP協(xié)議重罪,AJP是專門設(shè)計(jì)用來為tomcat與http服務(wù)器之間通信專門定制的協(xié)議樱哼,能提供較高的通信速度和效率哀九。如與Apache服務(wù)器集成時(shí),采用這個(gè)協(xié)議搅幅。
APR HTTP Connector阅束,用C實(shí)現(xiàn),通過JNI調(diào)用的茄唐。主要提升對(duì)靜態(tài)資源(如HTML息裸、圖片、CSS沪编、JS等)的訪問性能『襞瑁現(xiàn)在這個(gè)庫已獨(dú)立出來可用在任何項(xiàng)目中。Tomcat在配置APR之后性能非常強(qiáng)勁蚁廓。
8.2 Connector配置##
對(duì)Connector的配置位于conf/server.xml文件中宿亡。
8.2.1 BIO HTTP/1.1 Connector配置###
<Connector port=”8080” protocol=”HTTP/1.1” maxThreads=”150”
connectionTimeout=”20000” redirectPort=”8443” />
其它一些重要屬性如下:
acceptCount : 接受連接request的最大連接數(shù)目,默認(rèn)值是10纳令;
address : 綁定IP地址挽荠,如果不綁定,默認(rèn)將綁定任何IP地址平绩;
allowTrace : 如果是true,將允許TRACE HTTP方法圈匆;
compressibleMimeTypes : 各個(gè)mimeType, 以逗號(hào)分隔,如text/html,text/xml捏雌;
compression : 如果帶寬有限的話跃赚,可以用GZIP壓縮;
connectionTimeout : 超時(shí)時(shí)間性湿,默認(rèn)為60000ms (60s)纬傲;
maxKeepAliveRequest : 默認(rèn)值是100;
maxThreads : 處理請(qǐng)求的Connector的線程數(shù)目肤频,默認(rèn)值為200叹括;
如果是SSL配置,如下:
<Connector port="8181" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol = "TLS"
address="0.0.0.0"
keystoreFile="E:/java/jonas-full-5.1.0-RC3/conf/keystore.jks"
keystorePass="changeit" />
其中宵荒,keystoreFile為證書位置汁雷,keystorePass為證書密碼。
8.2.2 NIO HTTP/1.1 Connector配置###
<Connector port=”8080” protocol=”org.apache.coyote.http11.Http11NioProtocol”
maxThreads=”150” connectionTimeout=”20000” redirectPort=”8443” />
8.2.3 Native APR Connector配置###
- ARP是用C/C++寫的报咳,對(duì)靜態(tài)資源(HTML侠讯,圖片等)進(jìn)行了優(yōu)化。所以要下載本地庫tcnative-1.dll與openssl.exe暑刃,將其放在%tomcat%\bin目錄下厢漩。
下載地址是:http://tomcat.heanet.ie/native/1.1.10/binaries/win32/
- 在server.xml中要配置一個(gè)Listener,如下圖。這個(gè)配置tomcat是默認(rèn)配好的岩臣。
<!--APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
- 配置使用APR connector
<Connector port=”8080” protocol=”org.apache.coyote.http11.Http11AprProtocol”
maxThreads=”150” connectionTimeout=”20000” redirectPort=”8443” />
- 如果配置成功溜嗜,啟動(dòng)tomcat,會(huì)看到如下信息:
org.apache.coyote.http11.Http11AprProtocol init
8.3 Tomcat架構(gòu)模塊##
- Server(服務(wù)器)是Tomcat構(gòu)成的頂級(jí)構(gòu)成元素柴底,所有一切均包含在Server中,Server的實(shí)現(xiàn)類StandardServer可以包含一個(gè)到多個(gè)Services;
- 次頂級(jí)元素Service的實(shí)現(xiàn)類為StandardService調(diào)用了容器(Container)接口粱胜,其實(shí)是調(diào)用了Servlet Engine(引擎),而且StandardService類中也指明了該Service歸屬的Server狐树;
- 接下來次級(jí)的構(gòu)成元素就是容器(Container):主機(jī)(Host)焙压、上下文(Context)和引擎(Engine)均繼承自Container接口,所以它們都是容器抑钟。但是涯曲,它們是有父子關(guān)系的,在主機(jī)(Host)在塔、上下文(Context)和引擎(Engine)這三類容器中幻件,引擎是頂級(jí)容器,直接包含是主機(jī)容器蛔溃,而主機(jī)容器又包含上下文容器绰沥,所以引擎、主機(jī)和上下文從大小上來說又構(gòu)成父子關(guān)系贺待,雖然它們都繼承自Container接口徽曲。
- 連接器(Connector)將Service和Container連接起來,首先它需要注冊(cè)到一個(gè)Service麸塞,它的作用就是把來自客戶端的請(qǐng)求轉(zhuǎn)發(fā)到Container(容器)秃臣,這就是它為什么稱作連接器的原因。
8.4 Tomcat運(yùn)行流程##
假設(shè)來自客戶的請(qǐng)求為:http://localhost:8080/test/index.jsp
- 請(qǐng)求被發(fā)送到本機(jī)端口8080哪工,被在那里偵聽的Coyote HTTP/1.1 Connector獲得奥此;
- Connector把該請(qǐng)求交給它所在的Service的Engine來處理,并等待Engine的回應(yīng)雁比;
- Engine獲得請(qǐng)求localhost:8080/test/index.jsp稚虎,匹配它所有虛擬主機(jī)Host;
- 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去處理)蜕径;
- path="/test"的Context獲得請(qǐng)求/index.jsp,在它的mapping table中尋找對(duì)應(yīng)的servlet败京;
- Context匹配到URL PATTERN為*.jsp的servlet兜喻,對(duì)應(yīng)于JspServlet類;
- 構(gòu)造HttpServletRequest對(duì)象和HttpServletResponse對(duì)象赡麦,作為參數(shù)調(diào)用JspServlet的doGet或doPost方法朴皆;
- Context把執(zhí)行完了之后的HttpServletResponse對(duì)象返回給Host帕识;
- Host把HttpServletResponse對(duì)象返回給Engine;
- Engine把HttpServletResponse對(duì)象返回給Connector遂铡;
- Connector把HttpServletResponse對(duì)象返回給客戶browser肮疗;