做者:北京運維javascript
本文檔是身邊一些朋友、技術大佬以前分享的一些筆記,記錄了 Tomcat 優化方法,筆記較多並且比較雜亂,通過整理、分類我我的以爲大體能夠從如下幾個方面優化 Tomcat:css
Tomcat Connector有三種運行模式:html
bio:阻塞 IO bio 是三種運行模式中性能最低第一種。前端
nio:是一個基於緩衝區,並能提供非阻塞 I/O 操做的 JAVA API 所以 NIO 也成爲非阻塞 I/O,比 bio 擁有更好的併發性能(默認是 nio)。java
apr:調用 httpd 核心連接庫來讀取或文件傳輸,從而提升 tomcat 對靜態文件的處理性能。Tomcat APR 模式也是 Tomcat 在高併發下的首選運行模式。nginx
APR 模式文檔連接:http://tomcat.apache.org/tomcat-8.5-doc/apr.html算法
Apache Portable Runtime 是一個高度可移植的庫,是 Apache HTTP Server 2.x 的核心。APR 有許多用途,包括訪問高級 IO 功能(如sendfile,epoll 和 OpenSSL),操做系統級功能(隨機數生成,系統狀態等)和本機進程處理(共享內存,NT 管道和 Unix 套接字)。apache
$ cd /usr/local/src/ $ wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-1.6.5.tar.gz $ wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-util-1.6.1.tar.gz $ tar xf apr-1.6.5.tar.gz $ cd apr-1.6.5 $ /configure --prefix=/usr/local/apr $ make -j 2 && make install $ cd ../ $ tar xf apr-util-1.6.1.tar.gz $ cd apr-util-1.6.1 $ ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr/
tomcat-native 不用單獨下載,解壓縮 tomcat 程序包後再 bin/ 目錄下存在該程序的源碼包。json
$ tar xf apache-tomcat-8.5.38.tar.gz -C /usr/local/ $ ln -sv /usr/local/apache-tomcat-8.5.38/ /usr/local/tomcat $ cp /usr/local/tomcat/bin/tomcat-native.tar.gz /usr/local/src/ $ tar xf tomcat-native.tar.gz $ cd tomcat-native-1.2.21-src/ $ cd native/ $ ./configure --prefix=/usr/local/apr --with-java-home=/usr/local/jdk1.8.0_121/ $ make -j 2 && make install $ vim /etc/profile export LD_LIBRARY_PATH=/usr/local/apr/lib ##添加apr path $ source /etc/profile
一、修改 protocol 值vim
Tomcat 默認是 "HTTP/1.1" ,若是運行 apr 模式須要把 protocol 值修改爲 apr 模式:org.apache.coyote.http11.Http11AprProtocol
參考官方文檔中 protocol 說明:http://tomcat.apache.org/tomcat-8.5-doc/config/http.html#HTTP/1.1_and_HTTP/1.0_Support
$ cd /usr/local/tomcat/conf/ $ vim server.xml <Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol" connectionTimeout="20000" redirectPort="8443" />
二、修改 SSLEngine
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="off" />
查看 catalina.out 日誌中最下面三行
你是否遇到過當你執行了 ./startup.sh
或者 ./catalina.sh start
後,訪問你的服務會一直轉啊轉啊,可能要幾分鐘才能正常提供服務。
緣由:
在apache-tomcat 官方文檔:如何讓 tomcat 啓動更快裏面提到了一些啓動時的優化項,其中一項是關於隨機數生成時,採用的 "熵源"(entropy source)的策略。提到 tomcat7 的 session id 的生成主要經過 java.security.SecureRandom 生成隨機數來實現,隨機數算法使用的是 」SHA1PRNG」 文章具體內容見下圖
在 http://wiki.apache.org/tomcat/HowTo/FasterStartUp (Entropy Source 部分)有一段解釋。stackoverflow 上面也有一大批這方面的說明,因此這裏就再也不多作介紹。
經過修改 Tomcat 啓動文件 -Djava.security.egd=file:/dev/urandom 經過修改 JRE 中的 java.security 文件 securerandom.source=file:/dev/urand
安裝熵池服務 rngd
$ yum -y install rng-tools $ systemctl start rngd $ systemctl enable rngd
啓動服務後觀察 cat /proc/sys/kernel/random/entropy_avail
基本在三千左右。
隱藏 Tomcat 版本號,不在優化內容範圍內,出於安全角度建議你們隱藏。
修改前以下:
$ cd /usr/local/tomcat/lib/ $ jar xf catalina.jar $ cd org/apache/catalina/util/ $ vim ServerInfo.properties # 修改下面信息 server.info=Apache Tomcat/8.5.38 server.number=8.5.38.0 server.built=Feb 5 2019 11:42:42 UTC 修改後爲 server.info=Apache Tomcat server.number=0.0.0.0 server.built=Feb 5 2019 11:42:42 UTC
將修改後的信息壓縮回 jar 包:
$ cd /usr/local/tomcat/lib $ jar uvf catalina.jar org/apache/catalina/util/ServerInfo.properties 正在添加: org/apache/catalina/util/ServerInfo.properties(輸入 = 885) (輸出 = 512)(壓縮了 42%) # 重啓 Tomcat
修改後再查看以下:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="40000" maxHttpHeaderSize="32768" redirectPort="8443" maxThreads="1000" minSpareThreads="100" maxSpareThreads="1000" acceptorThreadCount="2" acceptCount="2000" minProcessors="100" maxProcessors="2000" enableLookups="false" maxKeepAliveRequests="-1" keepAliveTimeout="-1" disableUploadTimeout="false" connectionUploadTimeout="150000" useSendfile="false" URIEncoding="UTF-8" />
配置參數說明
protocol="HTTP/1.1" maxHttpHeaderSize="8192" maxThreads="1000" //最大線程數,默認200 minSpareThreads="100" //Tomcat初始化時建立的socket線程數,線程的最小運行數目,這些始終保持運行,若是未指定,默認值爲10 enableLookups="false"//關閉DNS反向查詢,若設爲true,則支持域名解析,可把ip地址解析爲主機名 compression="on"//打開壓縮功能 compressionMinSize="2048" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" connectionTimeout="20000"//表明鏈接超時時間,單位爲毫秒,默認值爲60000。一般狀況下設置爲30000 URIEncoding="utf-8"//URL統一編碼 acceptCount="1000"//監聽端口隊列最大數,滿了以後客戶請求會被拒絕(不能小於maxSpareThreads),若是未指定,默認值爲100 redirectPort="8443"//在須要基於安全通道的場合,把客戶請求轉發到基於SSL的redirectPort端口 disableUploadTimeout="true"/>//這個標誌容許servlet[Container](http://lib.csdn.net/base/4)在一個servlet執行的時候,使用一個不一樣的,更長的鏈接超時。最終的結果是給servlet更長的時間以便完成其執行,或者在數據上載的時候更長的超時時間。若是沒有指定,設爲false
禁用 AJP(若是你服務器沒有使用 Apache)
<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
Tomcat 壓縮配置,建議在前端 nginx 上開啓壓縮。Tomcat 做爲應用服務器自己就很繁忙了。
compression="on" compressionMinSize="2048" compressableMimeType="text/html,text/xml,application/javascript,application/json,text/javascript,text/css,text/plain,image/gif,image/jpg,image/png"
Java 程序調優,主要就是調『堆內存』和『垃圾回收機制』。
參數 | 做用 |
---|---|
-Xms | 堆初始內存大小 |
-Xmx | 堆最大內存大小 |
-Xmn | 初始新生代內存大小,通常設置爲整個堆的 1/3到 1/4 左右 |
-XX:PrintGC | 每次觸發GC的時候打印相關日誌 |
-XX:+PrintGCDetails | 更詳細的GC日誌 |
-XX:SurvivorRatio=2 | 用來設置新生代中 Eden 空間和 from/to 空間比例,默認是 8 (Edem : from : to = 8 : 1 : 1) |
-Dfile.encoding=UTF-8 | 設置字符集避免日誌中出現亂碼 |
-Dsun.jnu.encoding=UTF-8 | 設置字符集避免日誌中出現亂碼 |
-Duser.timezone=GMT+08 | 時區設置 |
-XX:+HeapDumpOnOutOfMemoryError | 堆異常報錯輸出 |
-XX:HeapDumpPath=path | 設置在dump heap時將文件dump到哪裏。默認是當前目錄下 java_pidpid.hprof這樣形式的文件。 |
-XX:+UseParallelGC | 選擇垃圾收集器爲並行收集器。此配置僅對年輕代有效。即上述配置下,年輕代使用併發收集,而年老代仍舊使用串行收集 |
-XX:+UseParallelOldGC | 配置年老代垃圾收集方式爲並行收集。 |
-XX:ParallelGCThreads=8 | 設置支持併發GC的線程數。 |
在實際工做中,咱們能夠直接將初始堆大小(-Xms)與最大堆大小(-Xmx) 配置相等,這樣的好處就是能夠減小程序運行時垃圾回收的次數,從而提升效率。
新生代與老年代的設置比例:1:3 或者 1:4
如下內核參數是工做環境中你們常常會看到的一些:
net.core.netdev_max_backlog = 32768 net.core.somaxconn = 32768 net.core.rmem_default = 8388608 net.core.wmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.ip_local_port_range = 1024 65535 net.ipv4.route.gc_timeout = 100 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_keepalive_time = 1200 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_syn_retries = 1 net.ipv4.tcp_synack_retries = 1 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.tcp_max_syn_backlog = 65535
以上內核參數做用說明:
# cat /proc/sys/net/core/netdev_max_backlog # 默認值:1000 # 做用:網卡設備將請求放入隊列的長度 net.core.netdev_max_backlog = 32768 # cat /proc/sys/net/core/somaxconn # 默認值:128 # 做用:已經成功創建鏈接的套接字將要進入隊列的長度 net.core.somaxconn = 32768 # cat /proc/sys/net/core/rmem_default # 默認值:212992 # 做用:默認的TCP數據接收窗口大小(字節) net.core.rmem_default = 8388608 # cat /proc/sys/net/core/wmem_default # 默認值:212992 # 做用:默認的TCP數據發送窗口大小(字節) net.core.wmem_default = 8388608 # cat /proc/sys/net/core/rmem_max # 默認值:212992 # 做用:最大的TCP數據接收窗口大小(字節) net.core.rmem_max = 16777216 # cat /proc/sys/net/core/wmem_max # 默認值:212992 # 做用:最大的TCP數據發送窗口大小(字節) net.core.wmem_max = 16777216 # cat /proc/sys/net/ipv4/ip_local_port_range # 默認值:32768 61000 # 做用:可用端口的範圍 net.ipv4.ip_local_port_range = 1024 65535 # cat /proc/sys/net/ipv4/route/gc_timeout # 默認值 300 # 做用:路由緩存刷新頻率,當一個路由失敗後多長時間跳到另外一個路由 net.ipv4.route.gc_timeout = 100 # cat /proc/sys/net/ipv4/tcp_fin_timeout # 默認 60 # 做用:表示若是套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀態的時間 net.ipv4.tcp_fin_timeout = 30 # cat /proc/sys/net/ipv4/tcp_keepalive_time # 默認值:7200 # 做用:間隔多久發送1次keepalive探測包 net.ipv4.tcp_keepalive_time = 1200 # cat /proc/sys/net/ipv4/tcp_timestamps # 默認值:1 # 做用:TCP時間戳,時間戳必需要開啓,不然下面的 TIME_WAIT 重用和快速回收無效 net.ipv4.tcp_timestamps = 1 # cat /proc/sys/net/ipv4/tcp_tw_recycle # 默認值:0 # 做用:表示開啓TCP鏈接中TIME-WAIT sockets的快速回收,默認爲0,表示關閉。 net.ipv4.tcp_tw_recycle = 1 ######################## cat /proc/sys/net/ipv4/tcp_tw_reuse # 默認值:0 # 做用:針對 TIME-WAIT,作爲客戶端能夠啓用(例如,做爲nginx-proxy前端代理,要訪問後端的服務) net.ipv4.tcp_tw_reuse = 1 # cat /proc/sys/net/ipv4/tcp_syn_retries # 默認值 2 # 做用:在內核放棄創建鏈接以前發送SYN包的數量。 net.ipv4.tcp_syn_retries = 1 # cat /proc/sys/net/ipv4/tcp_synack_retries # 默認值 2 # 做用:爲了打開對端的鏈接,內核須要發送一個SYN並附帶一個迴應前面一個SYN的ACK。也就是所謂三次握手中的第二次握手。這個設置決定了內核放棄鏈接以前發送SYN+ACK包的數量。 net.ipv4.tcp_synack_retries = 1 # cat /proc/sys/net/ipv4/tcp_mem #肯定 TCP 棧應該如何反映內存使用;每一個值的單位都是內存頁(一般是 4KB)。 #第一個值是內存使用的下限。 #第二個值是內存壓力模式開始對緩衝區使用應用壓力的上限。 #第三個值是內存上限。在這個層次上能夠將報文丟棄,從而減小對內存的使用。對於較大的 BDP 能夠增大這些值(可是要記住,其單位是內存頁,而不是字節)。 net.ipv4.tcp_mem = 94500000 915000000 927000000 # cat /proc/sys/net/ipv4/tcp_max_orphans # 默認值 16384 # 做用:系統中最多有多少個TCP套接字不被關聯到任何一個用戶文件句柄上。若是超過這個數字,孤兒鏈接將即刻被複位並打印出警告信息。這個限制僅僅是爲了防止簡單 的DoS攻擊,你絕對不能過度依靠它或者人爲地減少這個值,更應該增長這個值(若是增長了內存以後)。 net.ipv4.tcp_max_orphans = 3276800 # cat /proc/sys/net/ipv4/tcp_max_syn_backlog # 默認值:128 # 做用:增大SYN隊列的長度,容納更多鏈接 net.ipv4.tcp_max_syn_backlog = 65535
開啓 JMX 遠程鏈接之後,就能夠經過 jconsole
和 jvisualvm
兩個 JDK 內置工具鏈接 JMX 從而查看當前主機的 JVM 相關信息。
$ cd /usr/local/tomcat/bin/ # 默認狀況下,$CATALINA_HOME/bin 目錄下是沒有 setenv.sh,能夠本身新建此文件 $ vim setenv.sh CATALINA_OPTS="-Djava.rmi.server.hostname=10.100.4.169 -Dcom.sun.management.jmxremote.port=8686 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" :wq
JMX 配置說明:
# 經過哪一個 IP 訪問本機的 JMX -Djava.rmi.server.hostname=10.100.4.169 # JMX 監聽的端口,默認 8686 -Dcom.sun.management.jmxremote.port=8686 # 是否須要用戶名密碼認證,默認 false -Dcom.sun.management.jmxremote.authenticate=false # 是否啓用 SSL -Dcom.sun.management.jmxremote.ssl=false"
Jconsole 是 JDK 自帶的監控工具,在 JDK/bin 目錄下能夠找到。它用於鏈接正在運行的本地或者遠程的 JVM,對運行在 java 應用程序的資源消耗和性能進行監控,並畫出大量的圖表,提供強大的可視化界面。
當你在 Windows 或者 Mac 下運行 jconsole 後就會在桌面彈出 jconsole 的控制檯,選擇遠程鏈接,輸入IP:端口 點擊鏈接
我這裏在配置 JMX 的時候沒有啓用 SSL 因此會提示不安全,點擊『不安全的鏈接』
jvisualvm 一樣是 JDK 內置的工具,使用方法與 jconsole 相似。功能上要比 jconsole 強大一些。推薦使用。
運行 jvisualvm 後一樣在桌面彈出一個控制檯窗口
遠程 --> 添加主機 --> 添加 JMX 鏈接
jmap 能夠輸出 Java 進程 內存中對象的工具。jmap 通常和 jhat 或者 MAT 配合使用,以圖像的形式直觀的展現當前內存是否有問題。
-dump:[live,]format=b,file=<filename> 以hprof二進制格式轉儲Java堆到指定filename的文件中。 live子選項是可選的,若是指定了live子選項,堆中只有活動的對象會被轉儲。 想要瀏覽heap dump,你可使用 jhat(Java堆分析工具) 或者 MAT 讀取生成的文件。 -finalizerinfo 打印等待終結的對象信息。 -heap 打印一個堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。 -histo[:live] 打印每一個Java類、對象數量、內存大小(單位:字節)、徹底限定的類名。 打印的虛擬機內部的類名稱將會帶有一個'*'前綴。 若是指定了live子選項,則只計算活動的對象。 -permstat 打印Java堆內存的永久保存區域的類加載器的智能統計信息。 對於每一個類加載器而言,它的名稱、活躍度、地址、父類加載器、它所加載的類的數量和大小都會被打印。 此外,包含的字符串數量和大小也會被打印。 -F 強制模式。若是指定的pid沒有響應,請使用jmap -dump或jmap -histo選項。此模式下,不支持live子選項。 -h | -help 打印幫助信息。 -J<flag> 指定傳遞給運行jmap的JVM的參數。
示例 一、jmap -histo <pid>
打印每一個Java類、對象數量、內存大小(單位:字節)、徹底限定的類名。
$ jmap -histo 14110 num #instances #bytes class name ---------------------------------------------- 1: 50994 7789848 [C 2: 22160 6263680 [B 3: 8527 1437176 [I 4: 49099 1178376 java.lang.String 5: 63 936248 [J 6: 19118 611776 java.util.HashMap$Node 7: 6243 549384 java.lang.reflect.Method 8: 8948 509464 [Ljava.lang.Object; 9: 10588 423520 java.util.TreeMap$Entry 10: 3696 417952 java.lang.Class 11: 1372 230544 [Ljava.util.HashMap$Node; 12: 4760 228480 java.util.HashMap 13: 4789 191560 java.util.HashMap$ValueIterator 14: 5851 187232 java.io.File
B byte
C char
D double
F float
I int
J long
Z boolean
[ 數組,如[I表示int[]
[L+類名 其餘對象
示例 二、jmap -heap <pid>
查看進程堆內存使用狀況,包括使用的GC算法、堆配置參數和各代中堆內存使用狀況。
$ jmap -heap 14110 Attaching to process ID 14110, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.121-b13 using thread-local object allocation. Parallel GC with 2 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 134217728 (128.0MB) NewSize = 44564480 (42.5MB) MaxNewSize = 44564480 (42.5MB) OldSize = 89653248 (85.5MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 19398656 (18.5MB) used = 6316256 (6.023651123046875MB) free = 13082400 (12.476348876953125MB) 32.56027634079392% used From Space: capacity = 12582912 (12.0MB) used = 98304 (0.09375MB) free = 12484608 (11.90625MB) 0.78125% used To Space: capacity = 12582912 (12.0MB) used = 0 (0.0MB) free = 12582912 (12.0MB) 0.0% used PS Old Generation capacity = 89653248 (85.5MB) used = 20189088 (19.253814697265625MB) free = 69464160 (66.24618530273438MB) 22.519081517269736% used 13984 interned Strings occupying 1890552 bytes.
示例 三、jmap -dump:format=b,file=<dumpFileName> <pid>
用jmap把進程內存使用狀況dump到文件中
$ jmap -dump:format=b,file=/tmp/123.hprof 14110 Dumping heap to /tmp/123.hprof ... Heap dump file created
jhat 能夠對 dump 出來的堆信息進行處理,以 html 頁面的形式展現出來。
執行 jhat /tmp/123.hprof
便可,默認端口是 7000,訪問 http://localhost:7000 便可查看結果。經過 -port 指定端口。
以上就是我整理出的 Tomcat 基礎優化方法,若是有不對的地方或者你有更好的建議,歡迎在評論區留言,謝謝。