Web服務請求異步化介紹(實踐篇)
Author:放翁(文初)
Date: 2010/8/5
Email:fangweng@taobao.com
圍脖: http://t.sina.com.cn/fangweng
在概念篇介紹完以后,開始實際的對TOP開始做技術改造。(這篇東西更像是對短期工作的總結和匯報,寫的不是很詳實,后續會有一個ppt來深化異步化的一些思想)下面將第一階段的工作做個總結,第一階段主要做了以下幾個方面的事情:
1. 典型taobao后臺應用(主要是用到了多個內部組件)的Jetty遷移。
2. TOP管道化體系的異步改造。
3. 測試不同容器不同模式下的應用處理能力,并通過數據得出結論。
一.應用遷移及容器部署
這次遷移主要的工作就是在服務框架的遷移,服務框架內部是小的OSGI容器,和當初在阿軟做服務平臺一樣,當時是SCA容器,道理都一樣,如何打通SCA和OSGI與應用服務器,主要涉及的就是ClassLoader的互通。
服務框架組的同學當時做過一個Jetty的內置互通支持版本,其是修改了Jetty的代碼,在啟動過程中植入了外部應用容器的初始化和循環互通。考慮到將來Jetty升級的方便,自己還是從新考慮做一個外部互通的支持版本。(期間波折就不再此說了,大概說一兩點關鍵之處),首先是要啟動外部容器,由于Jetty可以支持LifeCycle的Bean在Jetty容器啟動時優先裝載,因此外部容器就實現接口,在Jetty的Server配置中設置即可。然后需要將兩個容器互通(相互可以引用對方的服務接口),這需要在應用上下文構建的時候相互關聯兩者的classLoader,一種方式在Server的AppDeployer部署過程中植入,一種在指定的AppContext部署中植入。這兩種方式就是Jetty支持的兩種模式,一種配置在etc目錄下的jetty總配置文件中(Server配置中),一種配置在contexts目錄下(這種就是現在比較推崇的片段化部署),我選擇了后一種,因為對我來說不是所有應用都需要支持容器互通的,當前只有TOP這個應用。
容器部署,Jetty真的是太干凈了,首先類似于xml的解析實現沒有(jdk可只有框架接口),log4j沒有,jndi需要另外引入插件支持,lib下的jetty插件按需載入(在start.ini,start.config和啟動腳本中可指定),遙想當年的jboss也應該是很干凈,回顧今天的部署應用的jboss已經被貼了N多膏藥。因此最終要的幾個配置就是:啟動腳本,etc下的jetty.xml(可以指定其他配置),start.config (可以從jetty的jar包中獲取出來自己指定和配置),start.ini(啟動的默認配置),contexts下的應用上下文配置。
容器部署和外部插件遷移雖然不是異步化的工作,但是在異步化以前一定要搞定,否則就無法談到后續的應用遷移,總的來說jetty的模塊化和擴展做的很好,基本上任何步驟都能夠替換和實現新的邏輯。(有需要jetty配置和hsf外部插件支持的同學可以直接找我)
二.TOP管道化體系異步改造
TOP的服務接入層就是由很多個管道切面組成的,流控,安全,業務校驗,路由,協議轉換,響應格式轉換等等,因此TOP自身很適合采用管道化流程體系來構建,同時采用管道化體系構建能夠簡單的隔離業務邏輯,實現服務降級,新功能beta發布。引入異步化概念后,對于開發者其實不需要過多了解,僅僅只需要配置異步化的管道,交由管道框架和容器協作來完成異步化的請求處理。
這里順帶在提一下上一篇中說到的異步化的作用:差別化耦合系統的體系設計,差別化流程中流程處理。異步化不會節省業務事務處理時間(反而會增加),也不一定會提高系統可用性和穩定性(起碼全局上來看,整體復雜度增加,異常波及會被隔離,但是可能發現也較晚),也不一定會節省資源(異步化往往是空間換時間,將業務狀態獨立于處理,提高處理線程的利用率,代價是增加了交互和存儲的成本)。
因此一直困擾TOP的服務分流和隔離可以通過異步化方式得以實現,方式就是將系統處理和業務處理流程隔離,系統處理用少量線程就可以支持大并發請求,同時將后續的業務處理交給業務工作池,而業務工作池的資源分配完全可以通過業務特征設置權重,也可以通過后臺服務質量的反饋來自動調整,最終實現對服務使用者服務差別化,對不同質量的服務提供差別化的流量引入。
同樣和上一篇文章談到的多種模式一樣,改造支持兩種模式,適合不同場景。純粹的異步方式往往需要借助于類似于epoll的io事件驅動模式來徹底高效的分開兩個系統,否則就是采用偽異步。
1. Pull & Check Status & Resume Mode
這種模式對于后端服務的要求較高,首先服務使用需要支持異步方式,其次要求在完成服務后修改任務狀態,而對于依賴方來說,會通過輪詢的方式去獲取結果。流程圖如下,不過第二個是第一個的改進,效果不錯,前者是檢查所有的任務狀態,確定是否完成,放入任務的是前端系統,后者是不需要檢測任務狀態,凡是獲得任務,即表示任務完成,放入任務的是后端服務提供系統。


2. Push & Complete Mode
這種模式下對于后端服務來說可以只提供同步服務或者也支持異步服務,TOP一期改造采用這種模式,后端服務采用同步模式,具體測試結果參看后面的測試部分。

三.測試
場景:
后臺服務執行時間為1秒,no think time,容器為Nginx+Jetty
并發用戶數
|
Jetty連接池線程數量
|
業務線程池線程數量
|
load
|
TPS
|
200(異步)
|
200
|
200
|
0.8
|
180
|
400(異步)
|
200
|
200
|
0.7
|
208
|
200(同步)
|
200
|
200
|
1.16
|
190
|
400(同步)
|
200
|
200
|
1
|
190
|
200(異步)
|
200
|
400
|
0.95
|
195
|
400(異步)
|
200
|
400
|
0.84
|
390
|
200(同步)
|
200
|
400
|
0.82
|
195
|
400(同步)
|
200
|
400
|
0.88
|
195
|
200(同步)
|
400
|
400
|
0.7
|
174
|
400(同步)
|
400
|
400
|
0.85
|
350
|
結論:TPS還是取決于兩個線程池的線程多少,在異步化后系統消耗并沒有明顯增加,容器連接池的放大和業務線程池的放大都可以提高處理效率,同時業務線程池較為輕量,容量翻倍處理基本也是翻倍,容器的線程池放大處理能力會有衰減。
場景:
200并發用戶,調用user.get服務,no think time,容器線程池400,業務線程池500.
容器
|
load
|
TPS
|
Nginx + Jetty
|
6
|
800
|
Jetty
|
8
|
620
|
結論:沒有前段Nginx對于數據緩沖處理,jetty處理效率一般,因此需要假設Jetty作為前段預處理容器(必要的時候也可以作為反向代理)
場景:
200并發用戶,調用user.get服務,no think time,容器線程池200,業務線程池500.
容器
|
并發用戶
|
請求處理模式
|
TPS
|
Apache + Jboss
|
200
|
同步
|
507
|
Nginx + Jetty
|
200
|
同步
|
1060
|
Nginx + Jetty
|
200
|
異步
|
1027
|
Jetty
|
200
|
異步
|
670
|
結論:同步模式下Apache+Jboss與Nginx+Jetty相差很大的級別(load也相差一倍多)。異步模式下的Nginx+Jetty與同步模式相差不大,因此異步帶來的損耗可以忽略。沒有Nginx作為前端整體性能下降很大。
場景:
200并發用戶,no think time,容器線程池200,業務線程池500.
容器
|
請求服務
|
請求處理模式
|
TPS
|
Nginx + Jetty
|
Time.get
|
同步
|
1534
|
Nginx + Jetty
|
Time.get
|
異步
|
1068
|
Nginx + Jetty
|
User.get
|
同步
|
1060
|
Nginx + Jetty
|
User.get
|
異步
|
1027
|
結論:異步占用事務整體時間的比例越高,TPS的影響也越大。
長時間運行測試:
半個多小時Full GC一次(當前內存開到1.5G),線上64位可以開的再大一些,沒有內存泄漏現象。