攜程App的網絡性能優化實踐
原文 http://www.infoq.com/cn/articles/how-ctrip-improves-app-networking-performance
編者按:在4月23日~25日舉行的QCon全球軟件開發大會(北京站)上,攜程無線開發總監 陳浩然 分享了《移動開發網絡性能優化實踐》,總結了攜程在App網絡性能優化方面的一些實踐經驗。在2014年接手攜程無線App的框架和基礎研發工作之后,陳 浩然面對的首要工作就是App客戶端性能優化,尤其是網絡服務性能,這是所有App優化工作的重中之重。以下為正文。
首先介紹一下攜程App的網絡服務架構。下圖是攜程App的架構設計(典型的層次化設計):
由于攜程業務眾多,開發資源導致無法全部使用Native來實現業務邏輯,因此有相當一部分頻道是基于Hybrid來實現。網絡通訊屬于基礎設施層的一部分,為整個App提供統一的網絡服務。
- Native端的網絡服務
Native模塊是核心業務模塊,絕大部分Native模塊基于TCP連接實現網絡服務,而非常見的HTTP Restful API那種HTTP連接實現,少量輕量級服務使用HTTP接口作為補充。
TCP連接網絡服務模塊使用了長連接+短連接機制,即有一個長連接池保持一定數目長連接,用于減少每次服務額外的連接,服務完成后會將該連接放回長連接池,同時保持連接狀態;短連接作為補充,每次服務完成后便會主動關閉連接。
TCP網絡服務的Payload使用的是自定義的數據及序列化協議;HTTP服務的Payload比較簡單,就是常用的JSON格式。
- Hybrid端的網絡服務
Hybrid模塊由于是在WebView中展示本地或者直連的Hybrid頁面,發起的網絡服務都是通過系統WebView中的相應HTTP請求實現的。只有少量業務以Hybrid接口形式的TCP通道來完成網絡服務。
下圖是網絡服務的部署架構圖:
攜程App所有網絡服務,無論是TCP還是HTTP都會先連接到一個Gateway服務器。如果是TCP服務,會先連接上TCP Gateway,TCP Gateway會負責將請求通過HTTP轉發到后端的SOA服務接口。HTTP Gateway的原理與之類似。TCP Gateway和HTTP Gateway的區別僅僅在客戶端到服務端的連接方式不同而已。
要發現常見網絡性能問題,先來看看一個網絡服務做了哪些事情:
1.DNS Lookup
2.TCP Handshake
3.TLS Handshake
4.TCP/HTTP Request/Response
首先會是DNS解析,然后TCP連接握手,TLS連接握手(如果有的話),連接成功后再發送TCP或者HTTP請求以及收到響應。如果我們能夠將 這些過程逐一梳理并確保不會存在明顯的性能問題,那么基本可以確保獲得不錯的網絡性能。網絡服務里有一個重要的性能標準,即RTT(Round-Trip Time),往返時延,它表示從發送端發送數據開始,到發送端收到來自接收端的確認(接收端收到數據后便立即發送確認)所間隔的時間。理想情況下我們可以 假設4G網絡RTT為100ms,3G網絡RTT為200ms,很容易就能計算出你的App網絡服務耗時的下限,當然還要加上服務器處理時間。
常見的網絡性能問題有如下幾種:
- 問題一:DNS問題
DNS出問題的概率其實比大家感覺的要大,首先是DNS被劫持或者失效,2015年初業內比較知名的就有Apple內部DNS問題導致App Store、iTunes Connect賬戶無法登錄;京東因為DNS問題導致服務停擺。攜程在去年11月也遇到過DNS問題,主域名被國外服務商誤列入黑名單,導致主站和H5等 所有站點無法訪問,但是App客戶端的Native服務都正常,原因后面介紹。另一個常見問題就是DNS解析慢或者失敗,例如國內中國運營商網絡的DNS 就很慢,一次DNS查詢的耗時甚至都能趕上一次連接的耗時,中國移動的DNS服務相當不靠譜,錯誤率極高(據說出錯率在60%以上),2G網絡情況下 DNS解析失敗是很正常的情況。因此如果直接使用DNS,對于首次網絡服務請求耗時和整體服務成功率都有非常大的影響。
- 問題二:TCP連接問題
DNS成功后拿到IP,便可以發起TCP連接。HTTP協議的網絡層也是TCP連接,因此TCP連接的成功和耗時也成為網絡性能的一個因素。我們 發現常見的問題有TCP端口被封,或者TCP連接超時時長問題。端口被封,最終無論如何都連不上;連接超時時長過短,在低速網絡上可能總是無法成功連接; 連接超時過長,又有可能導致用戶長時間等待,用戶體驗差,有時候盡快失敗重新發起一次連接會很快,這也是移動網絡帶寬不穩定情況下的一個常見情況。
- 問題三:Write/Read問題
DNS Lookup和TCP連接成功后,就會開始發送Request,服務端處理后返回Response,如果是HTTP連接,業內大部分App是使用第三方 SDK或者系統提供的API來實現,那么只能設置些緩存策略和超時時間。iOS上的NSURLConnection超時時間在不同版本上還有不同的定義; 如果是直接使用TCP連接實現網絡服務,就要自己對讀寫超時時間負責,與網絡連接超時時長參數類似,太小了在低速網絡很容易讀寫失敗,太大了又可能影響用 戶體驗,因此需要非常小心地處理。我們還遇到另一個問題,某些酒店Wi-Fi對使用非80、8080和443等常見HTTP端口的服務進行了限制,即使發 送Request是正常的,服務端能夠正常收到,但是Response卻被酒店網絡proxy或者防火墻攔截,客戶端最終會等待讀取超時。
- 問題四:傳輸Payload過大
傳的多就傳的慢,如果沒做過特別優化,傳輸Payload可能會比所需要的大很多,那么對于整體網絡服務耗時影響非常大。
- 問題五:復雜的國內外網絡情況
國內運營商互聯和海外訪問國內帶寬低傳輸慢的問題也讓我們非常頭疼。
看下攜程App用戶的網絡類型分布:
Wi-Fi用戶占比已超過60%,4G用戶量正接近3G用戶量,2G用戶在逐步減少,用戶的網絡是越來越好的。4G/3G/2G網絡的帶寬和延遲差別很大,而帶寬和延遲是網絡性能的一個重要指標:
針對攜程App用戶的網絡帶寬和延遲,我們采樣了海內外各8個城市的數據(有趣的是東京和香港的用戶網絡質量真不錯):
針對上面這些問題,在網絡復雜狀況無能為力的情況下,我們就針對問題逐一優化,達到我們的目標:連得上、連得塊、傳輸時間短。
· 優化實踐一:優化DNS解析和緩存
由于我們的App網絡服務主要基于TCP連接,為了將DNS時間降至最低,我們內置了Server IP列表,該列表可以從服務端下發更新。每次App啟動后首先從Server IP列表中取一個IP地址進行TCP連接,同時DNS解析會并行進行,DNS成功后,會返回最適合用戶網絡的Server IP,那么這個Server IP會被加入到Server IP列表中被優先使用。
Server IP列表是有權重機制的,DNS解析返回的IP很明顯具有最高的權重,每次從Server IP列表中取IP會取權重最高的IP。列表中IP權重也是動態更新的,根據連接或者服務的成功失敗來動態調整,這樣即使DNS解析失敗,用戶在使用一段時 間后也會選取到適合的Server IP。
· 優化實踐二:網絡質量檢測(根據網絡質量來改變策略)
針對網絡連接和讀寫操作的超時時間,我們提出了網絡質量檢測機制。目前做到的是根據用戶是在2G/3G/4G/Wi-Fi的網絡環境來設置不同的 超時參數,以及網絡服務的并發數量。2G/3G/4G網絡環境對并發TCP連接的數量是有限制的,因此網絡服務重要參數能夠根據網絡質量狀況來動態設定對 性能和體驗都非常重要。
不過目前攜程App網絡質量檢測的粒度還比較粗,我們正在做的優化就是能夠測算到用戶當前的網絡RTT,根據RTT值來設置參數,那會更加準確。
· 優化實踐三:提供網絡服務優先級和依賴機制
由于網絡對并發TCP連接的限制,就需要能夠控制不必要的網絡服務數量,因此我們在通訊模塊中加入了網絡服務優先級和依賴機制。發送一個網絡服 務,可以設置它的優先級,高優先級的服務優先使用長連接, 低優先級的就是用短連接。長連接由于是從長連接池中取到的TCP連接,因此節省了TCP連接時間。
網絡服務依賴機制是指可以設置數個服務的依賴關系,即主從服務。假設一個App頁面要發多個服務,主服務成功的情況下,才去發子服務,如果主服務失敗了,上子服務就沒必要再關心成功或者失敗,會直接被取消。如果主服務成功了,那么子服務就會自動觸發。
· 優化實踐四:提供網絡服務重發機制
移動網絡是非常不穩定的,如果一次網絡服務失敗,就立刻反饋給用戶你失敗了,體驗并不友好。我們提供了網絡服務重發機制,即當網絡服務在連接失 敗、寫Request失敗、讀Response失敗時自動重發服務;長連接失敗時就用短連接來做重發補償,短連接服務失敗時當然還是用短連接來補償。這樣 的機制大大增加了用戶體驗到的服務成功概率。
當然不是所有網絡服務都是可以被重發的,例如當下訂單服務在讀取Response失敗時,就不能重發,因為下單請求可能已經到達服務器,此時重發服務可能會造成重復訂單,所以我們給也添加了重發服務開關,可以自行控制是否需要。
· 優化實踐五:減少數據傳輸量
我們優化了TCP服務Payload數據的格式和序列化/反序列化算法,從自定義格式轉換到了Protocol Buffer數據格式,效果非常明顯。圖片格式優化在業界已有成熟的方案,例如非死book App中使用到的WebP圖片格式,已經在手機淘寶等App中使用。
· 優化實踐六:優化海外網絡性能
海外網絡性能的優化手段主要是通過花錢升級基礎設施,例如CDN加速,提高帶寬,實現動靜資源分離,對于App中的Hybrid模塊優化效果是非常明顯的。
經過上面優化手段,攜程App的網絡性能從優化之初的V5.9版本到現在V6.4版本,服務成功率已經有了大幅提升,核心服務成功率都在99%以 上。注意這是客戶端采集的成功率,即用戶感知到的網絡服務成功率,失敗量也包含了服務端的錯誤。網絡服務平均耗時下降了150-200ms。我們的目標是 除2G網絡外,核心業務的網絡服務成功率都能夠達到三個九。
數據格式優化的效果尤其明顯,采用新的Protocol Buffer數據格式+Gzip壓縮后的Payload大小降低了15%-45%。數據序列化耗時下降了80%-90%。
經歷了這半年的網絡性能優化,體會最深的就是Logging基礎設施的重要性。如果我們沒有完整端到端監控和統計的能力,性能優化只能是盲人摸 象。Logging基礎設施需要包括客戶端埋點采集、服務端T+0處理、后期分析、Portal展示、自動告警等多種功能,這不是單純的客戶端框架團隊可 以完成的,需要公司多個部門的合作共同完成。
下圖是我們公司的網絡實時監控Portal,基于Elastic Search開發,能夠實時監控所有的網絡服務,包括多種緯度,可以跟蹤到單個目標用戶的所有網絡請求信息。
用戶的性能數據都被噴到Haddop和Hive大數據平臺,我們可以輕松制定并分析網絡性能KPI,例如服務成功率、服務耗時、連接成功率和連接 耗時等,我們做到了在時間、網絡類型、城市、長短連接、服務號等多緯度的分析。下圖是我們的網絡性能KPI Portal,可以查看任一服務的成功率,服務耗時、操作系統、版本等各種信息,對于某個服務的性能分析非常有用。
最后看看業界有哪些針對網絡性能的新技術方向,最重要的就是HTTP/2.0。它的前身是SPDY協議,目前已經被Google廢棄,專注于 HTTP/2.0的研發。HTTP/2.0提供了很多誘人的特性(多路復用、請求優化、支持服務端推送、壓縮HTTP Header、強制SSL傳輸、對服務端程序透明等)。國內很多App包括美團、淘寶、支付寶都已經在使用SPDY協議,我們也在積極測試,如果性能不 錯,或許未來會采用這種更先進的網絡服務協議。