關於tomcat性能優化


前言

關於 Tomcat 性能調優,一直以來就是運維面試的一個重要話題。今天我們就簡單聊聊 Tomcat 如何進行性能優化? 首先聲明,我不會去說 Tomcat 是什么,內部結構,原理什么的。我不懂......我只是會說一些我在工作當中的一些參數以及自己所了解的方法,主要還是和大家溝通、交流。

一、關於選型

簡單說明,關於Openjdk 和 Oracle jdk的選擇,我個人比較傾向於使用Oracle jdk,雖然它很流氓但是我覺得東西還是靠譜;其次是在版本上的選擇,建議選擇最新穩定版進行在生產環境使用,以此來獲得更高效的性能;

一、JVM相關參數優化

export JAVA_OPTS=""-Dfile.encoding=UTF-8 -server -Xms1400M -Xmx1400M -Xss512k 
-XX:+AggressiveOpts 
-XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=256M 
-XX:+DisableExplicitGC -XX:MaxTenuringThreshold=30 -XX:+UseConcMarkSweepGC 
-XX:+UseParNewGC  -XX:+CMSParallelRemarkEnabled -XX:ReservedCodeCacheSize=32m
-XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m  
-XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly 
-Djava.awt.headless=true"

提示:在java 8中永久代已被移除,如果你是java8的環境,需要你去掉"-XX:PermSize=128M -XX:MaxPermSize=256M"參數,並且"-XX:MaxTenuringThreshold=31"參數值只能設置在0-15,否則會提示相關錯誤; 

配置說明:

  • Tomcat 默認是以 java -client 的方式運行,server 意味着是已真正的生產環境來運行,這樣可以獲得更高的並發、更高效的垃圾回收能力;
  • Xms、Xmx表示JVM 最小內存初始值和最大內存初始值,建議設置為相同參數,以減少CPU對內存資源的調度,避免CPU高速運轉進行的垃圾回收;
  • Xmn設置年輕代大小為512m。整個堆大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為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()”。調用System.gc()付出的代價就是系統響應時間嚴重降低,就和我在關於Xms,Xmx里的解釋的原理一樣;
  • -XX:+UseParNewGC 對年輕代采用多線程並行回收。
  • -XX:+UseConcMarkSweepGC 即CMS gc,這一特性只有jdk1.5即后續版本才具有的功能,它使用的是gc估算觸發和heap占用觸發。我們知道頻頻繁的GC會造面JVM的大起大落從而影響到系統的效率,因此使用了CMS GC后可以在GC次數增多的情況下,每次GC的響應時間卻很短,比如說使用了CMS GC后經過jprofiler的觀察,GC被觸發次數非常多,而每次GC耗時僅為幾毫秒;
  • -XX:+CMSParallelRemarkEnabled在使用UseParNewGC 的情況下, 盡量減少mark的時間;
  • -XX:+UseCMSCompactAtFullCollection在使用concurrent gc 的情況下, 防止 memoryfragmention, 對live object 進行整理, 使 memory 碎片減少;
  • -XX:LargePageSizeInBytes指定 Java heap的分頁頁面大小;
  • -XX:CMSInitiatingOccupancyFraction=70CMSInitiatingOccupancyFraction,這個參數設置有很大技巧,基本上滿足(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關聯在一起;
三、Tomcat 自身優化
3.1 修改Tomcat Connector運行模式

Tomcat Connector(Tomcat連接器)有bio、nio、apr三種運行模式,簡單介紹(抄百度的,有本事來打我):

  • 1. bio(blocking I/O),即阻塞式I/O操作,表示Tomcat使用的是傳統的Java I/O操作(即java.io包 及其子包)。Tomcat在默認情況下(敲黑板,主要是在tomcat7或以下的版本),就是以bio模式運行的。遺憾的是,就一般而言,bio模式是三種運行模式中性能最低的一種。

  特性:一個線程處理一個請求。缺點:並發量高時,線程數較多,浪費資源。

  • 2. 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)更好的並發運行性能。nio目前存在nio以及nio2模式,因此到了Tomcat8.5和Tomcat9.0,則去掉了對BIO的支持。tomcat 8默認以 nio 啟動,其他版本自己百度怎么修改為 nio,在啟動的時候觀察日志:
12-Dec-2017 14:17:46.429 信息 [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
12-Dec-2017 14:17:46.445 信息 [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
12-Dec-2017 14:17:46.473 信息 [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["ajp-nio-8009"]
12-Dec-2017 14:17:46.474 信息 [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
12-Dec-2017 14:17:46.476 信息 [main] org.apache.catalina.startup.Catalina.load Initialization processed in 1031 ms

特性:利用Java的異步IO處理,可以通過少量的線程處理大量的請求。

  • 3. apr(Apache Portable Runtime/Apache可移植運行時),是Apache HTTP服務器的支持庫。在tomcat中,又被稱為 tomcat native,從操作系統層面解決io阻塞問題。是一個利用 APR 來提升Tomcat性能的本地API。怎么配置?自己查!生產環境,強烈推薦。

3.2 tomcat server.xml配置優化

線程池參數配置:

<?xml version="1.0" encoding="UTF-8"?>
  <Service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
    maxThreads="1000" 
    minSpareThreads="100" 
    maxIdleTime="60000"
    prestartminSpareThreads = "true"
    maxQueueSize = "100" 
    className="org.apache.catalina.core.StandardThreadExecutor" />
  • URIEncoding="UTF-8" 使得tomcat可以解析含有中文名的文件的url;
  • minSpareThreads 最小備用線程數,tomcat啟動時的初始化的線程數。
  • enableLookups 消除DNS查詢對性能的影響我們可以關閉DNS查詢。
  • connectionTimeout為網絡連接超時時間毫秒數。
  • maxThreads Tomcat使用線程來處理接收的每個請求。這個值表示Tomcat可創建的最大的線程數,即最大並發數。建議值為500-1000,具體根據服務器性能以及壓測數據結果測試;
  • acceptCount是當線程數達到maxThreads后,后續請求會被放入一個等待隊列,這個acceptCount是這個隊列的大小,如果這個隊列也滿了,就直接refuse connection。
  • maxProcessors與minProcessors在 Java中線程是程序運行時的路徑,是在一個程序中與其它控制線程無關的、能夠獨立運行的代碼段。它們共享相同的地址空間。多線程幫助程序員寫出CPU最 大利用率的高效程序,使空閑時間保持最低,從而接受更多的請求。

提示:網上很多混淆了,異步servlet和非阻塞connector,一個是Executor,一個是connector,兩者的工作階段不同。

連接器配置:

    <Connector executor="tomcatThreadPool" URIEncoding="utf-8"
               port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
               enableLookups="false"
               maxConnections="2000"
               useURIValidationHack="false"
               keepAliveTimeout="60000"
               connectionTimeout="20000"               
maxThreads="1000"
minSpareThreads="100"
maxSpareThreads="2000"
minProcessors="100"
maxProcessors="1000" tcpNoDelay="true" redirectPort="8443" />
  • maxThreads="X" 表示最多同時處理X個連接;
  • minSpareThreads="X" 初始化X個連接;
  • maxSpareThreads="X" 表示如果最多可以有X個線程,一旦超過X個,則會關閉不在需要的線程;
  • acceptCount="X" 當同時連接的人數達到maxThreads時,還可以排隊,隊列大小為X.超過X就不處理;

提示:Tomcat 中可以同時接收的連接數為maxConnections+acceptCount 。上面這些參數需要手動開啟,默認值配置都較低,無法發揮最佳性能。可以去你們生產環境看看,是不是很多沒有做優化呢;

四、關於性能分析

4.1 常見工具

  • 1 jmx 端口獲取監控信息(比如,zabbix);
  • 2 jps/jstat/jmap等命令進行調試;
  • 3 java agent方式獲取以及代碼植入以探針的方式監控(開源PinPoint);

 4.2 其他

查看tomcat連接數:

netstat –nat | grep 8080

查看 tomcat 線程數:  

# 查看進程ID
ps -ef |grep java

# 查看線程
ps -o nlwp 3598

 提示:通過使用該方式可以查看到該進程有多少線程,但並沒有排除處於idle狀態的線程。所以,要想獲取當前進程running的線程數,還需要執行如下命令。

ps -eLo pid,stat | grep 3598 | grep running | wc -l

其中ps -eLo pid,stat可以找出所有線程,並打印其所在的進程號和線程當前的狀態;兩個grep命令分別篩選進程號和線程狀態;wc統計個數。SL表示空閑狀態。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM