tomcat之性能優化
tomcat是我們常用的web容器,它的性能高低直接影響到應用對外提供服務的能力和用戶的體驗,所以tomcat的優化至關重要。對於單臺tomcat服務器而言,優化主要是兩方面:內存優化和配置優化(例如,連接數,線程池,iO等),當然還有使用tomcat原生庫。除了這些tomcat本身固有配置優化,還有一些減少占用tomcat連接時間的配置,比如數據壓縮。如果單個服務器經過優化後依然無法滿足,性能要求,這時服務器集群就是必須的手段了,下面針對這些方面的優化詳細介紹。
內存優化
主要是針對jvm各個內存大小的分配進行優化,以提高內存的使用率和減少資源變化帶來的時間消耗。有關文件:catalina.sh(linux下)或者catalina.bat(window下)。
註意:對於32位操作系統上對jvm的內存有不能超過2G的限制,但是64位系統上沒有這個限制。但很多情況下32操作系統上jvm的最大使用內存是不到2G的,2G只是個理想值,根據服務器的配置不同,可能有些只能用1500M或者1700M。具體支持多少,可以在控制臺輸入:java -Xmx2089M -version,如果出現Java版本,說明你的jvm內存可以設置到2089M。
Linux系統中tomcat的啟動參數:
export JAVA_OPTS="-server -Xms1400M -Xmx1400M -Xss512k -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=256M -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=31 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true "
Windows系統中tomcat的啟動參數:
set JAVA_OPTS=-server -Xms1400M -Xmx1400M -Xss512k -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=256M -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=31 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true
- -server
這個參數必須加上,因為tomcat默認是以一種叫java –client的模式來運行的,server即意味著你的tomcat是以真實的production的模式在運行的,這也就意味著你的tomcat以server模式運行時將擁有:更大、更高的並發處理能力,更快更強捷的JVM垃圾回收機制等。
- -Xms和–Xmx
Xms是JVM初始化時的內存大小;Xmx是jvm的最大內存大小。Xms默認是物理內存的1/64,Xmx默認是物理內存的1/4,建議是物理內存的80%,但是一般我們會把Xms和Xmx設置一樣大,這樣在服務器啟動時就是最大值可以避免內存膨脹和回落時帶來的資源的消耗。特別是回落時會帶來cpu的高速運轉進行垃圾回收,系統可能會出現幾秒甚至幾十秒的卡頓現象。
- –Xmn
Xmn是年輕代大小。整個堆大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m,所以增大年輕代後,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。
- -Xss
是指設定每個線程的堆棧大小。這個就要依據你的程序,看一個線程 大約需要占用多少內存,可能會有多少線程同時運行等。一般不易設置超過1M,要不然容易出現out ofmemory。
- -XX:+AggressiveOpts
作用如其名(aggressive),啟用這個參數,則每當JDK版本升級時,你的JVM都會使用最新加入的優化技術(如果有的話)
- -XX:+UseBiasedLocking
啟用一個優化了的線程鎖,我們知道在我們的appserver,每個http請求就是一個線程,有的請求短有的請求長,就會有請求排隊的現象,甚至還會出現線程阻塞,這個優化了的線程鎖使得你的appserver內對線程處理自動進行最優調配。
- -XX:PermSize=128M和-XX:MaxPermSize=256M
JVM使用-XX:PermSize設置非堆內存初始值,默認是物理內存的1/64;在數據量的很大的文件導出時,一定要把這兩個值設置上,否則會出現內存溢出的錯誤。由XX:MaxPermSize設置最大非堆內存的大小,默認是物理內存的1/4。那麽,如果是物理內存4GB,那麽64分之一就是64MB,這就是PermSize默認值,也就是永生代內存初始大小;四分之一是1024MB,這就是MaxPermSize默認大小。
- -XX:+DisableExplicitGC
在程序代碼中不允許有顯示的調用”System.gc()”。看到過有兩個極品工程中每次在DAO操作結束時手動調用System.gc()一下,覺得這樣做好像能夠解決它們的out ofmemory問題一樣,付出的代價就是系統響應時間嚴重降低,就和我在關於Xms,Xmx裏的解釋的原理一樣,這樣去調用GC導致系統的JVM大起大落,性能不到什麽地方去喲!
- -XX:+UseParNewGC
對年輕代采用多線程並行回收,這樣收得快。
- -XX:+UseConcMarkSweepGC
即CMS gc,這一特性只有jdk1.5即後續版本才具有的功能,它使用的是gc估算觸發和heap占用觸發。
我們知道頻頻繁的GC會造面JVM的大起大落從而影響到系統的效率,因此使用了CMS GC後可以在GC次數增多的情況下,每次GC的響應時間卻很短,比如說使用了CMS GC後經過jprofiler的觀察,GC被觸發次數非常多,而每次GC耗時僅為幾毫秒。
- -XX:MaxTenuringThreshold
設置垃圾最大年齡。如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代。對於年老代比較多的應用,可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概率。
這個值的設置是根據本地的jprofiler監控後得到的一個理想的值,不能一概而論原搬照抄。
- -XX:+CMSParallelRemarkEnabled
在使用UseParNewGC 的情況下, 盡量減少 mark 的時間
- -XX:+UseCMSCompactAtFullCollection
在使用concurrent gc 的情況下, 防止 memoryfragmention, 對live object 進行整理, 使 memory 碎片減少。
- -XX:LargePageSizeInBytes
指定 Java heap的分頁頁面大小
- -XX:+UseFastAccessorMethods
get,set 方法轉成本地代碼
- -XX:+UseCMSInitiatingOccupancyOnly
指示只有在 oldgeneration 在使用了初始化的比例後concurrent collector 啟動收集
- -XX:CMSInitiatingOccupancyFraction=70
CMSInitiatingOccupancyFraction,這個參數設置有很大技巧,基本上滿足(Xmx-Xmn)*(100- CMSInitiatingOccupancyFraction)/100>=Xmn就不會出現promotion failed。在我的應用中Xmx是6000,Xmn是512,那麽Xmx-Xmn是5488兆,也就是年老代有5488 兆,CMSInitiatingOccupancyFraction=90說明年老代到90%滿的時候開始執行對年老代的並發垃圾回收(CMS),這時還 剩10%的空間是5488*10%=548兆,所以即使Xmn(也就是年輕代共512兆)裏所有對象都搬到年老代裏,548兆的空間也足夠了,所以只要滿 足上面的公式,就不會出現垃圾回收時的promotion failed;
因此這個參數的設置必須與Xmn關聯在一起。
- -Djava.awt.headless=true
這個參數一般我們都是放在最後使用的,這全參數的作用是這樣的,有時我們會在我們的J2EE工程中使用一些圖表工具如:jfreechart,用於在web網頁輸出GIF/JPG等流,在winodws環境下,一般我們的app server在輸出圖形時不會碰到什麽問題,但是在linux/unix環境下經常會碰到一個exception導致你在winodws開發環境下圖片顯示的好好可是在linux/unix下卻顯示不出來,因此加上這個參數以免避這樣的情況出現。
配置優化
主要是優化conf/server.xml文件中相關配置參數。
- 連接器
這裏主要是優化connector標簽中的相關參數,用於處理訪問的請求;當然此標簽中也可以設置線程的相關參數,但是為了更好的性能一般我們使用外部的線程池,這樣有利於線程的復用,以減少線程的創建和銷毀帶來的資源消耗;另外,我們也可以修改tomcat的運行模式:BIO,NIO,AIO(NIO2),APR.
- 首先,我們需要禁用AJP協議
AJP連接器:
用於將Apache與Tomcat集成在一起,當Apache接收到動態內容請求時,通過在配置中指定的端口號將請求發送給在此端口號上監聽的AJP連接器組件。
屬性:
backlog:當所有可能的請求處理線程都在使用時,隊列中排隊的請求最大數目。默認為10,當隊列已滿,任何請求都將被拒絕
maxSpareThread:允許存在空閑線程的最大數目,默認值為50
maxThread:最大線程數,默認值為200
minSpareThreads:設當連接器第一次啟動時創建線程的數目,確保至少有這麽多的空閑線程可用,默認值為4
port:服務端套接字的TCP端口號,默認值為8089(必須)
topNoDelay:為true時,可以提高性能,默認值為true
soTimeout:超時值
例:
<!—Define an AJP1.3 Connector on port 8089-->
<Connector port=”8089” enableLookups=”false” redirectPort=”8443” protocol=”AJP/1.3” />
AJPv13協議是面向包的。WEB服務器和Servlet容器通過TCP連接來交互;為了節省SOCKET創建的昂貴代價,WEB服務器會嘗試維護一個永久TCP連接到servlet容器,並且在多個請求和響應周期過程會重用連接。我們一般是使用Nginx+tomcat的架構,所以用不著AJP協議,所以把AJP連接器禁用。
2.Connector標簽常見參數的配置:
<Connector port="8080" protocol="HTTP/1.1"
//編碼格式
URIEncoding="UTF-8"
//線程配置
maxThreads="1000" maxProcessors="1000" minProcessors="5" minSpareThreads="100" maxSpareThreads="1000
//連接數配置
maxConnections="1000"
connectionTimeout="20000" acceptCount="1000"
//是否關閉DNS的反響查詢 false:關閉,true:開啟
enableLookups="false"
//是否保持長時間連接,false:關閉,ture:開啟
disableUploadTimeout="true"
//是否開啟對url進行校驗的配置 ,false:關閉,true:開啟
useURIValidationHack="false"
//啟用壓縮的配置
compression="on" compressionMinSize="2048" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
//post請求提交最大大小
maxPostSize="10485760"
acceptorThreadCount="8"
redirectPort="8443" />
- port:代表Tomcat監聽端口,也就是網站的訪問端口,默認為8080,可以根據需要改成其他。
- protocol:協議類型,可選類型有四種,分別為BIO(阻塞型IO),NIO,NIO2和APR。
(1)BIO:BIO(Blocking I/O),顧名思義,即阻塞式I/O操作,表示Tomcat使用的是傳統的Java I/O操作(即java.io包及其子包)。Tomcat在默認情況下,是以bio模式運行的。遺憾的是,就一般而言,bio模式是三種運行模式中性能最低的一種。BIO配置采用默認即可。
(2)NIO:NIO(New I/O),是Java SE 1.4及後續版本提供的一種新的I/O操作方式(即java.nio包及其子包)。Java nio是一個基於緩沖區、並能提供非阻塞I/O操作的java API,因此nio也被看成是non- blocking I/O的縮寫。它擁有比傳統I/O操作(bio)更好的並發運行性能。要讓Tomcat以nio模式來運行也比較簡單,我們只需要protocol類型修改為:
//NIO
protocol="org.apache.coyote.http11.Http11NioProtocol"
//NIO2
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
即可。
(3)APR:APR(Apache Portable Runtime/Apache可移植運行時),是Apache HTTP服務器的支持庫。你可以簡單地理解為:Tomcat將以JNI的形式調用 Apache HTTP服務器的核心動態鏈接庫來處理文件讀取或 網絡傳輸操作,從而大大地提高 Tomcat對靜態文件的處理性能。
與配置 NIO運行模式一樣,也需要將對應的 Connector節點的 protocol屬性值改為:protocol="org.apache.coyote.http11.Http11AprProtocol"
相關APR介紹及配置會在下面專門講。
- maxThreads:由該連接器創建的處理請求線程的最大數目,也就是可以處理的同時請求的最大數目。如果未配置默認值為200。如果一個執行器與此連接器關聯,則忽略此屬性,因為該屬性將被忽略,所以該連接器將使用執行器而不是一個內部線程池來執行任務。但是這個值的是指和jvm內存和為每個線程所分配的內存大小有關
- minSpareThreads:tomcat初始化時創建的線程數,線程的最小運行數目,這些始終保持運行。如果未指定,默認值為10。
- maxSpareThreads:一旦創建的線程超過這個值,Tomcat就會關閉不再需要的socket線程.
- acceptCount:當所有可能的請求處理線程都在使用時傳入連接請求的最大隊列長度。如果未指定,默認值為100。一般是設置的跟 maxThreads一樣或一半,此值設置的過大會導致排隊的請求超時而未被處理。所以這個值應該是主要根據應用的訪問峰值與平均值來權衡配置。
- maxConnections:在任何給定的時間內,服務器將接受和處理的最大連接數。當這個數字已經達到時,服務器將接受但不處理,等待進一步連接。NIO與NIO2的默認值為10000,APR默認值為8192。
- connectionTimeout:當請求已經被接受,但未被處理,也就是等待中的超時時間。單位為毫秒,默認值為60000。通常情況下設置為30000。
- maxHttpHeaderSize:請求和響應的HTTP頭的最大大小,以字節為單位指定。如果沒有指定,這個屬性被設置為8192(8 KB)。
- tcpNoDelay:如果為true,服務器socket會設置TCP_NO_DELAY選項,在大多數情況下可以提高性能。缺省情況下設為true。
- compression:是否啟用gzip壓縮,默認為關閉狀態。這個參數的可接受值為“off”(不使用壓縮),“on”(壓縮文本數據),“force”(在所有的情況下強制壓縮)。
- compressionMinSize:如果compression="on",則啟用此項。被壓縮前數據的最小值,也就是超過這個值後才被壓縮。如果沒有指定,這個屬性默認為“2048”(2K),單位為byte。
- disableUploadTimeout:這個標誌允許servlet Container在一個servlet執行的時候,使用一個不同的,更長的連接超時。最終的結果是給servlet更長的時間以便完成其執行,或者在數據上載的時候更長的超時時間。如果沒有指定,設為false。
- enableLookups:關閉DNS反向查詢。
- URIEncoding:URL編碼字符集。
- maxPostSize:設置post提交內容大小。通過將此屬性設置為小於零的值,可以禁用限制。如果未指定,則此屬性設置為2097152(2兆字節)。
- acceptorThreadCount:默認是1,一般調成和cpu的線程數保持一致,比如8線程的cpu,值就調成:8.
當然Connector標簽中的參數還有很多,這是只是常見的參數設置;並且這些中的參數也不一定都要設置,需要根據項目的具體情況去設置。
- 線程池
在介紹Connector標簽中我們看到已經有線程的相關參數的設置,為什麽還要有線程池?其實這個和Java中的線程池是一個道理,使用線程池可以使多連接公用線程,避免線程的頻繁的創建和銷毀帶來的資源消耗。
引入線程池的方法,使用Connector標簽中的executor的屬性,將其值設置為Executor標簽的name的值。例如:
<Connector port="8066" executor="tomcatThreadPool" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" enableLookups="false" maxPostSize="10485760" URIEncoding="UTF-8" useBodyEncodingForURI="true"
acceptCount="100" acceptorThreadCount="2" disableUploadTimeout="true" maxConnections="10000" SSLEnabled="false" />
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="1000" minSpareThreads="100" maxIdleTime="60000" maxQueueSize="Integer.MAX_VALUE" prestartminSpareThreads="false" threadPriority="5" className="org.apache.catalina.core.StandardThreadExecutor"/>
- io
壓縮
tomcat原生庫
tomcat之性能優化