Author:放翁(文初)
Date: 2010/4/14
Email:fangweng@taobao.com
圍脖: http://t.sina.com.cn/fangweng
這部分是結果,大家可以當看倒序的電影,后續會有前篇給出。
Web服務異步化:
包括兩部分,數據傳輸層異步化(大家已經熟知的NIO),Http業務請求異步化(continuations,servlet3.0)。服務異步處理我將會有一個詳細的說明文檔(服務異步化的概念,服務異步化的幾種標準實現,服務異步化容器的特點),后續給出。
Web服務異步化測試原因:
TOP應用特殊性:
1.自身服務能力由后端的服務能力決定。(對同步Web請求的轉發)
2.后端服務部署等同性,但要求服務互不影響。
第一點導致TOP無法預估自身服務能力(不同后端服務處理速度下的TOP有不一樣的支持能力),同時也無法應對在后端服務異常的情況下,整體的服務質量。
第二點導致TOP只有在物理上分隔不同服務提供者所對應的TOP集群(資源浪費,同時無法動態調整資源來滿足服務變化情況)。
因此需要對TOP實施web服務異步處理的測試。這里簡單的說一下服務異步化的使用場景需要滿足的幾個特點:
1. 處理耗時大多消耗在于對后端或者外部服務資源的請求上。
2. 后端或者外部資源在更多的流量下不會成為瓶頸。
拿TOP來解釋一下:TOP自身處理主要包括路由,安全,流控等,但是最耗時的是在請求后端各個淘寶團隊的服務。其次當前后端服務能力尚未達到真實的處理高峰,因此很多請求被堵在TOP平臺,特別是當某些服務異常的時候,另一些服務就會被拖累無法得到充分利用。(當然我們有流控,發現后端服務能力已經成為瓶頸的時候可以對單獨服務作限制)。
長話短說,上測試結果……
環境說明:
Linux 2.6.9-55.ELsmp
4 Core
4 G Memory
JDK 1.6.0_07
測試工具:loadRunner 9.5
測試涉及到了四種容器部署:后面都會用縮寫在測試結果上注明
1. Apache + modjk + Jboss(后面縮寫為Jboss):
此模式Apache配置如下:
<IfModule mpm_worker_module>
ServerLimit 80
ThreadLimit 128
StartServers 10
MaxClients 10240
MinSpareThreads 64
MaxSpareThreads 800
ThreadsPerChild 128
MaxRequestsPerChild 10000
</IfModule>
Jboss的web容器配置如下:
<Connector port="8009" address="${jboss.bind.address}" connectionTimeout="8000" protocol="AJP/1.3" maxThreads="500" minSpareThreads="40" maxSpareThreads="75" maxPostSize="512000" acceptCount="300" bufferSize="16384" emptySessionPath="false" enableLookups="false" redirectPort="8443" URIEncoding="utf-8"/>
Jboss的web部分以APR模式啟動。
2. Tomcat6(APR)
關鍵配置如下:
<Executor name="topThreadPool" namePrefix="top-exec-"
maxThreads="500" minSpareThreads="40"/>
<Connector port="7777" protocol="HTTP/1.1"
executor="topThreadPool" connectionTimeout="20000" acceptCount="1000"
redirectPort="8444" />
3. Tomcat7 RC 4(APR)
關鍵配置如下:
<Executor name="topThreadPool" namePrefix="top-exec-"
maxThreads="500" minSpareThreads="4"/>
<Connector executor="topThreadPool" port="3333" protocol="HTTP/1.1"
connectionTimeout="20000" acceptCount="1000"
redirectPort="6443" />
4. Jetty7
關鍵配置如下:
<Set name="ThreadPool">
<!-- Default queued blocking threadpool -->
<New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<Set name="minThreads">10</Set>
<Set name="maxThreads">500</Set>
</Set>
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
<Set name="host"><SystemProperty name="jetty.host" /></Set>
<Set name="port"><SystemProperty name="jetty.port" default="6060"/></Set>
<Set name="maxIdleTime">300000</Set>
<Set name="Acceptors">2</Set>
<Set name="statsOn">false</Set>
<Set name="confidentialPort">8443</Set>
<Set name="lowResourcesConnections">20000</Set>
<Set name="lowResourcesMaxIdleTime">5000</Set>
</New>
</Arg>
</Call>
附注:
Asyn表示異步模式,syn表示同步。Asyn中還分成resume和complete兩種方式,后續在介紹技術背景的時候詳細描述。
對于服務端的load不是每一個測試都做了記錄,選取了最全面的1500并發用戶做了測試。
最大服務請求處理數是通過應用自身實現,具體代碼可以參考后面的代碼附件。
測試結果如下:
場景1:500 并發用戶場景下,后端服務一次請求消耗3秒鐘
容器
|
模式
|
TPS
|
Average Response Time(s)
|
Average Throughput(byte/s)
|
最大請求處理數
|
Success rate
|
Jetty7
|
asyn(resume)
|
162.8
|
3.008
|
38430
|
500
|
100%
|
Tomcat6
|
syn
|
163.3
|
3.005
|
18453
|
500
|
100%
|
這個場景測試的目的是比較在線程池資源足夠的時候,異步和同步的差別。(也就是TOP服務器所有的資源處于正常服務,前臺請求沒有因為前段連接被消耗完,導致服務質量降低)
可以看到,在TPS和Response Time上兩者基本上沒有太大差別,TPS就等于500/3=167左右(3秒一個請求,因此用這種簡單算式可以算出),響應時間也較為正常。當時我發現在每秒吞吐量上有些差別,后來單個測試case跑了一下,發現是返回的http header比較大,應該是在做異步化時重入等作的一些標識(后面其他容器的異步化也是一樣)。
最大處理請求數都在服務端后臺看到是500,等同于最大的并發用戶數。
場景2:1000 并發用戶場景下,后端服務一次請求消耗3秒鐘
容器
|
模式
|
TPS
|
Average Response Time(s)
|
Average Throughput(byte/s)
|
最大請求處理數
|
Success rate
|
Jetty7
|
asyn(resume)
|
317.06
|
3.036
|
74826
|
1000
|
100%
|
Tomcat6
|
syn
|
163.323
|
5.904
|
18455
|
500
|
100%
|
場景2就在資源不足的情況下,比較異步服務請求與同步請求處理能力。(例如由于后端某些服務比較慢,導致前段的服務器能夠承載的請求數目超過了線程數)
這個場景的結果可以看到TPS在異步模式下與并發用戶數呈現同步增長,就好比配置了1000個線程作為線程池一樣,同樣在后端打出的最大請求數上也證明了這一點,因此前段線程池的服務能力在異步的情況下充分復用(當然這里使用的異步服務處理使用的是NIO而不是BIO的Connector)。同樣在吞吐量上依然是增加的,由于異步附加的內容。
場景3:1500 并發用戶場景下,后端服務一次請求消耗3秒鐘
容器
|
模式
|
TPS
|
Average Response Time(s)
|
Average Throughput(byte/s)
|
Server Load
|
Success rate
|
Jboss
|
syn
|
75.546
|
5.347
|
21002
|
0.115
|
68%
|
Jetty7
|
Syn
|
163.156
|
8.788
|
19252
|
0.129
|
100%
|
Jetty7
|
Asyn(complete)
|
432.153
|
3.312
|
76491
|
2.649
|
100%
|
Jetty7
|
Asyn(resume)
|
423.638
|
3.375
|
99979
|
2.826
|
100%
|
Tomcat6
|
Syn
|
163.836
|
8.75
|
18513
|
0.258
|
100%
|
Tomcat7
|
ASyn
|
423.501
|
3.328
|
54632
|
1.064
|
99.3%
|
場景三比對了現有TOP的部署模式(Apache + modjk + Jboss)和Jetty7的同步模式,兩種異步模式,Tomcat同步模式,Tomcat的servlet3.0異步模式的處理情況。根據測試可以得到的信息如下:
1. 現有部署方式在后端服務處理耗時較大的情況下,處理能力不如Jetty7和Tomcat6,同時出錯率很高。
2. Jetty7的同步處理測試結果和Tomcat6的同步處理測試結果很類似,但是load方面jetty7更好。
3. 異步處理方面Jetty7的兩種方式基本上差別不大(后續還需要深入源碼看看對于數據緩存資源復用的狀況),Tomcat7的異步處理成功率有些問題(錯誤多半是在獲取response回寫的時候,response已經被提前釋放,看來Tomcat7還是需要一些時間來考驗),load上來說tomcat結果比較好。
4. 異步請求在提高處理能力的情況下,對于資源消耗也較大(線程切換較為頻繁),不過還是在承受范圍。
三個場景組合比較:
容器
|
并發用戶
|
模式
|
TPS
|
Average Response Time(s)
|
Average Throughput(byte/s)
|
Success rate
|
Jetty7
|
500
|
asyn(resume)
|
162.8
|
3.008
|
38430
|
100%
|
Jetty7
|
1000
|
asyn(resume)
|
317.06
|
3.036
|
74826
|
100%
|
Jetty7
|
1500
|
asyn(resume)
|
423.638
|
3.375
|
99979
|
100%
|
Tomcat6
|
500
|
syn
|
163.3
|
3.005
|
18453
|
100%
|
Tomcat6
|
1000
|
syn
|
163.323
|
5.904
|
18455
|
100%
|
Tomcat6
|
1500
|
Syn
|
163.836
|
8.75
|
18513
|
100%
|
最后將三個場景合并起來做一個簡單的比較,得到信息如下:
1. 隨著并發用戶的增加,本身異步處理也會有衰減,同時對于性能消耗(線程切換)也會不斷增長。
2. 異步化在消息頭上會增加一些數據,會增加回寫的帶寬消耗(不過量不大),一個請求增加了100byte左右的消息數據。
測試總結:
1. Web請求異步化在TOP很合適。
重復兩個條件:
a. 處理耗時大多消耗在于對后端或者外部服務資源的請求上。
b. 后端或者外部資源在更多的流量下不會成為瓶頸。
2. Web請求異步化在Jetty6到7上已經經歷了2年多的成長(Google App Engine , Yahoo Hadoop),穩定性和效率較好。(同時很多優化可以通過擴展自行定制,jetty的可擴展性很好)Tomcat7在servlet3上處于剛發布階段,還有待繼續完善。(Servlet3的另一種模式尚未執行成功,直接導致jvm退出)
后續需要繼續跟進的:
1. 對于大數據量請求的容器間性能對比。(圖片上傳或者大數據量的Post請求)
2. 容器安全性。(是否容易被攻擊)
3. 代碼遷移成本。
后續篇涉及:服務異步化的概念,服務異步化的幾種標準實現,服務異步化容器的特點和實現,現有容器可優化的點。
打個廣告:http://blog.open.taobao.com/archives/1417 TOP需要人才加入