常見的http服務(wù)器有apache,nginx,iis西采,tomcat等。HTTP服務(wù)器本質(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格式)等等資源冶忱。
??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)求生成HttpServletResponse對(duì)象并傳遞給Servlet進(jìn)行處理眶拉,將Servlet中的HttpServletResponse對(duì)象生成的內(nèi)容返回給瀏覽器千埃。
自己的第一門語言就是java,所以tomcat一直在用忆植,但也基本只是在開發(fā)環(huán)境用一下放可,具體的參數(shù)也就改過JVM內(nèi)存,沒做過深入研究朝刊,主要還是因?yàn)樽约汗ぷ饕恢睕]有需要去優(yōu)化過tomcat耀里。
??最近由于工作上的原因,需要對(duì)tomcat并發(fā)進(jìn)行優(yōu)化拾氓。在tomcat的server.xml文件中有關(guān)于請(qǐng)求連接的配置Connector冯挎。本文就具體屬性進(jìn)行一下說明。
Connector的protocol屬性咙鞍,三種運(yùn)行模式(BIO, NIO, APR)
1)BIO:一個(gè)線程處理一個(gè)請(qǐng)求房官。缺點(diǎn):并發(fā)量高時(shí),線程數(shù)較多续滋,浪費(fèi)資源翰守。Tomcat7或以下在Linux系統(tǒng)中默認(rèn)使用這種方式。
2)NIO:利用Java的異步IO處理疲酌,可以通過少量的線程處理大量的請(qǐng)求蜡峰。Tomcat8在Linux系統(tǒng)中默認(rèn)使用這種方式。Tomcat7必須修改Connector配置來啟動(dòng)(conf/server.xml配置文件) protocol="org.apache.coyote.http11.Http11NioProtocol"
3)APR(Apache Portable Runtime):從操作系統(tǒng)層面解決io阻塞問題朗恳。Linux如果安裝了apr和native湿颅,Tomcat直接啟動(dòng)就支持apr。在Tomcat.7.0.30版本開始僻肖,tomcat自帶tcnative-1.dll等文件肖爵,windows上默認(rèn)是運(yùn)行apr模式。
linux安裝apr和native
apr是比較惡心的一個(gè)東東臀脏,因?yàn)橛弥苯佑脃um install apr安裝apr后劝堪,當(dāng)再安裝其他東西需要apr環(huán)境時(shí)候 經(jīng)常還是找不到,盡管已經(jīng)安裝它了揉稚。需要的幾個(gè)環(huán)境:
??#yum -y install autoconf // 安裝autoconf
??#yum -y install libtool // 安裝libtool
??#yum -y install openssl openssl-devel // 安裝openssl
這樣的話我們只能通過下面這兩個(gè)參數(shù)來指定他們的位置了
apr官網(wǎng)下載地址 http://apr.apache.org/download.cgi
安裝apr:
??#tar xvzf apr-1.5.2.tar.gz // 解壓apr-1.5.2.tar.gz
??#cd apr-1.5.2 // 進(jìn)入apr-1.5.2目錄
??#./configure --prefix=/usr/apr // 指定安裝到/usr/apr目錄
??#make & make install
安裝tomcat-native:
??#cd /usr/tomcat/apache-tomcat-7.0.59/bin // 切換目錄秒啦,在tomcat/bin目錄下找到tomcat-native.tar.gz;
??#tar xvzf tomcat-native.tar.gz // 解壓tomcat-native.tar.gz
??#cd /usr/tomcat/apache-tomcat-7.0.59/bin/tomcat-native-1.1.32-src/jni/native // 切換目錄
??#./configure --with-apr=/usr/local/apr --with-java-home=/usr/java/jdk1.8.0_11 --with-ssl=/usr/bin --prefix=/usr/local/apr // 指定之前安裝的目錄
??#make & make install
配置APR本地庫到系統(tǒng)共享庫搜索路徑中
??編輯$TOMCAT_HOME/bin/catalina.sh文件,在虛擬機(jī)啟動(dòng)參數(shù)JAVA_OPTS中添加java.library.path參數(shù)搀玖,指定apr庫的路徑
JAVA_OPTS="$JAVA_OPTS -Djava.library.path=/usr/local/apr/lib"
??Tomcat8以下版本余境,需要指定運(yùn)行模式,將protocol從HTTP/1.1改成org.apache.coyote.http11.Http11AprProtocol
這里重點(diǎn)比較一下BIO和NIO,在百度很多文章都是說BIO是一個(gè)請(qǐng)求一個(gè)線程芳来,NIO可以一個(gè)線程服務(wù)多個(gè)請(qǐng)求含末。字面意思很清楚,但具體細(xì)節(jié)卻不是很明白即舌。雖然網(wǎng)上也有測(cè)試說明NIO在高并發(fā)的時(shí)候比BIO性能要好佣盒。我也做了一個(gè)簡單的實(shí)驗(yàn)。
??這里需要優(yōu)化的就是網(wǎng)關(guān)tomcat顽聂,這里的網(wǎng)關(guān)tomcat主要起到的作用主要是驗(yàn)證請(qǐng)求者肥惭,然后通過http請(qǐng)求調(diào)用后端服務(wù),沒有復(fù)雜的業(yè)務(wù)邏輯處理紊搪。后端服務(wù)的提供者有很多蜜葱,圖中只舉例了兩個(gè),各個(gè)服務(wù)由于業(yè)務(wù)不同耀石,處理時(shí)間也不一樣牵囤,有耗時(shí)的服務(wù),也有快速返回的服務(wù)滞伟。網(wǎng)關(guān)tomcat如果按照默認(rèn)的配置奔浅,則網(wǎng)關(guān)tomcat的服務(wù)器數(shù)量必須大于等于后端服務(wù)中并發(fā)量最大的那個(gè)服務(wù)的機(jī)器數(shù)量。這就形成了網(wǎng)關(guān)tomcat成為了整個(gè)系統(tǒng)的一個(gè)瓶頸诗良,但網(wǎng)關(guān)tomcat只是起到一個(gè)網(wǎng)關(guān)作用汹桦,需要可以像nginx代理服務(wù)器那樣可以抗大并發(fā)的能力。
??由于后端機(jī)器使用的低配置的阿里云鉴裹,10秒的耗時(shí)應(yīng)用的并發(fā)能力不到100舞骆,也就是兩臺(tái)機(jī)器通過nginx負(fù)載均衡后200的并發(fā)。這里為了測(cè)試方便把網(wǎng)關(guān)tomcat的maxThreads設(shè)置為50径荔。
首先做一個(gè)測(cè)試比較protocol分別為BIO和NIO時(shí)督禽,并發(fā)請(qǐng)求耗時(shí)應(yīng)用的情況。
這里測(cè)試了三種情況总处,分別為并發(fā)50狈惫,并發(fā)100,并發(fā)200鹦马,總請(qǐng)求數(shù)都是500胧谈,每種情況測(cè)試3次。通過圖片數(shù)據(jù)可以看到BIO和NIO的結(jié)果沒有什么區(qū)別荸频。NIO并有像描述的那樣一個(gè)線程可以處理更多的請(qǐng)求連接菱肖。網(wǎng)上也有實(shí)驗(yàn)數(shù)據(jù)說明NIO確實(shí)在并發(fā)處理上比BIO效果要好很多,但這個(gè)實(shí)驗(yàn)就不行旭从,為什么呢稳强?后來在知乎上的一篇文章上找到了點(diǎn)原因场仲。
這張表格引用自apache-tomcat官方網(wǎng)站,對(duì)于connector的三種模式有很好的對(duì)比退疫,NIO也不是完全非阻塞(讀body和寫response是模擬阻塞行為)的地方用紅色突出了一下渠缕,于header的處理,NIO不會(huì)阻塞褒繁,只有在body的讀取時(shí)褐健,NIO采取模擬阻塞的方式。這是由于servlet規(guī)范澜汤,tomcat要實(shí)現(xiàn)servlet規(guī)范所以不能最大發(fā)揮NIO的特性。在read http body 以及 response的情況下舵匾,即使tomcat選擇 NIO的 connector也是模擬阻塞的行為俊抵,因?yàn)閟ervlet規(guī)范里定義了ServletInputStream在讀數(shù)據(jù)時(shí)是阻塞模式。
關(guān)于Wait for next Request
它表示的是當(dāng)開啟keep-alive的情況下三種模式對(duì)等待下一次請(qǐng)求是否阻塞坐梯。
在BIO模式下徽诲,如果請(qǐng)求是開啟keep-alive的話,socket在請(qǐng)求結(jié)束后仍處于OPEN狀態(tài)吵血,下一次請(qǐng)求仍可以復(fù)用當(dāng)前socket而不必重新創(chuàng)建谎替,在 finally 塊里會(huì)判斷連接狀況如果是keep-alive會(huì)再次封裝為同樣的任務(wù)提交給線程池,重復(fù)這段邏輯蹋辅,相當(dāng)于一個(gè)循環(huán)钱贯,只不過每次執(zhí)行的線程不一定相同。如果socket上已經(jīng)沒有請(qǐng)求了侦另,最終socket會(huì)因超時(shí)或客戶端close造成的EOFException而關(guān)閉秩命。也就是說在BIO下的keep-alive的請(qǐng)求,只有等到keep-alive超時(shí)或者達(dá)到maxKeepAliveRequests時(shí)才會(huì)把線程放回tomcat的線程池褒傅,服務(wù)其他請(qǐng)求弃锐。
在NIO和APR模式下,即使開啟keep-alive殿托,在Wait for next Request時(shí)候霹菊,這個(gè)線程也是可以服務(wù)其他請(qǐng)求的。
??看了上面這么多也就知道為什么會(huì)出現(xiàn)實(shí)驗(yàn)的效果了支竹,NIO的非阻塞并不能完全解決我這個(gè)業(yè)務(wù)場(chǎng)景旋廷,因?yàn)榧词故荖IO下,在read body時(shí)也還是阻塞的礼搁,而我當(dāng)前的業(yè)務(wù)場(chǎng)景是在serlvet中請(qǐng)求服務(wù)耗時(shí)柳洋。servlet的規(guī)范ServletInputStream在讀數(shù)據(jù)時(shí)是阻塞模式這一特性是問題根本節(jié)點(diǎn)。通過調(diào)整maxThreads會(huì)是解決這種并發(fā)的一個(gè)方式叹坦。
??在查找上面BIO和NIO比較的時(shí)候熊镣,我看到有人說到了servlet3.0中有異步serlvet,可以解決serlvet阻塞的問題。于是我使用異步servlet重試了上訴實(shí)驗(yàn):
通過實(shí)驗(yàn)結(jié)果可以看到在NIO下使用異步serlvet確實(shí)可以達(dá)到想要的效果绪囱,tomcat網(wǎng)關(guān)在線程數(shù)50時(shí)也能處理并發(fā)200個(gè)請(qǐng)求测蹲。
AsyncContext asyncContext = request.startAsync();
asyncContext.start();//如果使用start方法膊爪,雖然把請(qǐng)求交給了另一個(gè)線程瓤摧,但這個(gè)線程還是使用的tomcat的線程宙址。
//這里最好使用一個(gè)線程池否灾,為了簡化測(cè)試炬太,我直接使用new Thread
new Thread(new AsyncRequest(asyncContext)).start();//這樣就可以把tomcat的線程還回給tomcat線程池接收新請(qǐng)求了锭部。
Connector的其他屬性
connectionTimeout:網(wǎng)絡(luò)連接超時(shí)凌受,單位:毫秒决摧。設(shè)置為0表示永不超時(shí)涣脚,通呈颈玻可設(shè)置為20000毫秒。
URIEncoding:指定Tomcat 容器的URL編碼格式遣蚀。
enableLookups:如果為true矾麻,則可以通過調(diào)用request.getRemoteHost()進(jìn)行DNS查詢來得到遠(yuǎn)程客戶端的實(shí)際主機(jī)名,若為false則不進(jìn)行DNS查詢芭梯,而是返回其ip地址险耀。建議設(shè)置false,能提高部分性能玖喘。
maxThreads:tomcat起動(dòng)的最大線程數(shù)甩牺,即同時(shí)處理的任務(wù)個(gè)數(shù),默認(rèn)值為200
acceptCount:當(dāng)tomcat起動(dòng)的線程數(shù)達(dá)到最大時(shí)累奈,接受排隊(duì)的請(qǐng)求個(gè)數(shù)柴灯,默認(rèn)值為100
minSpareThreads:表示即使沒有人使用也開這么多空線程等待
maxConnections:這個(gè)貌似應(yīng)該是新加的參數(shù)吧,我沒求證费尽。服務(wù)器能接受和處理的最大數(shù)量赠群,bio默認(rèn)是maxThreads的值,nio默認(rèn)10000旱幼,APR/native 默認(rèn)是8192查描。
keepAliveTimeout:這個(gè)是保持長連接的時(shí)間限制,默認(rèn)就是connectionTimeout設(shè)置的時(shí)間柏卤。
關(guān)于keepAlive冬三,tomcat長連接短連接
1.WEB應(yīng)用有很多,下面就兩個(gè)典型的應(yīng)用(管理頁面和接口服務(wù))做對(duì)比缘缚。
管理頁面:多涉及到用戶的登錄和長時(shí)間的頻繁操作處理勾笆,這些操作都集中在一個(gè)session中,建議采用長連接桥滨;
接口服務(wù):比如常見的webservice,操作集中在很短時(shí)間內(nèi)完成窝爪,不需要對(duì)session進(jìn)行維護(hù)弛车,建議采用短連接。
HTTP1.1默認(rèn)采用長連接蒲每,需要去掉長連接的話纷跛,只需修改默認(rèn)配置參數(shù)maxKeepAliveRequests。把maxKeepAliveRequests="5"邀杏,意思是每個(gè)連接只響應(yīng)5個(gè)請(qǐng)求贫奠,然后就shutdown連接.不用等到keepAliveTimeout就關(guān)閉這個(gè)連接
Apache優(yōu)化之KeepAlive
1)KeepAlive是在HTTP1.1中定義的,用來保持客戶機(jī)和服務(wù)器的長連接望蜡,通過減少建立TCP Session的次數(shù)來提高性能唤崭。
2)常用的配置參數(shù)有{KeepAlive,KeepAliveTimeout,MaxKeepAliveRequests}
KeepAlive是決定開啟KeepAlive支持;
KeepAliveTimeout決定一 個(gè)KeepAlive的連接能保持多少時(shí)間,Timeout就盡快shutdown鏈接脖律,若還有數(shù)據(jù)必須再建立新的連接了;
MaxKeepAliveRequests于KeepAliveTimeout相似谢肾,意思是服務(wù)多少個(gè)請(qǐng)求就shutdown連接。
3)對(duì)于KeepAlive的配置需要慎重状您,錯(cuò)誤的參數(shù)可能導(dǎo)致嚴(yán)重的性能問題。
一個(gè)高負(fù)載的Server兜挨,如果建立的很多長連接將無法繼續(xù)服 務(wù)新的連接膏孟。因此需要根據(jù)server的性質(zhì)調(diào)整KeepAliveTimeout或是MaxKeepAliveRequests的值。
其他:tomcat內(nèi)存優(yōu)化
主要是在bin/catalina.bat或bin/catalina.sh 配置文件中進(jìn)行拌汇。linux上柒桑,在catalina.sh中添加:
JAVA_OPTS="-server -Xms1G -Xmx2G -Xss256K -Djava.awt.headless=true -Dfile.encoding=utf-8 -XX:MaxPermSize=256m -XX:PermSize=128M -XX:MaxPermSize=256M"
其中:
? -server:啟用jdk的server版本。
? -Xms:虛擬機(jī)初始化時(shí)的最小堆內(nèi)存噪舀。
? -Xmx:虛擬機(jī)可使用的最大堆內(nèi)存魁淳。
?-Xms與-Xmx設(shè)成一樣的值,避免JVM因?yàn)轭l繁的GC導(dǎo)致性能大起大落
? -XX:PermSize:設(shè)置非堆內(nèi)存初始值,默認(rèn)是物理內(nèi)存的1/64与倡。
? -XX:MaxNewSize:新生代占整個(gè)堆內(nèi)存的最大值界逛。
? -XX:MaxPermSize:Perm(俗稱方法區(qū))占整個(gè)堆內(nèi)存的最大值,也稱內(nèi)存最大永久保留區(qū)域纺座。
1)錯(cuò)誤提示:java.lang.OutOfMemoryError:Java heap space
Tomcat默認(rèn)可以使用的內(nèi)存為128MB息拜,在較大型的應(yīng)用項(xiàng)目中,這點(diǎn)內(nèi)存是不夠的净响,有可能導(dǎo)致系統(tǒng)無法運(yùn)行少欺。常見的問題是報(bào)Tomcat內(nèi)存溢出錯(cuò)誤,Outof Memory(系統(tǒng)內(nèi)存不足)的異常馋贤,從而導(dǎo)致客戶端顯示500錯(cuò)誤赞别,一般調(diào)整Tomcat的-Xms和-Xmx即可解決問題,通常將-Xms和-Xmx設(shè)置成一樣配乓,堆的最大值設(shè)置為物理可用內(nèi)存的最大值的80%仿滔。
set JAVA_OPTS=-Xms512m-Xmx512m
2)錯(cuò)誤提示:java.lang.OutOfMemoryError: PermGenspace
PermGenspace的全稱是Permanent Generationspace,是指內(nèi)存的永久保存區(qū)域惠毁,這塊內(nèi)存主要是被JVM存放Class和Meta信息的,Class在被Loader時(shí)就會(huì)被放到PermGenspace中,它和存放類實(shí)例(Instance)的Heap區(qū)域不同,GC(Garbage Collection)不會(huì)在主程序運(yùn)行期對(duì)PermGenspace進(jìn)行清理堤撵,所以如果你的應(yīng)用中有很CLASS的話,就很可能出現(xiàn)PermGen space錯(cuò)誤仁讨,這種錯(cuò)誤常見在web服務(wù)器對(duì)JSP進(jìn)行precompile的時(shí)候。如果你的WEB APP下都用了大量的第三方j(luò)ar, 其大小超過了jvm默認(rèn)的大小(4M)那么就會(huì)產(chǎn)生此錯(cuò)誤信息了实昨。解決方法:
setJAVA_OPTS=-XX:PermSize=128M
3)在使用-Xms和-Xmx調(diào)整tomcat的堆大小時(shí)洞豁,還需要考慮垃圾回收機(jī)制。如果系統(tǒng)花費(fèi)很多的時(shí)間收集垃圾荒给,請(qǐng)減小堆大小丈挟。一次完全的垃圾收集應(yīng)該不超過3-5 秒。如果垃圾收集成為瓶頸志电,那么需要指定代的大小曙咽,檢查垃圾收集的詳細(xì)輸出,研究垃圾收集參數(shù)對(duì)性能的影響挑辆。一般說來例朱,你應(yīng)該使用物理內(nèi)存的 80% 作為堆大小。當(dāng)增加處理器時(shí)鱼蝉,記得增加內(nèi)存洒嗤,因?yàn)榉峙淇梢圆⑿羞M(jìn)行,而垃圾收集不是并行的魁亦。