網絡優化對於App產品的用戶體驗至關重要,與公司的運營和營收息息相關。這里列舉兩個公開的數據:
“Amazon頁面加載延長1秒,一年就會減少16億美金營收。”
在做網絡優化前,我們首先要為網絡通信質量設立一個標尺。
在美團點評,監控團隊開發了基於端到端的客戶端監控平台。這里要先解釋一下“端到端”的含義:是指請求從客戶端發出到服務端響應返回的整個過程。它區別於后台服務監控,是一種從用戶角度觀察到的真實體驗監控。
監控頁面如圖所示:
通過基於命令字的、多維度、實時的監控工具,可以及時發現線上問題。
為了方便發現問題,我們在公司內統一了網絡響應狀態碼的范圍。通過狀態碼的分段范圍,也可以迅速清晰地看到網絡成敗的原因和占比。
有了監控工具后,我們來討論:移動網絡請求過程中,出現了哪些最常見的問題?
首先是網絡不可用的問題。主要由以下幾種原因導致:
- GFW的攔截,原因你懂的。
- DNS的劫持,端口的意外封禁等。
- 偏遠地區網絡基礎設施比較差。
其次是網絡加載時間長。原因包括: * 移動設備出於省電的目的,發出網絡請求前需要先預熱通信芯片。 * 網絡請求需要跨網絡運營商,物理路徑長。 * HTTP請求是基於Socket設計的,請求發起之前會經歷三次握手,斷開時又會進行四次揮手。
最后是HTTP協議的數據安全問題。原因有: * HTTP協議的數據容易被抓包。Post包體數據經過加密能夠避免泄露,但協議中的URL和header部分還是會暴露給抓包軟件。HTTPS也面臨相似的問題。 * 運營商數據惡意篡改嚴重。如下圖中,App的網頁中就被運營商插入了廣告。
面對上述網絡問題,我們首先在HTTP短連請求中進行了一些優化嘗試。
短連方案一、域名合並方案
隨着開發規模逐漸擴大,各業務團隊出於獨立性和穩定性的考慮,紛紛申請了自己的三級域名。App中的API域名越來越多。如下所示: search.api.dianping.com ad.api.dianping.com tuangou.api.dianping.com waimai.api.dianping.com movie.api.dianping.com …
App中域名多了之后,將面臨下面幾個問題: * HTTP請求需要跟不同服務器建立連接。增加了網絡的並發連接數量。 * 每條域名都需要經過DNS服務來解析服務器IP。
如果想將所有的三級域名都合並為一個域名,又會面臨巨大的項目推進難題。因為不同業務團隊當初正是出於獨立性和穩定性的考慮才把域名進行拆分,現在再想把域名合並起來,勢必會遭遇巨大的阻力。
所以我們面臨的是:既要將域名合並,提升網絡連接效率,又不能改造后端業務服務器。經過討論,我們想到了一個折中的方案。
該方案的核心思想在於:保持客戶端業務層代碼編寫的網絡請求與后端業務服務器收到的請求保持一致,請求發出前,在客戶端網絡層對域名收編,請求送入后端,在SLB(Server Load Balancing)中對域名進行還原。
網絡請求發出前,在客戶端的網絡底層將URL中的域名做簡單的替換,我們稱之為“域名收編”。
例如:URL “http://ad.api.dianping.com/command?param1=123" 在網絡底層被修改為 “http://api.dianping.com/ad/command?param1=123" 。
這里,將域名”ad.api.dianping.com”替換成了”api.dianping.com”,而緊跟在域名后的其后的”ad”表示了這是一條與廣告業務有關的域名請求。
依此類推,所有URL的域名都被合並為”api.dianping.com”。子級域名信息被隱藏在了域名后的path中。
被改造的請求被送到網絡后端,在SLB中,擁有與客戶端網絡層相反的一套域名反收編邏輯,稱為“域名還原”。
例如:”http://api.dianping.com/ad/command?param1=123" 在SLB中被還原為 “http://ad.api.dianping.com/command?param1=123" 。 SLB的作用是將請求分發到不同的業務服務器,在經過域名還原之后,請求已經與客戶端業務代碼中原始請求一致了。
該方案具有如下優勢: 1. 域名得到了收編,減少了DNS調用次數,降低了DNS劫持風險。 2. 針對同一域名,可以利用Keep-Alive來復用Http的連接。 3. 客戶端業務層不需要修改代碼,后端業務服務也不需要進行任何修改。
短連方案二、IP直連方案
經過域名合並方案,我們已經將所有的域名都統一成了”api.dianping.com”。針對這唯一的域名,我們可以在客戶端架設自己的DNS服務。
方案很簡單:程序啟動的時候拉取”api.dianping.com”對應的所有的IP列表;對所有IP進行跑馬測試,找到速度最快的IP。后續所有的HTTPS請求都將域名更換為跑馬最快的IP即可。
舉個例子,假如:經過跑馬測試發現域名”api.dianping.com”對應最快的IP是”1.23.456.789”。
URL”http://api.dianping.com/ad/command?param1=123"將被替換為"http://1.23.456.789/ad/command?param1=123"
IP直連方案有下面幾大優勢: 1. 摒棄了系統DNS,減少外界干擾,擺脫DNS劫持困擾。 2. 自建DNS更新時機可以控制。 3. IP列表更換方便。
此外,如果你的App域名沒有經過合並,域名比較多,也建議可以嘗試使用HttpDNS方案。參考:http://www.tuicool.com/articles/7nAJBb 對HTTPS中的證書處理: > HTTPS由於要求證書綁定域名,如果做IP直連方案可能會遇到一些麻煩,這時我們需要對客戶端的HTTPS的域名校驗部分進行改造,參見:http://blog.csdn.net/github_34613936/article/details/51490032 。
經過域名合並加上IP直連方案改造后,HTTP短連的端到端成功率從95%提升到97.5%,網絡延時從1500毫秒降低到了1000毫秒,可謂小投入大產出。
接下來要想進一步提升端到端成功率,就要開始進行長連通道建設了。
提到長連通道建設,首先讓人想到的應該是HTTP/2技術。它具有異步連接多路復用、頭部壓縮、請求響應管線化等眾多優點。
如果查看HTTP/2的拓撲結構,其實非常簡單:
HTTP/2在客戶端與服務器之間建立長連通道,將同一域名的請求都放在長連通道上進行。這種拓撲結構有如下一些缺點: 1. 請求基於DNS,仍將面臨DNS劫持風險。 2. 不同域名的請求需要建立多條連接。 3. 網絡通道難以優化。客戶端與服務器之間是公網鏈路。如果在多地部署服務器,成本消耗又會很大。 4. 業務改造難度大。部署HTTP/2,需要對業務服務器進行改造,而且使用的業務服務器越多,需要改造的成本也越大。 5. 網絡協議可訂制程度小。
與HTTP/2相區別,我們這里推薦另一種代理長連的模式。這種模式的拓撲圖如下:
基本思路為:在客戶端與業務服務器之間架設代理長連服務器,客戶端與代理服務器建立TCP長連通道,客戶端的HTTP請求被轉換為了TCP通道上的二進制數據包。代理服務器負責與業務服務器進行HTTP請求,請求的結果通過長連通道送回客戶端。
與HTTP/2模式對比,代理長連模式具有下面一些優勢: 1. 對DNS無依賴。客戶端與代理服務器之間的長連通道是通過IP建立的,與DNS沒有關系。客戶端的HTTP請求被轉換為二進制數據流送到代理服務器,也不需要進行DNS解析。代理服務器轉發請求到業務服務器時,都處於同一內網,因此可以自己搭建DNS服務,減少對公網DNS服務的依賴。從這個層面上說,代理長連模式天生具有防DNS劫持的能力。 2. 不同域名的請求可以復用同一條長連通道。 3. 通道易優化。與部署業務服務器相比,部署代理長連服務器的代價就小了很多,可以在全國甚至全世界多地部署代理長連服務器。客戶端在選擇代理長連服務器時,可以通過跑馬找到最快的服務器IP進行連接。另一方面,代理服務器與業務服務器之間的網絡通道也可以進行優化,通過架設專線或者租用騰訊雲等方式可以大大提升通道服務質量。 4. 對業務完全透明。客戶端的業務代碼只要接入網絡層的SDK即可,完全不用關心網絡請求使用的是長連通道還是短連通道。代理服務器將客戶端的請求還原為HTTP短連方式送到業務服務器,業務服務器不需要進行任何改造。 5. 網絡協議完全自定義。
在長連通道項目的早期,出於快速推進的目的,同時受限於建設代理長連服務器需要投入大量資金,我們首先接入使用了騰訊的維納斯(WNS)服務(官網地址:https://www.qcloud.com/product/wns )。
WNS服務采用的也是代理長連模式,依托騰訊雲的強大硬件建設,我們使用下來發現端到端成功率可以達到99.6%以上。(PS:這里的提到的端到端成功率與官網宣傳的99.9%不同是由於統計口徑的不同。)
由於騰訊WNS服務是面向公眾的雲服務,服務的客戶遠不止一家,無法完全滿足我們公司技術需求的快速變更,因此還是需要進行自己的長連通道項目建設。
自建長連建設大概可以分為以下幾個周期:
① 中轉服務的開發和部署
作為開發的初級階段,這一時期的任務主要是搭建代理中轉服務器,並架設完整鏈路結構。
② 加密通道的建設
為了保護TCP通道上數據的安全性,客戶端與代理長連服務器之間的二進制通信數據可以利用加密來保障數據安全。
③ 專線建設
在代理長連服務器與后台業務服務器之間建設專線。使用專線,可以大大降低公網環境的干擾,保障服務的穩定性。
④ 自動降級Failover建設
由於客戶端的請求都放在TCP通道上進行,當代理長連服務器需要升級或者由於極端情況發生了故障時,將會造成客戶端的整體網絡服務不可用。為了解決這個問題,我們准備了Failover降級方案。當TCP通道無法建立或者發生故障時,可以使用UDP面向無連接的特性提供另一條請求通道,或者繞過代理長連服務器之間向業務服務器發起HTTP公網請求。本文的后面章節有展示Failover機制的實際效果。
⑤ 多地部署接入點
在全國多地部署代理長連接入點。客戶端與接入點建立長連通道時,可以選擇最快的服務器就近接入,從而大大降低通道連接速度並提升通信質量。 我們在近兩年的網絡優化實踐中,將客戶端的網絡通道服務整理成了一個獨立的SDK,SDK內除了包含了自建的長連通信服務,也包含了WNS等網絡通道。
完整的網絡通道拓撲圖如下所示:
圖中網絡通道SDK包含了三大通信通道: 1. CIP通道:CIP通道就是上文中提到的自建代理長連通道。CIP是China Internet Plus的縮寫,為美團點評集團的注冊英文名稱。App中絕大部分的請求通過CIP通道中的TCP子通道與長連服務器(CIP Connection Server)通信,長連服務器將收到的請求代理轉發到業務服務器(API Server)。由於TCP子通道在一些極端情況下可能會無法工作,我們在CIP通道中額外部署了UDP子通道和HTTP子通道,其中HTTP子通道通過公網繞過長連服務器與業務服務器進行直接請求。CIP通道的平均端到端成功率目前已達99.7%,耗時平均在350毫秒左右。 2. WNS通道:出於災備的需要,騰訊的WNS目前仍被包含在網絡通道SDK中。當極端情況發生,CIP通道不可用時,WNS通道還可以作為備用的長連替代方案。 3. HTTP通道:此處的HTTP通道是在公網直接請求API Server的網絡通道。出於長連通道重要性的考慮,上傳和下載大數據包的請求如果放在長連上進行都有可能導致長連通道的擁堵,因此我們將CDN訪問、文件上傳和頻繁的日志上報等放在公網利用HTTP短連進行請求,同時也減輕代理長連服務器的負擔。
推送方案:在網絡通道拓撲圖的右上角,有個Push Server。它是考慮到TCP通道的雙工特性,為網絡通道SDK提供推送的能力。利用通知推送,可以在服務器數據發生變化時及時通知客戶端。推送方案可以替換掉代碼中常見的耗時低效的輪詢方案。
案例展示
下圖展示了某開機接口在接入長連后的端到端成功率對比:
上圖中黑色曲線是某開機接口在短連通道下的成功率曲線。成功率平均只有81%,抖動的特別劇烈,說明網絡服務穩定性不夠。
藍色曲線是同一接口在長連通道下的成功率曲線。成功率平均已達到99%,抖動大幅減小。
成功延時對比圖:
上圖中展示了同樣情況下的成功延時曲線。藍色線為長連延時曲線,黑色線為短連延時曲線。
接下來我們看Failover的效果展示圖。
下圖展示了2015年的一次長連服務器故障。
當時Android客戶端采用了Failover方案,在長連不可用時Failover到短鏈或者UDP通道上。與未采用Failover方案的iOS客戶端相比,Failover機制在維持網絡整體可用性方面體現出了非常大的優勢。
網絡配置系統
網絡通道SDK包含了CIP|WNS|HTTP三大通道,不同的通道具有各自的優缺點,控制各請求選擇合適的網絡通道成了迫在眉睫的重要課題。
為此我們開發了網絡配置系統,通過下發指令,調整App中網絡通道SDK中的通道選擇策略,可以控制不同的API請求動態切換網絡通道。
下圖是某接口的線上通道切換示意圖:
圖中展示了某接口切換WNS通道的過程。圖中的黑色線代表短連通道下的請求數量曲線,藍色線代表WNS通道下的請求數量曲線。通過線上控制系統下發了通道切換指令后,絕大部分的短連請求在5分鍾之內被切換成了WNS通道請求。
在客戶端開發過程中,我們發現: * 長連通道建立越早,成功率越高。長連通道越早建立,越多的請求能夠在長連通道上進行。特別是當App剛打開時,數量眾多的請求同時需要發出。面對這種情況,我們采取的策略是首先建立長連通道,將眾多請求放入等待發送隊列中,待長連通道建立完畢后再將等待隊列中的請求放在長連通道上依次送出。采用這種策略后,我們發現啟動時的接口成功率平均提升了1.4%,延時平均降低了160毫秒。 * TCP數據包越大,傳輸時間越長。如果長連通道未采用類似HTTP/2中的數據切片技術,大的數據包非常容易導致長連通道的堵塞。 * 底層SDK上線新功能一定要有線上降級手段。當新功能上線了發生故障時,可以通過開關或參數控制,或是采用ABTest方式等進行降級,防止故障擴大化。 * iOS和Android系統網絡庫存在很多默認行為。例如系統網絡庫會在內部處理網絡重定向,再比如請求頭中如果沒有填寫Accept-Encoding或Content-Type等字段,系統網絡庫會自動填寫默認值。 * 一個容易忽視的地方:HTTP的請求頭鍵值對中的的鍵是允許相同和重復的。例如下圖所示的”Set-Cookie”字段就是包含了多組的相同的鍵名稱。與之類似的還有”Cookie”字段。在長連通信中,如果對header中的鍵值對用不加處理的字典方式保存和傳輸,就會造成數據的丟失。
對於正在成長中的創業公司,我們有如下改善網絡狀況的建議: * 收攏網絡底層。隨着公司的成長,開發團隊越來越多,不可避免的將會引入越來越多的網絡庫。網絡庫多了之后,再對網絡請求進行集中管理就非常困難了。我們的建議是在網絡庫與業務代碼之間架設自己的網絡層,業務的網絡請求全部經過網絡層代碼進行請求。這樣未來進行底層網絡庫的更換,或者網絡通道的優化將變得容易很多。 * 使用網絡監控。引入網絡監控機制,發現網絡問題。這里推薦我公司開發的開源的Cat監控系統。Cat開源地址為http://github.com/dianping/cat 。 * 嘗試進行短連優化。前文中提到的域名合並和IP直連方案都是簡單有效的手段。 * 可以嘗試HTTP/2或騰訊WNS長連服務。
周輝,美團點評資深移動架構師。所在團隊負責整個集團客戶端網絡通道、監控、推送等底層SDK的開發和維護工作。