一.Tomcat內存優化
1.JAVA_OPTS參數說明
Tomcat內存優化主要是對 tomcat 啟動參數優化,我們可以在 tomcat 的啟動腳本 catalina.sh 中設置 JAVA_OPTS參數。
服務器參數配置
配置完成后可重啟Tomcat ,通過以下命令進行查看配置是否生效:
1. 首先查看Tomcat 進程號:
我們可以看到Tomcat 進程號是 12222 。
1. 查看是否配置生效:
我們可以看到MaxHeapSize 等參數已經生效。
優化 server.xml
Tomcat的主配置文件,該文件中包含很多主要元素,比如Service、Connector、Host等,這些元素都會創建軟件"對象"、排序及進程管道中設置的這些元素嵌套方,使我們可以執行過濾、分組等工作。
如果要對該文件做優化,我們需要先了解該文件的結構!
server.xml的結構圖:
該文件描述了如何啟動Tomcat Server
<Server> <Listener /> <GlobaNamingResources> </GlobaNamingResources <Service> <Connector /> <Engine> <Logger /> <Realm /> <host> <Logger /> <Context /> </host> </Engine> </Service> </Server>
針對該文件,我們需要優化的點有如下:
1、 maxThreads 連接數限制
maxThreads 是 Tomcat 所能接受最大連接數。一般設置不要超過8000以上,如果你的網站訪問量非常大可能使用運行多個Tomcat實例的方法, 即,在一個服務器上啟動多個tomcat然后做負載均衡處理。
這里還需要注意的一點是,tomcat 和 php 不同。php可以按照cpu和內存的情況去配置連接數,上萬很正常。而 java 還需要注意 jvm 的參數配置。如果不注意就會因為jvm參數過小而崩潰。
2、多虛擬主機
強烈建議不要使用 Tomcat 的虛擬主機,推薦每個站點使用一個實例。即,可以啟動多個 Tomcat,而不是啟動一個 Tomcat 里面包含多個虛擬主機。因為 Tomcat是多線程,共享內存,任何一個虛擬主機中的應用崩潰,都會影響到所有應用程序。雖然采用多實例的方式會產生過多的開銷,但至少保障了應用程序的隔離和安全。
3、壓錯傳輸
tomcat作為一個應用服務器,也是支持 gzip 壓縮功能的。我們可以在 server.xml 配置文件中的 Connector 節點中配置如下參數,來實現對指定資源類型進行壓縮。
compression="on" # 打開壓縮功能 compressionMinSize="50" # 啟用壓縮的輸出內容大小,默認為2KB noCompressionUserAgents="gozilla, traviata" # 對於以下的瀏覽器,不啟用壓縮 compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" # 哪些資源類型需要壓縮
提示:
Tomcat 的壓縮是在客戶端請求服務器對應資源后,從服務器端將資源文件壓縮,再輸出到客戶端,由客戶端的瀏覽器負責解壓縮並瀏覽。相對於普通的瀏覽過程 HTML、CSS、Javascript和Text,它可以節省40% 左右的流量。更為重要的是,它可以對動態生成的,包括CGI、PHP、JSP、ASP、Servlet,SHTML等輸出的網頁也能進行壓縮,壓縮效率也很高。但是, 壓縮會增加 Tomcat 的負擔,因此最好采用Nginx + Tomcat 或者 Apache + Tomcat 方式,將壓縮的任務交由 Nginx/Apache 去做。
一旦啟用了這個壓縮功能后,我們怎么來測試壓縮是否有效呢?首先Tomcat是根據瀏覽器請求頭中的accept-encoding來判斷瀏覽器是否支持 壓縮功能,如果這個值包含有gzip,就表明瀏覽器支持gzip壓縮內容的瀏覽,所以我們可以用httpclient來寫一個這樣的簡單測試程序
import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.GetMethod; public class HttpTester { public static void main(String[] args) throws Exception{ HttpClient http = new HttpClient(); GetMethod get = new GetMethod("http://www.dlog.cn/js/prototype.js"); try{ get.addRequestHeader("accept-encoding", "gzip,deflate"); get.addRequestHeader("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Alexa Toolbar; Maxthon 2.0)"); int er = http.executeMethod(get); if(er==200){ System.out.println(get.getResponseContentLength()); String html = get.getResponseBodyAsString(); System.out.println(html); System.out.println(html.getBytes().length); } }finally{ get.releaseConnection(); } } }
執行這個測試程序,看看它所輸出的是什么內容,如果輸出的是一些亂碼,以及打印內容的長度遠小於實際的長度,那么恭喜你,你的配置生效了,你會發現你網站的瀏覽速度比以前快多了。
4、管理AJP端口
AJP是為 Tomcat 與 HTTP 服務器之間通信而定制的協議,能提供較高的通信速度和效率。如果tomcat前端放的是apache的時候,會使用到AJP這個連接器。由於我們公司前端是由nginx做的反向代理,因此不使用此連接器,因此需要注銷掉該連接器。
<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
5、更改關閉 Tomcat 實例的指令
server.xml中定義了可以直接關閉 Tomcat 實例的管理端口。我們通過 telnet 連接上該端口之后,輸入 SHUTDOWN (此為默認關閉指令)即可關閉 Tomcat 實例(注意,此時雖然實例關閉了,但是進程還是存在的)。由於默認關閉 Tomcat 的端口和指令都很簡單。默認端口為8005,指令為SHUTDOWN 。因此我們需要將關閉指令修改復雜一點。
當然,在新版的 Tomcat 中該端口僅監聽在127.0.0.1上,因此大家也不必擔心。除非黑客登陸到tomcat本機去執行關閉操作。
修改實例:
<Server port="8005" shutdow n="9SDKJ29jksjf23sjf0LSDF92JKS9DKkjsd">
6、更改 Tomcat 的服務監聽端口
一般公司的 Tomcat 都是放在內網的,因此我們針對 Tomcat 服務的監聽地址都是內網地址。
修改實例:
<Connector port="8080" address="172.16.100.1" />
7、關閉war自動部署
默認 Tomcat 是開啟了對war包的熱部署的。為了防止被植入木馬等惡意程序,因此我們要關閉自動部署。
修改實例:
<Host name="localhost" appBase="" unpackWARs="false" autoDeploy="false">
二.Tomcat並發優化
1.調整連接器connector的並發處理能力
在Tomcat 配置文件 server.xml 中的 <Connector ... /> 配置中
1.參數說明
maxThreads 客戶請求最大線程數
minSpareThreads Tomcat初始化時創建的 socket 線程數
maxSpareThreads Tomcat連接器的最大空閑 socket 線程數
minProcessors:最小空閑連接線程數,用於提高系統處理性能,默認值為 10
maxProcessors:最大連接線程數,即:並發處理的最大請求數,默認值為 75
acceptCount:允許的最大連接數,應大於等於 maxProcessors ,默認值為 100
enableLookups:是否反查域名,取值為: true 或 false 。為了提高處理能力,應設置為 false
redirectPort 在需要基於安全通道的場合,把客戶請求轉發到基於SSL 的 redirectPort 端口
acceptAccount 監聽端口隊列最大數,滿了之后客戶請求會被拒絕(不能小於maxSpareThreads )
connectionTimeout:網絡連接超時,單位:毫秒。設置為 0 表示永不超時,這樣設置有隱患的。通常可設置為30000 毫秒。
URIEncoding URL統一編碼
其中和最大連接數相關的參數為maxProcessors 和 acceptCount 。如果要加大並發連接數,應同時加大這兩個參數。
web server允許的最大連接數還受制於操作系統的內核參數設置,通常 Windows 是 2000 個左右, Linux 是1000 個左右。
2.Tomcat中的配置示例
<Connector port="9027" protocol="HTTP/1.1" maxHttpHeaderSize="8192" maxThreads="1000" minSpareThreads="100" maxSpareThreads="1000" minProcessors="100" maxProcessors="1000" enableLookups="false" URIEncoding="utf-8" acceptCount="1000" redirectPort="8443" disableUploadTimeout="true"/>
3.Tomcat緩存優化
tomcat的maxThreads、acceptCount(最大線程數、最大排隊數)
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxThreads="800" acceptCount="1000"/>
maxThreads:tomcat起動的最大線程數,即同時處理的任務個數,默認值為200
acceptCount:當tomcat起動的線程數達到最大時,接受排隊的請求個數,默認值為100
這兩個值如何起作用,請看下面三種情況
情況1:接受一個請求,此時tomcat起動的線程數沒有到達maxThreads,tomcat會起動一個線程來處理此請求。
情況2:接受一個請求,此時tomcat起動的線程數已經到達maxThreads,tomcat會把此請求放入等待隊列,等待空閑線程。
情況3:接受一個請求,此時tomcat起動的線程數已經到達maxThreads,等待隊列中的請求個數也達到了acceptCount,此時tomcat會直接拒絕此次請求,返回connection refused
maxThreads如何配置
一般的服務器操作都包括量方面:1計算(主要消耗cpu),2等待(io、數據庫等)
第一種極端情況,如果我們的操作是純粹的計算,那么系統響應時間的主要限制就是cpu的運算能力,此時maxThreads應該盡量設的小,降低同一時間內爭搶cpu的線程個數,可以提高計算效率,提高系統的整體處理能力。
第二種極端情況,如果我們的操作純粹是IO或者數據庫,那么響應時間的主要限制就變為等待外部資源,此時maxThreads應該盡量設的大,這樣才能提高同時處理請求的個數,從而提高系統整體的處理能力。此情況下因為tomcat同時處理的請求量會比較大,所以需要關注一下tomcat的虛擬機內存設置和linux的open file限制。
我在測試時遇到一個問題,maxThreads我設置的比較大比如3000,當服務的線程數大到一定程度時,一般是2000出頭,單次請求的響應時間就會急劇的增加,
百思不得其解這是為什么,四處尋求答案無果,最后我總結的原因可能是cpu在線程切換時消耗的時間隨着線程數量的增加越來越大,
cpu把大多數時間都用來在這2000多個線程直接切換上了,當然cpu就沒有時間來處理我們的程序了。
以前一直簡單的認為多線程=高效率。。其實多線程本身並不能提高cpu效率,線程過多反而會降低cpu效率。
當cpu核心數<線程數時,cpu就需要在多個線程直接來回切換,以保證每個線程都會獲得cpu時間,即通常我們說的並發執行。
所以maxThreads的配置絕對不是越大越好。
現實應用中,我們的操作都會包含以上兩種類型(計算、等待),所以maxThreads的配置並沒有一個最優值,一定要根據具體情況來配置。
最好的做法是:在不斷測試的基礎上,不斷調整、優化,才能得到最合理的配置。
acceptCount的配置,我一般是設置的跟maxThreads一樣大,這個值應該是主要根據應用的訪問峰值與平均值來權衡配置的。
如果設的較小,可以保證接受的請求較快相應,但是超出的請求可能就直接被拒絕
如果設的較大,可能就會出現大量的請求超時的情況,因為我們系統的處理能力是一定的。
提示
很多做過php運維的朋友在這里會犯一個大錯誤,php優化服務器通常怎做法是安裝cpu以及內存的情況配置連接數,連接數過萬都很正常,但java不同jvm配置要非常小心,稍有差錯就會崩潰。
maxThreads 配置要結合 JVM -Xmx 參數調整,也就是要考慮內存開銷。
在線上環境中我們是采用了tomcat作為Web服務器,它的處理性能直接關系到用戶體驗,在平時的工作和學習中,歸納出以下七種調優經驗。
1. 服務器資源
服務器所能提供CPU、內存、硬盤的性能對處理能力有決定性影響。
(1) 對於高並發情況下會有大量的運算,那么CPU的速度會直接影響到處理速度。
(2) 內存在大量數據處理的情況下,將會有較大的內存容量需求,可以用-Xmx -Xms -XX:MaxPermSize等參數對內存不同功能塊進行划分。我們之前就遇到過內存分配不足,導致虛擬機一直處於full GC,從而導致處理能力嚴重下降。
(3) 硬盤主要問題就是讀寫性能,當大量文件進行讀寫時,磁盤極容易成為性能瓶頸。最好的辦法還是利用下面提到的緩存。
2. 利用緩存和壓縮
對於靜態頁面最好是能夠緩存起來,這樣就不必每次從磁盤上讀。這里我們采用了Nginx作為緩存服務器,將圖片、css、js文件都進行了緩存,有效的減少了后端tomcat的訪問。
另外,為了能加快網絡傳輸速度,開啟gzip壓縮也是必不可少的。但考慮到tomcat已經需要處理很多東西了,所以把這個壓縮的工作就交給前端的Nginx來完成。可以參考之前寫的《利用nginx加速web訪問》。
除了文本可以用gzip壓縮,其實很多圖片也可以用圖像處理工具預先進行壓縮,找到一個平衡點可以讓畫質損失很小而文件可以減小很多。曾經我就見過一個圖片從300多kb壓縮到幾十kb,自己幾乎看不出來區別。
3. 采用集群
單個服務器性能總是有限的,最好的辦法自然是實現橫向擴展,那么組建tomcat集群是有效提升性能的手段。我們還是采用了Nginx來作為請求分流的服務器,后端多個tomcat共享session來協同工作。可以參考之前寫的《利用nginx+tomcat+memcached組建web服務器負載均衡》。
4. 優化tomcat參數
這里以tomcat7的參數配置為例,需要修改conf/server.xml文件,主要是優化連接配置,關閉客戶端dns查詢。
<Connector port="8080"
protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
redirectPort="8443"
maxThreads="500"
minSpareThreads="20"
acceptCount="100"
disableUploadTimeout="true"
enableLookups="false"
URIEncoding="UTF-8" />
5. 改用APR庫
tomcat默認采用的BIO模型,在幾百並發下性能會有很嚴重的下降。tomcat自帶還有NIO的模型,另外也可以調用APR的庫來實現操作系統級別控制。
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
修改為:
<Connector port="80" protocol="org.apache.coyote.http11.Http11NioProtocol " connectionTimeout="20000" redirectPort="8443" />
NIO模型是內置的,調用很方便,只需要將上面配置文件中protocol修改成org.apache.coyote.http11.Http11NioProtocol,重啟即可生效。上面配置我已經改過了,默認的是HTTP/1.1。
APR則需要安裝第三方庫,在高並發下會讓性能有明顯提升。具體安裝辦法可以參考http://www.cnblogs.com/huangjingzhou/articles/2097241.html。安裝完成后重啟即可生效。如使用默認protocal就是apr,但最好把將protocol修改成org.apache.coyote.http11.Http11AprProtocol,會更加明確。
在官方找到一個表格詳細說明了這三種方式的區別:
|
Java Blocking Connector |
Java Nio Blocking Connector |
APR/native Connector |
|
BIO |
NIO |
APR |
Classname |
AjpProtocol |
AjpNioProtocol |
AjpAprProtocol |
Tomcat Version |
3.x onwards |
7.x onwards |
5.5.x onwards |
Support Polling |
NO |
YES |
YES |
Polling Size |
N/A |
maxConnections |
maxConnections |
Read Request Headers |
Blocking |
Sim Blocking |
Blocking |
Read Request Body |
Blocking |
Sim Blocking |
Blocking |
Write Response |
Blocking |
Sim Blocking |
Blocking |
Wait for next Request |
Blocking |
Non Blocking |
Non Blocking |
Max Connections |
maxConnections |
maxConnections |
maxConnections |
6. 優化網絡
Joel也明確提出了優化網卡驅動可以有效提升性能,這個對於集群環境工作的時候尤為重要。由於我們采用了linux服務器,所以優化內核參數也是一個非常重要的工作。給一個參考的優化參數:
1. 修改/etc/sysctl.cnf文件,在最后追加如下內容:
net.core.netdev_max_backlog = 32768 net.core.somaxconn = 32768 net.core.wmem_default = 8388608 net.core.rmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.ip_local_port_range = 1024 65000 net.ipv4.route.gc_timeout = 100 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_keepalive_time = 1200 net.ipv4.tcp_timestamps = 0 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.tcp_max_syn_backlog = 65536
2. 保存退出,執行sysctl -p生效
7. 讓測試說話
優化系統最忌諱的就是只調優不測試,有時不適當的優化反而會讓性能更低。以上所有的優化方法都要在本地進行性能測試過后再不斷調整參數,這樣最終才能達到最佳的優化效果。
補充Bio、Nio、Apr模式的測試結果:
對於這幾種模式,我用ab命令模擬1000並發測試10000詞,測試結果比較意外,為了確認結果,我每種方式反復測試了10多次,並且在兩個服務器上都測試了一遍。結果發現Bio和Nio性能差別非常微弱,難怪默認居然還是Bio。但是采用apr,連接建立的速度會有50%~100%的提升。直接調用操作系統層果然神速啊,這里強烈推薦apr方式!
Tomcat的四種基於HTTP協議的Connector性能比較
Tomcat從5.5版本開始,支持以下四種Connector的配置分別為:
<Connector port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" redirectPort="8443"/>
<Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000"
redirectPort="8443"/>
<Connector executor="tomcatThreadPool"
port="8081" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector executor="tomcatThreadPool"
port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
redirectPort="8443" />
我們姑且把上面四種Connector按照順序命名為 NIO, HTTP, POOL, NIOP
為了不讓其他因素影響測試結果,我們只對一個很簡單的jsp頁面進行測試,這個頁面僅僅是輸出一個Hello World。假設地址是 http://tomcat1/test.jsp
我們依次對四種Connector進行測試,測試的客戶端在另外一台機器上用ab命令來完成,測試命令為: ab -c 900 -n 2000 http://tomcat1/test.jsp ,最終的測試結果如下表所示(單位:平均每秒處理的請求數):
NIO HTTP POOL NIOP
281 | 65 | 208 | 365 |
666 | 66 | 110 | 398 |
692 | 65 | 66 | 263 |
256 | 63 | 94 | 459 |
440 | 67 | 145 | 363 |
由這五組數據不難看出,HTTP的性能是很穩定,但是也是最差的,而這種方式就是Tomcat的默認配置。NIO方式波動很大,但沒有低於280 的,NIOP是在NIO的基礎上加入線程池,可能是程序處理更復雜了,因此性能不見得比NIO強;而POOL方式則波動很大,測試期間和HTTP方式一樣,不時有停滯。
由於linux的內核默認限制了最大打開文件數目是1024,因此此次並發數控制在900。
盡管這一個結果在實際的網站中因為各方面因素導致,可能差別沒這么大,例如受限於數據庫的性能等等的問題。但對我們在部署網站應用時還是具有參考價值的。
1. JVM
1.1. 使用 Server JRE 替代JDK。
服務器上不要安裝JDK,請使用 Server JRE. 服務器上根本不需要編譯器,代碼應該在Release服務器上完成編譯打包工作。
理由:一旦服務器被控制,可以防止在其服務器上編譯其他惡意代碼並植入到你的程序中。
3. Tomcat 安全配置
3.1. 安裝后初始化配置
當Tomcat完成安裝后你首先要做的事情如下:
首次安裝完成后立即刪除webapps下面的所有代碼
rm -rf /srv/apache-tomcat/webapps/*
注釋或刪除 tomcat-users.xml 所有用戶權限,看上去如下:
# cat conf/tomcat-users.xml <?xml version='1.0' encoding='utf-8'?> <tomcat-users> </tomcat-users>
隱藏Tomcat版本信息
vim $CATALINA_HOME/conf/server.xml <Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxThreads="8192" minSpareThreads="64" maxSpareThreads="128" acceptCount="128" enableLookups="false" server="Neo App Srv 1.0"/> # curl -I http://localhost:8080/ HTTP/1.1 400 Bad Request Transfer-Encoding: chunked Date: Thu, 20 Oct 2011 09:51:55 GMT Connection: close Server: Neo App Srv 1.0
服務器信息已經被改為 Server: Neo App Srv 1.0
3.2. 啟動用戶與端口
不要使用root用戶啟動tomcat,Java程序與C程序不同。nginx,httpd 使用root用戶啟動守護80端口,子進程/線程會通過setuid(),setgid()兩個函數切換到普通用戶。即父進程所有者是root用戶,子進程與多線程所有者是一個非root用戶,這個用戶沒有shell,無法通過ssh與控制台登陸系統,Java 的JVM 是與系統無關的,是建立在OS之上的,你使用什么用戶啟動Tomcat,那麽Tomcat 就會繼承該所有者的權限。
這造成了一個問題,Linux系統小於1024的端口只有root可以使用,這也是為什么Tomcat默認端口是8080。如果你想使用80端口只能使用root啟動Tomcat。這有帶來了很多安全問題。
解決方案是創建一個不同用戶,如:
groupadd -g 80 daemon adduser -o --home /daemon --shell /sbin/nologin --uid 80 --gid 80 -c "Web Server" daemon
注意 /sbin/nologin , 意味着該用戶不能登錄,同時我也沒有給它指定密碼,這個用戶只能用於啟動tomcat
chown daemon:daemon -R /srv/* su - daemon -c "/srv/apache-tomcat/bin/startup.sh"
接下來解決80端口問題, 思路就是80去調用8080,或者映射端口。
下面是影射方案,80 跳轉 8080
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 取消跳轉 iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 查看規則 iptables -t nat -L
另一個就是從80請求去調用8080的方案
這個方案可以在 Tomcat 前段增加反向代理,例如:Nginx,Apache,Squid,Varnish或者F5, Array這類設備等等
3.3. 應用程序安全
關閉war自動部署 unpackWARs="false" autoDeploy="false"。防止被植入木馬等惡意程序
應用程序部署與tomcat啟動,不能使用同一個用戶。
我的tomcat 安裝在 /srv目錄下,Tomcat啟動用戶為daemon; 應用程序放在/www目錄下www所有者是www用戶。這樣的目的是一旦tomcat被植入web shell程序,它將不能創建或編輯/www目錄下面的任何內容。
adduser --home /www -c "Web Application" www
3.4. JSESSIONID
修改 Cookie 變量 JSESSIONID, 這個cookie 是用於維持Session關系。建議你改為PHPSESSID。
tomcat-user.xm 安全
查看進程帳戶應為專用非root帳號。
查看tomcat配置文件server.xml是否有設置connectionTimeout值,
符合:設置賬戶自動登出。
查看配置文件是否設置ip登陸范圍限制 |
符合:配置文件中含有登陸ip限制。 不符合:沒設置登陸ip限制。 |
檢查tomcat/conf/web.xml配置文件,查看是否含有如下配置
<security-constraint> <web-resource-collection> <url-pattern>/*</url-pattern> <http-method>PUT</http-method> <http-method>DELETE</http-method> <http-method>HEAD</http-method> <http-method>OPTIONS</http-method> <http-method>TRACE</http-method> </web-resource-collection> <auth-constraint> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method>