性能優(yōu)化|Tomcat 服務優(yōu)化

一、Tomcat工作原理

1. TCP的三次握手四次揮手

三次握手:

說明:

類比于A和B打電話:

A對B說:你好,我是A,你能聽到我說話嗎?

B對A說:嗯,我能聽到你說話

A對B說:好,那我們開始聊天吧

在服務器上使用如下命令能看到當前服務器的連接情況

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

返回結果說明:

LAST_ACK 5 (正在等待處理的請求數(shù))

SYN_RECV 30

ESTABLISHED 1597 (正常數(shù)據(jù)傳輸狀態(tài))

FIN_WAIT1 51

FIN_WAIT2 504

TIME_WAIT 1057 (處理完畢,等待超時結束的請求數(shù))

四次揮手:

說明:

同樣用A和B打電話來說明:

A對B說:我說完了,我要掛電話了

B對A說:等一下,我還沒說完

B繼續(xù)對A說:我說完了,你可以掛電話了

A對B說:好,我掛電話了

其他參數(shù)說明:

CLOSED:無連接是活動的或正在進行

LISTEN:服務器在等待進入呼叫

SYN_RECV:一個連接請求已經(jīng)到達,等待確認

SYN_SENT:應用已經(jīng)開始,打開一個連接

ESTABLISHED:正常數(shù)據(jù)傳輸狀態(tài)

FIN_WAIT1:應用說它已經(jīng)完成

FIN_WAIT2:另一邊已同意釋放

ITMED_WAIT:等待所有分組死掉

CLOSING:兩邊同時嘗試關閉

TIME_WAIT:另一邊已初始化一個釋放

LAST_ACK:等待所有分組死掉

2. Tomcat內(nèi)部結構

上圖說明:

?server:指的是整個應用的上下文, 也是最頂層的容器,tomcat中所有的東西都在這個server里邊。

?service:指的是一個服務,主要的功能是把connector組件和engine組織起來,使得通過connector組件與整個容器通訊的應用可以使用engine提供的服務。

?engine:服務引擎,這個可以理解為一個真正的服務器,內(nèi)部提供了多個虛擬主機對外服務。

?host:虛擬主機,每一個虛擬主機相當于一臺服務器,并且內(nèi)部可以部署多個應用,每個虛擬主機可以綁定一個域名,并指定多個別名。

?context:應用上下文,每一個webapp都有一個單獨的context,也可以理解為每一個context代表一個webapp。

?connector:連接器組件,可以配置多個連接器支持多種協(xié)議,如http,APJ 等

組件說明:

Tomcat常見組件:

?服務器(server):Tomcat的一個實例,通常一個JVM只能包含一個Tomcat實例;因此,一臺物理服務器上可以在啟動多個JVM的情況下在每一個JVM中啟動一個Tomcat實例,每個實例分屬于一個獨立的管理端口。這是一個頂級組件。

?服務(service):一個服務組件通常包含一個引擎和與此引擎相關聯(lián)的一個或多個連接器。給服務命名可以方便管理員在日志文件中識別不同服務產(chǎn)生的日志。一個server可以包含多個service組件,但通常情下只為一個service指派一個server。

連接器類組件:

?連接器(connectors):負責連接客戶端(可以是瀏覽器或Web服務器)請求至Servlet容器內(nèi)的Web應用程序,通常指的是接收客戶發(fā)來請求的位置及服務器端分配的端口。默認端口通常是HTTP協(xié)議的8080,管理員也可以根據(jù)自己的需要改變此端口。還可以支持HTTPS ,默認HTTPS端口為8443。同時也支持AJP,即(A)一個引擎可以配置多個連接器,但這些連接器必須使用不同的端口。默認的連接器是基于HTTP/1.1的Coyote。同時,Tomcat也支持AJP、JServ和JK2連接器。

容器類組件:

?引擎(Engine):引擎通是指處理請求的Servlet引擎組件,即Catalina Servlet引擎,它檢查每一個請求的HTTP首部信息以辨別此請求應該發(fā)往哪個host或context,并將請求處理后的結果返回的相應的客戶端。嚴格意義上來說,容器不必非得通過引擎來實現(xiàn),它也可以是只是一個容器。如果Tomcat被配置成為獨立服務器,默認引擎就是已經(jīng)定義好的引擎。而如果Tomcat被配置為Apache Web服務器的提供Servlet功能的后端,默認引擎將被忽略,因為Web服務器自身就能確定將用戶請求發(fā)往何處。一個引擎可以包含多個host組件。

?主機(Host):主機組件類似于Apache中的虛擬主機,但在Tomcat中只支持基于FQDN的“虛擬主機”。一個引擎至少要包含一個主機組件。

?上下文(Context):Context組件是最內(nèi)層次的組件,它表示W(wǎng)eb應用程序本身。配置一個Context最主要的是指定Web應用程序的根目錄,以便Servlet容器能夠將用戶請求發(fā)往正確的位置。Context組件也可包含自定義的錯誤頁,以實現(xiàn)在用戶訪問發(fā)生錯誤時提供友好的提示信息。

被嵌套類(nested)組件:

這類組件通常包含于容器類組件中以提供具有管理功能的服務,它們不能包含其它組件,但有些卻可以由不同層次的容器各自配置。

?閥門(Valve):用來攔截請求并在將其轉至目標之前進行某種處理操作,類似于Servlet規(guī)范中定義的過濾器。Valve可以定義在任何容器類的組件中。Valve常被用來記錄客戶端請求、客戶端IP地址和服務器等信息,這種處理技術通常被稱作請求轉儲(request dumping)。請求轉儲valve記錄請求客戶端請求數(shù)據(jù)包中的HTTP首部信息和cookie信息文件中,響應轉儲valve則記錄響應數(shù)據(jù)包首部信息和cookie信息至文件中。

?日志記錄器(Logger):用于記錄組件內(nèi)部的狀態(tài)信息,可被用于除Context之外的任何容器中。日志記錄的功能可被繼承,因此,一個引擎級別的Logger將會記錄引擎內(nèi)部所有組件相關的信息,除非某內(nèi)部組件定義了自己的Logger組件。

?領域(Realm):用于用戶的認證和授權;在配置一個應用程序時,管理員可以為每個資源或資源組定義角色及權限,而這些訪問控制功能的生效需要通過Realm來實現(xiàn)。Realm的認證可以基于文本文件、數(shù)據(jù)庫表、LDAP服務等來實現(xiàn)。Realm的效用會遍及整個引擎或頂級容器,因此,一個容器內(nèi)的所有應用程序將共享用戶資源。同時,Realm可以被其所在組件的子組件繼承,也可以被子組件中定義的Realm所覆蓋。

二、優(yōu)化思路

1.?網(wǎng)絡優(yōu)化

BIO、NIO、NIO2、APR,也就是阻塞與非阻塞

壓縮gzip、超時配置,防止close_wait過多。

1.1、非阻塞,Tomcat8已經(jīng)取消BIO

四種請求連接模型

HTTP/1.1

org.apache.coyote.http11.Http11Protocol 阻塞模式的連接協(xié)議

org.apache.coyote.http11.Http11NioProtocol 非阻塞模式的連接協(xié)議

org.apache.coyote.http11.Http11Nio2Protocol 非阻塞模式的連接協(xié)議

org.apache.coyote.http11.Http11AprProtocol – 本地連接協(xié)議

1.2、啟用壓縮,消耗CPU,減小網(wǎng)絡傳輸大小

compression="on"

disableUploadTimeout="true"

compressionMinSize="2048"

compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,a

pplication/javascript"

URIEncoding="utf-8"

2. 并發(fā)優(yōu)化

最大線程數(shù)

最佳并發(fā)數(shù)。。。

連接數(shù):maxConnections(最大連接數(shù))

處理線程:maxThreads(操作系統(tǒng)允許多少線程,線程多大會引起切換效能)

等候隊列:acceptCount(排隊數(shù)量)指最大連接數(shù)已經(jīng)滿了的時候允許多少請求排隊

3. 底層優(yōu)化

JVM優(yōu)化

多實例(必須的)

操作系統(tǒng)優(yōu)化

JVM優(yōu)化:固定堆內(nèi)存,多線程并發(fā)收集,對象預留新生代,大對象進入老年代,啟用內(nèi)聯(lián)

多實例:多個tomcat實例在一臺機上

操作系統(tǒng)優(yōu)化:網(wǎng)絡參數(shù),線程數(shù),關閉IPV6,最大文件數(shù)

Linux服務器每進程不允許超過1000個線程,據(jù)說6、700線程服務器切換線程就慢下來

命令:ps -eLf | grep java | wc –l? 可以查看當前啟動的java進程里面有多少個線程

Linux線程棧大小是8M,可以使用ulimit –s設置

三、優(yōu)化實戰(zhàn)

1. 優(yōu)化tomcat.conf配置文件

/etc/tomcat/tomcat.conf文件修改JAVA_OPTS

JAVA_OPTS=“-server –Xmx2048m–Xms2048m –Xmn768m - XX:TargetSurvivorRatio=90 -XX:PetenureSizeThreshold=1000000 - XX:MaxTenuringThreshold=30 –XX:+UseParallelGC

–XX:+UseConcMarkSweepGC –XX:ParallelGCThreads=2"

2. 優(yōu)化server.conf配置文件

/etc/tomcat/server.conf文件修改配置

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" //最大并發(fā)數(shù),默認設置 200,一般建議在 500 ~ 800,根據(jù)硬件設施和業(yè)務來判斷

minSpareThreads="100" //Tomcat 初始化時創(chuàng)建的線程數(shù),默認設置 25

prestartminSpareThreads = "true"http://在 Tomcat 初始化的時候就初始化 minSpareThreads 的參數(shù)值,如果不等于 true,minSpareThreads 的值就無效

maxQueueSize = "100"http://最大的等待隊列數(shù),超過則拒絕請求 />

<Connector executor="tomcatThreadPool" port="8080"

protocol="org.apache.coyote.http11.Http11Nio2Protocol" //Tomcat 8 設置 nio2 更好,Tomcat 6 、7設置nio更好:org.apache.coyote.http11.Http11NioProtocol

connectionTimeout="20000"

minSpareThreads="100" maxSpareThreads="1000"最大處理連接數(shù)線程

minProcessors="100“同時處理請求的最小數(shù)

maxProcessors=“1000”同時處理請求的最大數(shù)

maxConnections="1000" redirectPort="8443"

enableLookups="false" //禁用DNS查詢 acceptCount="100" //指定當所有可以使用的處理請求的線程數(shù)都被使用時,可以放到處理隊列中的請求數(shù),超過這個數(shù)的請求將不予處理,默認設置 100

maxPostSize="10485760" //以 FORM URL 參數(shù)方式的 POST 提交方式,限制提交最大的大小,默認是2097152(2兆),它使用的單位是字節(jié)。10485760 為 10M。如果要禁用限制,則可以設置為 -1。

compression="on" disableUploadTimeout="true" compressionMinSize="2048"

acceptorThreadCount="2" //用于接收連接的線程的數(shù)量,默認值是1。一般這個指需要改動的時候是因為該服務器是一個多核CPU,如果是多核CPU一般配置為 2.

compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/ja

vascript" URIEncoding="utf-8" keepAliveTimeout="0"

關閉shutdown端口:<Server port="-1" shutdown="SHUTDOWN">

關閉ajp連接:注釋<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

取消訪問日志Valve閥門

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"

prefix="localhost_access_log." suffix=".txt"

pattern="%h %l %u %t &quot;%r&quot; %s %b" />

/>

圖示:

3. linux內(nèi)核優(yōu)化

3.1 linux 默認值 open files 和 max user processes 為 1024

#ulimit -n

1024

#ulimit –u

1024

問題描述:

說明server只允許同時打開1024個文件,處理1024個用戶進程,使用ulimit -a 可以查看當前系統(tǒng)的所有限制值,使用ulimit -n可以查看當前的最大打開文件數(shù)。新裝的linux 默認只有1024,當作負載較大的服務器時,很容易遇到error: too many open files 。

解決方法:

使用 ulimit –n 65535 可即時修改,但重啟后就無效了。

有如下三種修改方式:

在/etc/security/limits.conf 最后增加:

* soft nofile 65535

* hard nofile 65535

* soft nproc 65535

* hard nproc 65535

3.2 其他的linux配置優(yōu)化

net.ipv4.tcp_syncookies = 1 開啟SYN Cookies。當出現(xiàn)SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊;

net.ipv4.tcp_tw_reuse = 1 開啟重用。允許將TIME-WAIT sockets重新用于新的TCP連接,默認為0

net.ipv4.tcp_tw_recycle = 1 開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉。

net.ipv4.tcp_fin_timeout = 30 如果套接字由本端要求關閉,它決定了它保持在FIN-WAIT-2狀態(tài)的時間。

net.ipv4.tcp_keepalive_time = 1200 當keepalive起用的時候,TCP發(fā)送keepalive消息的頻度。缺省是2小時

net.ipv4.tcp_keepalive_intvl = 30

net.ipv4.tcp_keepalive_probes = 3 probe 3次(每次30秒)不成功,內(nèi)核才徹底放棄。

tcp_keepalive_time = 7200 seconds (2 hours)

tcp_keepalive_probes = 9

tcp_keepalive_intvl = 75 seconds

net.ipv4.ip_local_port_range = 1024 65000 用于向外連接的端口范圍。缺省情況下很?。?2768到61000,改為1024到65000。

net.ipv4.tcp_max_syn_backlog = 8192 SYN隊列的長度,默認為1024,加大隊列長度為8192,可以容納更多等待連接的網(wǎng)絡連接數(shù)。

net.ipv4.netdev_max_backlog = 1000 表示進入包的最大設備隊列,默認300,改大

net.core.tcp_max_tw_buckets = 5000 系統(tǒng)同時保持TIME_WAIT套接字的最大數(shù)量,如果超過這個數(shù)字,TIME_WAIT套接字將立刻被清除并打印警告信息。默認為180000,改為 5000。

另外可以參考優(yōu)化內(nèi)核配置:

/proc/sys/net/core/wmem_max 最大socket寫buffer,可參考的優(yōu)化值:873200

/proc/sys/net/core/rmem_max 最大socket讀buffer,可參考的優(yōu)化值:873200

/proc/sys/net/ipv4/tcp_wmem TCP寫buffer,可參考的優(yōu)化值: 8192 436600 873200

/proc/sys/net/ipv4/tcp_rmem TCP讀buffer,可參考的優(yōu)化值: 32768 436600 873200

/proc/sys/net/ipv4/tcp_mem

同樣有3個值,意思是:配置單位為頁,不是字節(jié)

net.ipv4.tcp_mem[0]:低于此值,TCP沒有內(nèi)存壓力. 786432

net.ipv4.tcp_mem[1]:在此值下,進入內(nèi)存壓力階段. 1048576

net.ipv4.tcp_mem[2]:高于此值,TCP拒絕分配socket. 1572864

/proc/sys/net/core/somaxconn 256

listen()的默認參數(shù),掛起請求的最大數(shù)量.默認是128.對繁忙的服務器,增加該值有助于網(wǎng)絡性能.

/proc/sys/net/core/optmem_max socket buffer的最大初始化值,默認10K.

/proc/sys/net/ipv4/tcp_retries2 TCP失敗重傳次數(shù),默認值15.減少到5,以盡早釋放內(nèi)核資源.

net.core.somaxconn = 32768 socket監(jiān)聽(listen)的backlog上限,是socket的監(jiān)聽隊列。比如nginx定義

NGX_LISTEN_BACKLOG默認到511

4. nginx優(yōu)化

1. worker_processes 8;nginx 進程數(shù),建議按照cpu 數(shù)目來指定,一般為它的倍數(shù) (如,2個四核的cpu計為8)。

2. worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;為每個進程分配cpu,上例中將8個進程分配到8個cpu,當然可以寫多個,或者將一個進程分配到多個cpu。

3. worker_rlimit_nofile 65535;這個指令是指當一個nginx 進程打開的最多文件描述符數(shù)目,理論值應該是最多打開文件數(shù)(ulimit -n)與nginx進程數(shù)相除,但是nginx分配請求并不是那么均勻,所以最好與ulimit -n 的值保持一致。

查看linux系統(tǒng)文件描述符的方法:

[root@web001 ~]# sysctl -a | grep fs.file

fs.file-max = 789972

fs.file-nr = 510 0 789972

4. use epoll; 使用epoll 的I/O 模型

5. worker_connections 65535;每個進程允許的最多連接數(shù),理論上每臺nginx 服務器的最大連接數(shù)為worker_processes*worker_connections。

6. keepalive_timeout 60;keepalive 超時時間。

7. client_header_buffer_size 4k;客戶端請求頭部的緩沖區(qū)大小,這個可以根據(jù)你的系統(tǒng)分頁大小來設置,一般一個請求頭的大小不會超過1k,不過由于一般系統(tǒng)分頁都要大于1k,所以這里設置為分頁大小。分頁大小可以用命令getconf PAGESIZE 取得。

[root@web001 ~]# getconf PAGESIZE

4096

但也有client_header_buffer_size超過4k的情況,但是client_header_buffer_size該值必須設置為“系統(tǒng)分頁大小”的整倍數(shù)。

8. open_file_cache max=65535 inactive=60s;這個將為打開文件指定緩存,默認是沒有啟用的,max 指定緩存數(shù)量,建議和打開文件數(shù)一致,inactive 是指經(jīng)過多長時間文件沒被請求后刪除緩存。

9. open_file_cache_valid 80s;這個是指多長時間檢查一次緩存的有效信息。

10. open_file_cache_min_uses 1;open_file_cache 指令中的inactive 參數(shù)時間內(nèi)文件的最少使用次數(shù),如果超過這個數(shù)字,文件描述符一直是在緩存中打開的,如上例,如果有一個文件在inactive 時間內(nèi)一次沒被使用,它將被移除。

四、集群優(yōu)化

當線程數(shù)達到250以上,考慮群集部署,集群部署需要考慮的兩個問題:Tomcat部署和session共享,Tomcat<4時,可用tomcat內(nèi)部的集群session共享,否則采用redis方式集群

集群部署原理圖:

redis實現(xiàn)session共享的原理


Redis實現(xiàn)seesion共享的步驟如下:

1.?下載以下包放到tomcat的lib目錄下

TomcatRedisSessionManager-1.1 .jar

Jredis-2.8.0.jar

Commons-logging-1.2.jar

Commons-pool2-2.4.1.jar

2. 在tomcat里面增加如下配置

<Valve className="tomcat.request.session.redis.RequestSessionHandlerValve"/>

<Manager className="tomcat.request.session.redis.RequestSessionManager"/>


?創(chuàng)建一個redis的配置文件redis-data-cache.properties,放在conf.d目錄

redis.hosts=127.0.0.1:6379

redis.cluster.enabled=false

#- redis database (default 0)

#redis.database=0

#- redis connection timeout (default 2000)

#redis.timeout=2000

五、壓力測試

Ab測試

吞吐率(Requests per second):總請求數(shù) / 處理完成這些請求數(shù)所花費的時間

并發(fā)連接數(shù)(The number of concurrent users,Concurrency Level):一個用戶可能同時會產(chǎn)生多個會話,也即連接數(shù)

用戶平均請求等待時間(Time per request):處理完成所有請求數(shù)所花費的時間/ (總請求數(shù) / 并發(fā)用戶數(shù))

服務器平均請求等待時間(Time per request: across all concurrent?requests)計 算 公 式 : 處 理 完 成 所 有 請 求 數(shù) 所 花 費 的 時 間 / 總 請 求 數(shù)

使用示例:

ab –n 1000 –c 100 url/

如果只用到一個Cookie,那么只需鍵入命令:

ab -n 100 -C key=value http://test.com/

如果需要多個Cookie,就直接設Header:

ab -n 100 -H “Cookie: Key1=Value1; Key2=Value2” http://test.com/

歡迎工作一到五年的Java工程師朋友們加入Java架構開發(fā):760940986

群內(nèi)提供免費的Java架構學習資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容