作者:shockcao
mars 是微信官方使用 C++ 編寫的業務性無關、平台性無關的終端基礎元件,目前在微信 Android、iOS、Windows、Mac、Windows Phone 等多個平台中使用,并正在籌備開源,它主要包含以下幾個獨立的部分:
1、COMM:基礎庫,包括 socket、線程、消息隊列、協程等基礎工具;
2、XLOG:通用日志子產品,充分考慮移動終端的特點,提供高性能、高可用、安全性、容錯性的日志功能;(詳情點選:高性能日志子產品xlog )
3、SDT:網絡診斷子產品;
4、STN:信令傳輸網絡子產品,負責終端與伺服器的小資料信令通道。包含了微信終端在移動網絡上的大量優化經驗與成果,經曆了微信海量使用者的考驗。
本篇文章将為大家介紹 STN(信令傳輸網絡子產品),由于 STN 的複雜性,該子產品将被分解為多個篇章進行介紹,本文主要内容為微信中關于讀寫逾時的思考與設計。
微信信令通信主要使用 TCP/IP 協定,資料經過應用層、傳輸層、網絡層、鍊路層(見圖1)。其中,鍊路層與傳輸層,協定提供了逾時重傳的機制。

圖1 使用 TCP/IP 協定
在鍊路層,一般使用混合自動重傳請求(即 HARQ)。HARQ 是一種結合 FEC(前饋式錯誤修正)與 ARQ(自動重傳請求)的技術,原理如圖2所示。
圖2 HARQ 原理
通過使用确認和逾時這兩個機制,鍊路層在不可靠實體裝置的基礎上實作可靠的資訊傳輸。這個方案需要手機和 RNC 都支援,目前在 EDGE、HSDPA、HSUPA、UMTS和 LTE 上都已實作支援。
傳輸層(即 TCP 層)提供可靠的傳輸,然而,TCP 層依賴的鍊路本身是不可靠的,TCP 是如何在不可靠的環境中提供可靠服務的呢?答案是逾時和重傳。TCP 在發送資料時設定一個定時器,當定時器溢出還沒有收到 ACK,則重傳該資料。是以,逾時與重傳的關鍵之處在于如何決定定時器間隔與重傳頻率。
傳統 Unix 實作中,定時器的間隔取決于資料的往返時間(即 RTT),根據 RTT 進行一定的計算得到重傳逾時間隔(即 RTO)。由于網絡路由、流量等的變化,RTT 是經常發生變化的,RTT 的測量也極為複雜(平滑算法、Karn 算法、Jacbson 算法等)。在《TCP/IP詳解》中,實際測量的重傳機制如圖3所示,重傳的時間間隔,取整後分别為1、3、6、12、24、48和多個64秒。這個倍乘的關系被稱為“指數退避”。
圖3 實際測量的重傳機制
在移動終端中,RTO 的設計以及重試頻率的設計是否與傳統實作一緻呢?對此我們進行了實測,實測資料如下:
圖4所示為OPPO手機TCP逾時重傳的間隔,依次為[ 0.25s,0.5s,1s,2s,4s,8s,16s,32s,64s,64s,64s …]:
圖4 OPPO 手機 TCP 逾時重傳間隔
而 SamSung 中 TCP 逾時重傳的間隔依次為[0.42s, 0.9s, 1.8s, 3.7s, 7.5s, 15s, 30s, 60s, 120s, 120s …],見圖5。
圖5 三星手機 TCP 逾時重傳間隔
經過多次實際測試我們可以看出雖然由于不同廠商的 Android 系統實作,RTO 的值可能會有不同的設定,但都基本符合“指數退避”原則。
接下來再看 iOS 系統中,TCP RTO 的實驗資料,圖6所示為實驗中第一次的資料[ 1s,1s,1s,2s,4.5s,9s,13.5s,26s,26s … ]。
圖6 iOS 系統 TCP RTO 第一次實驗資料
上面的資料看起來并不完全符合指數退避,開始階段的重試會較為頻繁且 RTO 最終固定在 26s 這一較小的值上。
進行第二次測試後發現資料有了新的變化[1s,1s,1s,2s,3.5s,8.5s,12.5s,24s,24s …],如圖7所示。
圖7 iOS 系統 TCP RTO 第二次實驗資料
RTO 終值由26秒縮減至24秒,最終經過多次測試并未發現 iOS 中 TCP RTO 的規律,但可以看出 iOS 确實采用了較為激進的逾時時間設定,對重試更為積極。
通過上述的調研與實驗,可以發現在 TCP/IP 中,協定棧已經幫助我們進行了逾時與重傳的控制。并且在 Android、iOS 的移動作業系統中進行了優化,使用了更為積極的政策,以适應移動網絡不穩定的特征。
那是否意味着我們的應用層已經不需要逾時與重傳的控制了呢?其實不然。在鍊路層,HARQ 提供的是節點之間每一資料幀的可靠傳輸;在傳輸層,TCP 逾時重傳機制提供的是端與端之間每個 TCP 資料包的可靠傳輸;同理,在微信所處的應用層中,我們仍然需要提供以“請求”為粒度的可靠傳輸。
那麼,應用層的逾時重傳機制應該提供怎樣的服務呢?
首先,我們來看一下應用層重傳的做法。在應用層中,重傳的做法是:斷掉目前連接配接,重建立立連接配接并發送請求。這種重傳方式能帶來怎樣的作用呢?回顧 TCP 層的逾時重傳機制可以發現,當發生逾時重傳時,重傳的間隔以“指數退避”的規律急劇上升。在 Android 系統中,直到16分鐘,TCP 才确認失敗;在 iOS 系統中,直到1分半到3分半之間,TCP 才确認失敗。這些數值在大部分應用中都是不為“使用者體驗”所接受的。是以,應用層的逾時重傳的目标首先應是:
在使用者體驗的接受範圍内,盡可能地提高成功率
盡可能地增加成功率,是否意味着在有限的時間内,做盡可能多的重試呢?其實不然。當網絡為高延遲/低速率的網絡時,較快的應用層重傳會導緻“請求”在這種網絡下很難成功。是以,應用層逾時重傳的目标二:
保障弱網絡下的可用性
TCP連接配接是有固定實體線路的連接配接,當已 Connect 的線路中,如果中間裝置出現較大波動或嚴重擁塞,即使在限定時間内該請求能成功,但帶來的卻是性能低下,反應遲鈍的使用者體驗。通過應用層重連,期待的目标三是:
具有網絡敏感性,快速的發現新的鍊路
我們總結應用層逾時重傳,可以帶來以下作用:
1、減少無效等待時間,增加重試次數:當 TCP 層的重傳間隔已經太大的時候,斷連重連,使得 TCP 層保持積極的重連間隔,提高成功率;
2、切換鍊路:當鍊路存在較大波動或嚴重擁塞時,通過更換連接配接(一般會順帶更換IP&Port)獲得更好的性能。
在TCP層的逾時重傳設計中,逾時間隔取決于RTT,RTT即TCP包往返的時間。同理,在微信的早期設計中,我們分析應用層“請求”的往返時間,将其RTT分解為:
請求發送耗時 - 類比TCP包傳輸耗時;
響應信令接收耗時 - 類比ACK傳輸耗時;
伺服器處理請求耗時 - TCP接收端接收和處理資料包的時間相對固定,而微信伺服器由于信令所屬業務的不同,邏輯處理的耗時會差異明顯,是以無法類比;
等待耗時 - 受應用中請求并發數影響。
是以,我們提出了應用層的總讀寫逾時如圖8所示,最低網速根據不同的網絡取不同的值。
圖8 應用層的總讀寫逾時
在實際的使用過程中,我們發現這僅僅是一個可用的方案,并不是一個高性能的解決方案:逾時時長的設定使用了差網絡下、完整的完成單次信令互動的時間估值。這使得逾時時間過長,在網絡波動或擁塞時,無法敏感地發現問題并重試。進一步分析可以發現,我們無法預知伺服器回包的大小,是以使用了最大的回包進行估算(微信中目前最大回包可到 128KB)。然而,TCP 傳輸中當發送資料大于 MSS 時,資料将被分段傳輸,分段到達接收端後重新組合。如果伺服器的回包較大,用戶端可能會收到多個資料段。是以,我們可以對首個資料分段的到達時間進行預期,進而提出首包逾時,如圖9所示。
圖9 首包逾時計算
首包逾時縮短了發現問題的周期,但是我們發現如果首個資料分段按時到達,而後續資料包丢失的情況下,仍然要等待整個讀寫逾時才能發現問題。為此我們引入了包包逾時,即兩個資料分段之間的逾時時間。因為包包逾時在首包逾時之後,這個階段已經确認伺服器收到了請求,且完成了請求的處理,是以不需要計算等待耗時、請求傳輸耗時、伺服器處理耗時,隻需要估算網絡的 RTT。
在目前方案中,使用了不同網絡下的固定 RTT。由于有了“首包已收到”的上下文,使得包包逾時的間隔大大縮短,進而提高了對網絡突然波動、擁塞、突發故障的敏感性,使得應用獲得較高的性能。
在上述的方案中,總讀寫逾時、首包逾時都使用了一些估值,使得這兩個逾時是較大的值。假如我們能獲得實時的動态網速等,我們能獲得更好的逾時機制,如圖10所示。
圖10 實時動态網速下的逾時估算
但是,理想是豐滿的,現實是殘酷的:
動态網速需要通過工具方法測定,實時性要求高,并且要考慮網絡波動的影響;
伺服器動态耗時需要伺服器下發不同業務信令的處理耗時;
真實回包大小則隻能靠伺服器通知。
上述的三種途徑對用戶端和伺服器都是巨大的流量、性能的消耗,是以動态化這些變量看起來并不可行。
是以,這裡需要換個角度思考動态優化,手機的網絡狀況可以大概地歸為優質、正常、差三種情況,針對三種網絡狀況進行不同程度的調整,也是動态優化的一種手段。這裡選擇優質網絡狀況進行分析:
如何判定網絡狀況好?網速快、穩定,網絡子產品中與之等價的是能夠短時間完成信令收發,并且能夠連續長時間地完成短時間内信令收發。
即使出現網絡波動,也可以預期會很快恢複。
圖11 優質網絡狀況優化
根據對網絡狀況好的分析,我們可以做出這樣的優化(如圖11所示):
将用戶端網絡環境區分為優良(Excellent)、評估(Evaluating)兩種狀态;
網速快、穩定就是條件1,信令失敗或網絡類型切換是條件2。
進入Exc狀态後,就縮短信令收發的預期,即減小首包逾時時間,這樣做的原因是我們認為使用者的網絡狀況好,可以設定較短的逾時時間,當遇到網絡波動時預期它能夠快速恢複,是以可以盡快逾時然後進行重試,進而改善使用者體驗。
雖然 TCP/IP 協定棧中的鍊路層、傳輸層都已經提供了逾時重傳,保障了傳輸的可靠性。但應用層有着不同的可靠性需求,進而需要額外的應用層逾時重傳機制來保障應用的高性能、高可用。應用層逾時重傳的設計目标,筆者從自身經驗出發,總結為:
在使用者體驗的接受範圍内,盡可能地提高成功率;
保障弱網絡下的可用性;
具有網絡敏感性,快速地發現新的鍊路。
依從這些目标,mars STN 的逾時重傳機制在使用中不斷的精細化演進,使用了包含總讀寫逾時、首包逾時、包包逾時、動态逾時等多種方案的綜合。即使如此,STN 的逾時重傳機制也有着不少的缺點與局限性,例如相對适用于小資料傳輸的信令通道、局限于一來一回的通信模式等。mars STN 也會不斷發現新的問題持續演進,并且所有的演進都将在微信的海量使用者中進行驗證。同時也期待随着 mars STN 的開源,能收獲更多、更廣的經驗交流、問題回報、新想法的碰撞等。
本文來源于:WeMobileDev 微信公衆号