一、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 (正在等待處理的請求數)
SYN_RECV 30
ESTABLISHED 1597 (正常資料傳輸狀態)
FIN_WAIT1 51
FIN_WAIT2 504
TIME_WAIT 1057 (處理完畢,等待超時結束的請求數)
四次揮手:
說明:
同樣用A和B打電話來說明:
A對B說:我說完了,我要掛電話了
B對A說:等一下,我還沒說完
B繼續對A說:我說完了,你可以掛電話了
A對B說:好,我掛電話了
其他引數說明:
CLOSED:無連線是活動的或正在進行
LISTEN:伺服器在等待進入呼叫
SYN_RECV:一個連線請求已經到達,等待確認
SYN_SENT:應用已經開始,開啟一個連線
ESTABLISHED:正常資料傳輸狀態
FIN_WAIT1:應用說它已經完成
FIN_WAIT2:另一邊已同意釋放
ITMED_WAIT:等待所有分組死掉
CLOSING:兩邊同時嘗試關閉
TIME_WAIT:另一邊已初始化一個釋放
LAST_ACK:等待所有分組死掉
2. Tomcat內部結構
上圖說明:
•server:指的是整個應用的上下文, 也是最頂層的容器,tomcat中所有的東西都在這個server裡邊。
•service:指的是一個服務,主要的功能是把connector元件和engine組織起來,使得通過connector元件與整個容器通訊的應用可以使用engine提供的服務。
•engine:服務引擎,這個可以理解為一個真正的伺服器,內部提供了多個虛擬主機對外服務。
•host:虛擬主機,每一個虛擬主機相當於一臺伺服器,並且內部可以部署多個應用,每個虛擬主機可以繫結一個域名,並指定多個別名。
•context:應用上下文,每一個webapp都有一個單獨的context,也可以理解為每一個context代表一個webapp。
•connector:聯結器元件,可以配置多個聯結器支援多種協議,如http,APJ 等
元件說明:
Tomcat常見元件:
•伺服器(server):Tomcat的一個例項,通常一個JVM只能包含一個Tomcat例項;因此,一臺物理伺服器上可以在啟動多個JVM的情況下在每一個JVM中啟動一個Tomcat例項,每個例項分屬於一個獨立的管理埠。這是一個頂級元件。
•服務(service):一個服務元件通常包含一個引擎和與此引擎相關聯的一個或多個聯結器。給服務命名可以方便管理員在日誌檔案中識別不同服務產生的日誌。一個server可以包含多個service元件,但通常情下只為一個service指派一個server。
聯結器類元件:
•聯結器(connectors):負責連線客戶端(可以是瀏覽器或Web伺服器)請求至Servlet容器內的Web應用程式,通常指的是接收客戶發來請求的位置及伺服器端分配的埠。預設埠通常是HTTP協議的8080,管理員也可以根據自己的需要改變此埠。還可以支援HTTPS ,預設HTTPS埠為8443。同時也支援AJP,即(A)一個引擎可以配置多個聯結器,但這些聯結器必須使用不同的埠。預設的聯結器是基於HTTP/1.1的Coyote。同時,Tomcat也支援AJP、JServ和JK2聯結器。
容器類元件:
•引擎(Engine):引擎通是指處理請求的Servlet引擎元件,即Catalina Servlet引擎,它檢查每一個請求的HTTP首部資訊以辨別此請求應該發往哪個host或context,並將請求處理後的結果返回的相應的客戶端。嚴格意義上來說,容器不必非得通過引擎來實現,它也可以是隻是一個容器。如果Tomcat被配置成為獨立伺服器,預設引擎就是已經定義好的引擎。而如果Tomcat被配置為Apache Web伺服器的提供Servlet功能的後端,預設引擎將被忽略,因為Web伺服器自身就能確定將使用者請求發往何處。一個引擎可以包含多個host元件。
•主機(Host):主機元件類似於Apache中的虛擬主機,但在Tomcat中只支援基於FQDN的“虛擬主機”。一個引擎至少要包含一個主機元件。
•上下文(Context):Context元件是最內層次的元件,它表示Web應用程式本身。配置一個Context最主要的是指定Web應用程式的根目錄,以便Servlet容器能夠將使用者請求發往正確的位置。Context元件也可包含自定義的錯誤頁,以實現在使用者訪問發生錯誤時提供友好的提示資訊。
被巢狀類(nested)元件:
這類元件通常包含於容器類元件中以提供具有管理功能的服務,它們不能包含其它元件,但有些卻可以由不同層次的容器各自配置。
•閥門(Valve):用來攔截請求並在將其轉至目標之前進行某種處理操作,類似於Servlet規範中定義的過濾器。Valve可以定義在任何容器類的元件中。Valve常被用來記錄客戶端請求、客戶端IP地址和伺服器等資訊,這種處理技術通常被稱作請求轉儲(request dumping)。請求轉儲valve記錄請求客戶端請求資料包中的HTTP首部資訊和cookie資訊檔案中,響應轉儲valve則記錄響應資料包首部資訊和cookie資訊至檔案中。
•日誌記錄器(Logger):用於記錄元件內部的狀態資訊,可被用於除Context之外的任何容器中。日誌記錄的功能可被繼承,因此,一個引擎級別的Logger將會記錄引擎內部所有元件相關的資訊,除非某內部元件定義了自己的Logger元件。
•領域(Realm):用於使用者的認證和授權;在配置一個應用程式時,管理員可以為每個資源或資源組定義角色及許可權,而這些訪問控制功能的生效需要通過Realm來實現。Realm的認證可以基於文字檔案、資料庫表、LDAP服務等來實現。Realm的效用會遍及整個引擎或頂級容器,因此,一個容器內的所有應用程式將共享使用者資源。同時,Realm可以被其所在元件的子元件繼承,也可以被子元件中定義的Realm所覆蓋。
二、優化思路
1. 網路優化
BIO、NIO、NIO2、APR,也就是阻塞與非阻塞
壓縮gzip、超時配置,防止close_wait過多。
1.1、非阻塞,Tomcat8已經取消BIO
四種請求連線模型
HTTP/1.1
org.apache.coyote.http11.Http11Protocol 阻塞模式的連線協議
org.apache.coyote.http11.Http11NioProtocol 非阻塞模式的連線協議
org.apache.coyote.http11.Http11Nio2Protocol 非阻塞模式的連線協議
org.apache.coyote.http11.Http11AprProtocol – 本地連線協議
1.2、啟用壓縮,消耗CPU,減小網路傳輸大小
compression="on"
disableUploadTimeout="true"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,a
pplication/javascript"
URIEncoding="utf-8"
2. 併發優化
最大執行緒數
最佳併發數。。。
連線數:maxConnections(最大連線數)
處理執行緒:maxThreads(作業系統允許多少執行緒,執行緒多大會引起切換效能)
等候佇列:acceptCount(排隊數量)指最大連線數已經滿了的時候允許多少請求排隊
3. 底層優化
JVM優化
多例項(必須的)
作業系統優化
JVM優化:固定堆記憶體,多執行緒併發收集,物件預留新生代,大物件進入老年代,啟用內聯
多例項:多個tomcat例項在一臺機上
作業系統優化:網路引數,執行緒數,關閉IPV6,最大檔案數
Linux伺服器每程式不允許超過1000個執行緒,據說6、700執行緒伺服器切換執行緒就慢下來
命令:ps -eLf | grep java | wc –l 可以檢視當前啟動的java程式裡面有多少個執行緒
Linux執行緒棧大小是8M,可以使用ulimit –s設定
三、優化實戰
1. 優化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. 優化server.conf配置檔案
/etc/tomcat/server.conf檔案修改配置
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" //最大併發數,預設設定 200,一般建議在 500 ~ 800,根據硬體設施和業務來判斷
minSpareThreads="100" //Tomcat 初始化時建立的執行緒數,預設設定 25
prestartminSpareThreads = "true"//在 Tomcat 初始化的時候就初始化 minSpareThreads 的引數值,如果不等於 true,minSpareThreads 的值就無效
maxQueueSize = "100"//最大的等待佇列數,超過則拒絕請求 />
<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"最大處理連線數執行緒
minProcessors="100“同時處理請求的最小數
maxProcessors=“1000”同時處理請求的最大數
maxConnections="1000" redirectPort="8443"
enableLookups="false" //禁用DNS查詢 acceptCount="100" //指定當所有可以使用的處理請求的執行緒數都被使用時,可以放到處理佇列中的請求數,超過這個數的請求將不予處理,預設設定 100
maxPostSize="10485760" //以 FORM URL 引數方式的 POST 提交方式,限制提交最大的大小,預設是2097152(2兆),它使用的單位是位元組。10485760 為 10M。如果要禁用限制,則可以設定為 -1。
compression="on" disableUploadTimeout="true" compressionMinSize="2048"
acceptorThreadCount="2" //用於接收連線的執行緒的數量,預設值是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 "%r" %s %b" />
/>複製程式碼
圖示:
3. linux核心優化
3.1 linux 預設值 open files 和 max user processes 為 1024
#ulimit -n
1024
#ulimit –u
1024
問題描述:
說明server只允許同時開啟1024個檔案,處理1024個使用者程式,使用ulimit -a 可以檢視當前系統的所有限制值,使用ulimit -n可以檢視當前的最大開啟檔案數。新裝的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配置優化
net.ipv4.tcp_syncookies = 1 開啟SYN Cookies。當出現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狀態的時間。
net.ipv4.tcp_keepalive_time = 1200 當keepalive起用的時候,TCP傳送keepalive訊息的頻度。預設是2小時
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3 probe 3次(每次30秒)不成功,核心才徹底放棄。
tcp_keepalive_time = 7200 seconds (2 hours)
tcp_keepalive_probes = 9
tcp_keepalive_intvl = 75 seconds
net.ipv4.ip_local_port_range = 1024 65000 用於向外連線的埠範圍。預設情況下很小:32768到61000,改為1024到65000。
net.ipv4.tcp_max_syn_backlog = 8192 SYN佇列的長度,預設為1024,加大佇列長度為8192,可以容納更多等待連線的網路連線數。
net.ipv4.netdev_max_backlog = 1000 表示進入包的最大裝置佇列,預設300,改大
net.core.tcp_max_tw_buckets = 5000 系統同時保持TIME_WAIT套接字的最大數量,如果超過這個數字,TIME_WAIT套接字將立刻被清除並列印警告資訊。預設為180000,改為 5000。
另外可以參考優化核心配置:
/proc/sys/net/core/wmem_max 最大socket寫buffer,可參考的優化值:873200
/proc/sys/net/core/rmem_max 最大socket讀buffer,可參考的優化值:873200
/proc/sys/net/ipv4/tcp_wmem TCP寫buffer,可參考的優化值: 8192 436600 873200
/proc/sys/net/ipv4/tcp_rmem TCP讀buffer,可參考的優化值: 32768 436600 873200
/proc/sys/net/ipv4/tcp_mem
同樣有3個值,意思是:配置單位為頁,不是位元組
net.ipv4.tcp_mem[0]:低於此值,TCP沒有記憶體壓力. 786432
net.ipv4.tcp_mem[1]:在此值下,進入記憶體壓力階段. 1048576
net.ipv4.tcp_mem[2]:高於此值,TCP拒絕分配socket. 1572864
/proc/sys/net/core/somaxconn 256
listen()的預設引數,掛起請求的最大數量.預設是128.對繁忙的伺服器,增加該值有助於網路效能.
/proc/sys/net/core/optmem_max socket buffer的最大初始化值,預設10K.
/proc/sys/net/ipv4/tcp_retries2 TCP失敗重傳次數,預設值15.減少到5,以儘早釋放核心資源.
net.core.somaxconn = 32768 socket監聽(listen)的backlog上限,是socket的監聽佇列。比如nginx定義
NGX_LISTEN_BACKLOG預設到511複製程式碼
4. nginx優化
1. worker_processes 8;nginx 程式數,建議按照cpu 數目來指定,一般為它的倍數 (如,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 程式開啟的最多檔案描述符數目,理論值應該是最多開啟檔案數(ulimit -n)與nginx程式數相除,但是nginx分配請求並不是那麼均勻,所以最好與ulimit -n 的值保持一致。
檢視linux系統檔案描述符的方法:
[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;每個程式允許的最多連線數,理論上每臺nginx 伺服器的最大連線數為worker_processes*worker_connections。
6. keepalive_timeout 60;keepalive 超時時間。
7. client_header_buffer_size 4k;客戶端請求頭部的緩衝區大小,這個可以根據你的系統分頁大小來設定,一般一個請求頭的大小不會超過1k,不過由於一般系統分頁都要大於1k,所以這裡設定為分頁大小。分頁大小可以用命令getconf PAGESIZE 取得。
[root@web001 ~]# getconf PAGESIZE
4096
但也有client_header_buffer_size超過4k的情況,但是client_header_buffer_size該值必須設定為“系統分頁大小”的整倍數。
8. open_file_cache max=65535 inactive=60s;這個將為開啟檔案指定快取,預設是沒有啟用的,max 指定快取數量,建議和開啟檔案數一致,inactive 是指經過多長時間檔案沒被請求後刪除快取。
9. open_file_cache_valid 80s;這個是指多長時間檢查一次快取的有效資訊。
10. open_file_cache_min_uses 1;open_file_cache 指令中的inactive 引數時間內檔案的最少使用次數,如果超過這個數字,檔案描述符一直是在快取中開啟的,如上例,如果有一個檔案在inactive 時間內一次沒被使用,它將被移除。
四、叢集優化
當執行緒數達到250以上,考慮群集部署,叢集部署需要考慮的兩個問題:Tomcat部署和session共享,Tomcat<4時,可用tomcat內部的叢集session共享,否則採用redis方式叢集
叢集部署原理圖:
redis實現session共享的原理
Redis實現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"/>複製程式碼
建立一個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):總請求數 / 處理完成這些請求數所花費的時間
併發連線數(The number of concurrent users,Concurrency Level):一個使用者可能同時會產生多個會話,也即連線數
使用者平均請求等待時間(Time per request):處理完成所有請求數所花費的時間/ (總請求數 / 併發使用者數)
伺服器平均請求等待時間(Time per request: across all concurrent requests)計 算 公 式 : 處 理 完 成 所 有 請 求 數 所 花 費 的 時 間 / 總 請 求 數
使用示例:
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/