1. 程式人生 > >微信終端跨平臺元件 Mars 在行動網路的探索和實踐

微信終端跨平臺元件 Mars 在行動網路的探索和實踐

在 IM 方面,弱網路一直是橫亙在應用開發者面前的一大問題,微信終端跨平臺網路基礎元件 Mars 團隊基於微信業務需求,針對網路層進行了大量的優化工作,以解決國內在複雜行動網路情況下的網路連線問題,並經歷了微信 5 億使用者的檢驗。本文作者重點介紹了針對行動網路,Mars 做了哪些事情,解決了哪些問題,希望能夠給正在探索網路優化的開發者帶來啟發,也可以通過了解 Mars 來看其是否適合自己的業務。

行動網路概述

對於 TCP 網路請求來說,最重要的莫過於延遲和成功率。在兩者之中我們更為關心成功率,但其實可以認為當延遲高到一定程度也就導致了失敗。而影響 TCP 延遲的最主要的兩點是 IP 層以下的丟包和誤碼,相比有線乙太網絡和光纖,行動網路在這兩方面更為嚴重,可以先來看兩組資料,如圖 1 所示。

圖 1 行動網路和有線網丟包對比

從圖 1 很容易看出,行動網路的丟包率是高於有線網路的,同時從時間分佈上也能看出,接入網路的裝置越多,丟包越嚴重。

如果說丟包率方面行動網路雖然高於有線網路,但也沒有非常大的差距,那麼誤位元速率(Bit Error Rate)的差距就比較明顯了,如圖 2 所示。

圖 2 行動網路和其他網路誤碼對比

如果想對上面的差異性追根溯源的話,就需要來看核心網路的架構,以 LTE 為例,見圖 3。

圖 3 LTE 核心網路架構圖

行動網路整個傳輸過程只有手機至 RAN(無線接入網路)是無線的,這個過程極不穩定,會受到空氣微塵、溫度、溼度、障礙物、基站擁擠、訊號盲點等客觀因素影響。還有使用者高速移動等主觀因素也會導致較高的丟包率和誤位元速率。同時,核心網路的設計也將直接影響到網路延遲,在圖 3 中:

  • ①的耗時稱為控制面延遲,耗時<100ms;
  • ②的耗時稱為使用者面延遲,耗時<5ms;
  • ③的耗時稱為核心網路延遲,耗時 30-100ms;
  • ④的耗時稱為網際網路路由延遲,時間不定。

這裡需要特別注意的是控制面延遲,高時可達 100ms,低時可能為 0。至於為什麼浮動如此之大,這就又和通訊協議的 RRC 狀態有關。簡單描述下即為移動裝置為了省電,在使用手機網路的情況下,如果持續一段時間內沒有收發資料的話,網路模組會進入休眠狀態,此時只傳輸控制信令。如果在休眠態下需要收發資料,就必須先通過控制信令到活躍態下,如圖 4 所示。

圖 4 RRC 狀態圖

排除丟包誤碼以及控制面延遲,美國最大的移動運營商 AT&T 為不同的網路核心網路延遲給出了期望值(如圖 5 所示),這些值在很大程度上也代表了行業水平。

圖 5 核心網路延遲期望值

Mars

限於篇幅原因,如果將 Mars 的每一部分做具體描述,幾乎不大可能,但是我們這裡可以只看網路最核心的部分。

如圖 6 所示,將一個網路模組只保留 socket 的邏輯。

圖 6 網路請求的基本模型

根據 AT&T 的資料可以估算下總耗時:100ms(DNS) + 100ms(連線) + 50ms(傳送) +50ms(接收) = 300ms。但是再加上丟包誤碼以及控制面延遲,可能有時候能到 400ms+。

針對這個最簡單的邏輯,我們一個階段一個階段地進行優化。

RRC

首先是否有辦法將 RRC 切換的時間儘量避免掉?既然長時間不收發資料會進入 IDLE 狀態,那麼如果可以預知使用者將要使用網路前,主動先發下資料使 RRC 進入 Active 狀態,真正用網路時也就可以避免掉控制面延遲了。這裡需要注意:

  • 干擾 RRC 是把雙刃劍,不鼓勵用;
  • 精準預測到需要使用網路時再用;
  • 實現使用 UDP 可以減小伺服器壓力。

RRC 如果可以優化,那麼在連線之前最後一步準備工作——DNS 呢?

DNS

但凡使用域名來給使用者提供服務的業務,都無法避免在網際網路環境中遭遇到各種域名劫持、使用者跨網訪問慢等問題。事實上當前 DNS 的一些缺點(如域名劫持、解析轉發、更新緩慢等)也一直被業界詬病。拋開這些問題不談,在耗時方面,如果不對解析到的地址進行快取,每次使用時都要再次解析,而且每次只能解析單個域名。

2013 年前後,HTTPDNS 概念開始興起,基本克服了現有 DNS 的缺點,且支援批量解析,極大地提高了網路訪問速度。微信的 NewDNS 和 HTTPDNS 的實現原理類似,是從 2012 年中就開始建設的一個服務。從 NewDNS 的回包中擷取一段:

<domain name="your.domain1" timeout="1800">
         <ip>111.111.11.111</ip>
         <ip>111.111.11.112</ip>
    </domain>
    <domain name="your.domain2" timeout="1800">
        <ip>111.111.11.113</ip>
        <ip>111.111.11.114</ip>
    </domain>

在安全上,通過時間戳和簽名機制,可以做到防重放防篡改。但考慮到 NewDNS 和微信的業務結合過於緊密,且當前的 HTTPDNS 機制已經很成熟,Mars 開源並沒有將 NewDNS 的實現包括在內,不過也預留了回撥介面以供大家使用第三方的 HTTPDNS 服務。

連線

如果說 RRC 和 DNS 都可以把耗時優化到 0,接下來的流程在 TCP 層可控制的就不多了。在連線方式上,如果只用一個 IP 連線失敗就認為徹底失敗,大概是屬於最原始的方案了。一般會使用併發連線或序列連線,進而提高連通率,但兩者都有不容忽視的缺點:

  • 併發連線——網路資源競爭、伺服器負載、最快可用;
  • 序列連線——資源佔用少、無伺服器負載問題、超時選擇困難、最慢可用。

為了實現同時滿足高效能、高可用、低負載,在併發連線和序列連線的基礎上,Mars 提出了複合連線,可見圖 7。

圖 7 複合連線

對比序列連線與並行連線,複合連線有以下特點:

  • 常規情況下,伺服器負載與序列連線策略相同,實現了低負載的目標;
  • 異常情況下,每 4s 發起新(IP,Port)組合的 connect 呼叫,使得應用可以快速地查詢可用 IP&Port,實現高效能的目標;
  • 在超時時間的選擇上,複合方式的“併發”已經實現了高效能、低負載的目標,因此可以相對寬鬆,以保障高可用為重。

TCP 的大多數實現中,若主動 connect 方沒有收到 SYN 的迴應,後面的重試間隔會以“類指數退避”的方式增加。實測顯示,Android 超時間隔依次為(1,2,4,8,16,32),iOS 超時間隔依次為(1,1,1,1,1,2,4,8,16,32)。因此,期望通過 TCP 的自有超時機制來發現連線失敗,時間之長是不能忍受的。在綜合了幾個平臺的超時間隔之後選擇了 10s。

傳送

連線上肯定是用來收發資料的,但傳送也並不只是把資料放到系統 Buffer 裡這麼簡單。

我們知道 TCP/IP 網路協議棧分為應用層、傳輸層、網路層和鏈路層。在通訊過程中,應用層協議把我們真正關心的資料放進去,其他協議層的也都會加上一個資料頭部,最後發出的資料包結構如圖 8 所示。

圖 8 TCP 資料包結構

當傳送方產生的資料很慢,或接收端處理資料很慢,或二者兼有,就會使單次傳送資料的有效載荷很小。極端情況甚至只有 1 位元組的有效資料,稱之為糊塗視窗綜合症。針對傳送端的解決辦法是 Nagle 演算法,針對接收端的解決辦法是 Clark 和延遲 ACK。因為我們是傳送端,這裡只關注 Nagle 演算法:

  • 如果包長度達到 MSS,則允許傳送;
  • 如果該包含有 FIN,則允許傳送;
  • 設定了 TCP_NODELAY 選項,則允許傳送;
  • 未設定 TCP_CORK 選項時,若所有發出去的小資料包(包長度小於 MSS)均被確認,則允許傳送;
  • 上述條件都未滿足,但發生了超時(一般為 200ms),則立即傳送。

本來 Nagle 演算法是防止糊塗視窗綜合症產生的,但當我們的應用場景主要是傳送小資料時,極端情況下會被延遲 200ms,這幾乎是不能忍受的,所以設定 TCP_NODELAY 選項很重要。

把資料發出去了,是不是隻需要等回包和(或)等失敗就行了?前面有提到 TCP 的自有連線超時失敗時間很長,傳送超時是不是也類似?傳統 Unix 的實現是(1、3、6、12、24、48、64、64……),實測 Android 手機各個廠商的實現各異,但也基本符合“指數退避”的原則,其中一個廠商的實現是(0.42、0.9、1.8、3.7、7.5、15、30、 60、120……),相比這兩個系統,iOS 的實現就比較激進了,為(1、1、1、2、4.5、9、13.5、26、26……)。瞭解了具體實現後,很明顯應用層在傳送資料階段仍然需要超時機制。

在 Mars 中有四個超時概念,分別為首包超時、包包超時、讀寫超時、任務超時。首包超時為從請求發出去到收到第一個包最大等待時長,讀寫超時則是單次請求從傳送請求到收到完整回包的最大等待時長,計算公式分別為:

  • 首包超時 = 發包大小/最低網速+伺服器約定最大耗時+併發數*常量;
  • 包包超時 = 常量;
  • 讀寫超時 = 首包超時+最大回包大小/最低網速;
  • 任務超時 = (讀寫超時 + 常量) * 重試次數。

需要特別注意的是,讀寫超時的計算公式中有一個最大回包大小,這個數值只能預估。目前在 Mars 中預估為 64K,這也是為什麼不建議用 Mars 傳輸大資料的原因之一。

在上述的方案中,讀寫超時、首包超時都使用了一些估值,使得這兩個超時是比較大的值。假如我們能獲得實時的動態網路資訊,也就能得到更好的超時機制。基於這個想法,我們引入了動態超時機制,基本思想是:根據最近的歷史任務完成情況把網路分為優良、評估、惡劣,由此來變動估值的大小。

接收

接收沒有太多需要注意的地方。只需保證迴圈接收的 Buffer 不要太小,以防產生太多的系統呼叫,且注意將網路執行緒和業務處理執行緒分離就行了。

連線的維持

如果需要頻繁傳送資料或需即時收到伺服器的訊息,維持一個長連線會是不錯的選擇:

  • 訊息及時;
  • 省電省流量;
  • 提高發送速度。

但運營商會因為網路資源的原因,當一個連線長時間不傳送資料時會斷掉該連線,所以要想保持連線,就需要用心跳維持。太長的心跳會導致起不到相應的功能,太短的心跳因為頻繁喚醒手機,頻繁讓 RRC 狀態機進入 Active 狀態,會非常耗電。Mars 針對這個問題也有智慧心跳的方案,不過一般建議心跳間隔最短 4.5 min(實際測試到某個地區移動聯通 NAT 超時時間 5min,電信的大於 28min)。

技術方案

通過上面對幾個過程針對性地優化之後,我們有了整體的優化方案。有方案就需要通過程式碼實現,但怎麼去寫程式碼也是需要仔細思考,首先我們來看一下行動網路應用的特點:

  • 隨時啟動與中止——使用者退出或更改賬戶、手機休眠與喚醒……
  • 併發少狀態多——主要功能收發、網路的有無、使用者的活躍狀態……
  • 儘量少的資源、儘量快的網路——省電、省流量、網路要敏感……

基於這些特點,在方案選擇上可能也需要再三斟酌。執行緒模型方面,訊息佇列比多執行緒更合適,I/O 模型上,事件驅動的 I/O 複用模型比阻塞式的更為靈活。

不過,無論使用哪種技術方案,程式碼都不大可能寫得一點問題都沒有。Crash 方面就需要依賴各個平臺自己的實現進行捕捉堆疊了,不過捕捉到的堆疊最好包括所有執行緒的。Bug 方面,一般是通過記下的 Xlog 日誌進行推斷,疑難雜症可通過 TCPDump 抓包進行分析。

作者:閆國躍,微信高階工程師,目前主要負責 Mars 開源工作。先後參與了微信終端基礎元件的開發、微信終端日誌系統的建設、微信終端運維門戶的開發。
責編:唐小引(@唐門教主),歡迎技術投稿、約稿、給文章糾錯,請傳送郵件至[email protected]
本文為 CSDN 原創文章,未經允許,請勿轉載。

瞭解最新移動開發、VR/AR 乾貨技術分享,請關注 mobilehub 微信公眾號(ID: mobilehub)。

相關推薦

終端跨平臺元件 Mars行動網路探索實踐

在 IM 方面,弱網路一直是橫亙在應用開發者面前的一大問題,微信終端跨平臺網路基礎元件 Mars 團隊基於微信業務需求,針對網路層進行了大量的優化工作,以解決國內在複雜行動網路情況下的網路連線問題,並經歷了微信 5 億使用者的檢驗。本文作者重點介紹了針對行動

小程式web-view的簡單思考實踐

微信小程式的元件web-view推出有一段時間了,這個元件的推出可以說是微信小程式開發的一個重要事件,讓微信小程式不會只束縛在微信圈子裡了,打開了一個口子,這個口子或許還比較小,但未來有無限可能。簡單思考1.通過web-view嵌入網頁功能開放,給微信小程式的發展帶來無限的可

Mars 1.2.2 釋出,官方跨平臺跨業務終端基礎元件

   Mars 1.2.2 釋出了,Mars 是微信官方的跨平臺跨業務的終端基礎元件。 更新如下: 這是最後一個使用 stlport_shared stl 的版本,後續會使用 c++_shared, 並引入 C++11 這是最後一個使用 Android.mk 構建的版本,

小程式元件傳值的實驗

實驗目的 驗證微信小程式框架wepy元件之間進行資料傳遞的猜想與疑問 1. 猜想一: 元件在進行傳值的時候:<my-content :item.sync="myDate.item">這種寫法是否支援? 2. 猜想二: 元件中是否可以引用其他元件,並進行資料傳遞 實

小程式——元件之swiper

大家看到許多網頁的首頁面都會有圖片的輪播,圖片輪播:既能通過圖片增加整個頁面的美觀程度,又能讓客戶一眼看出網頁想要表達的內容。那麼,今天我們就通過swiper實現圖片的輪播效果,簡單又好看。 /**wxml**/ <swiper  indicator={{是否顯示面板指示點}}

小程式-元件-view標籤

簡單來說 ,可以理解為  view標籤 相當於div標籤。  view 具有屬性名 hover-class <view hover-class="color-red" >點我改變字型顏色 </view> 如果設定 color-red 為紅色, 則點選view

小程式元件攜帶引數

通過元件來傳遞引數tiggerEvent,注意元件事件命名要與引用處的命名一致(加粗部分) 元件test.wxml: 元件js: methods: { onTap: function(e){ var myEventDetail = { id:e.target.dataset.

小程式元件 movable-area 穿透性問題

使用場景:在頁面中,通過拖動圖示,可以在手機視窗中隨處移動,且不超出手機視窗範圍。 movable-area 的可移動區域,注意:movable-area 必須設定width和height屬性,不設定預設為10px movable-view 可移動的檢視容器,在頁面中可以

React-仿通訊錄控制元件

起因:產品給了一個需求,要求寫一個跟微信朋友圈一樣,取首字母產生的通訊錄標籤頁, 寫了一遍,發現意外的好寫 最終效果 資料採用了隨機字串的方法,並且使用陣列方法排序, let data = []; let title = [];

007-01、小程式---元件之swiper(tab切換)

此tab切換以登入頁面為參照。 1、WXMl <view class="tab-title"> <block wx:for="{{msg}}" wx:key="myke

【騰訊Bugly乾貨分享】打造“小程式”元件化開發框架

作者:Gcaufy 導語 Bugly 之前發了一篇關於微信小程式的開發經驗分享(點選閱讀),小夥伴們在公眾賬號後臺問了很多關於小程式開發方面的問題,精神哥在查閱相關內容的時候,發現了龔澄同學自己寫了一個小程式開發框架,真的怒贊,趕緊安利給大家

小程式例子——獲取手機網路狀態

1、效果展示 2、關鍵程式碼 .wxml佈局檔案程式碼 手機網路狀態:{{netWorkType}} .js邏輯檔案程式碼 Page({ data: { netWorkType:'

小程式元件通訊(一)

  當我們擼前端頁面的時候,當發現一個頁面內容很多、且有一定的分類,就應當考慮是否需要將頁面拆分成一個個元件。當使用元件的時候,不可避免的需要知道父子頁面通訊以及兄弟頁面的通訊。   1、父向子通訊 是通過properity去傳遞即可。properity是元件對外屬性,有三個屬性:type(必須)、valu

小程式 · 元件樣式效果 · 坑

小程式入門 -- 那些年我們踩過的坑 swiper元件     1.改變 swiper上 分頁器/指示器/小圓點 /面板指示點/indicator-dots 的預設樣式 預設樣式效果 目標效果 方法1:             原理:改變微信小程

小程式----元件之progress

根據文件寫程式碼 效果圖: 專案結構: 核心程式碼: index.wxml <view class="classname"> <progress percent="20" sh

小程式系列4(網路請求)

wx.request(OBJECT)發起https請求 第一點要注意的是微信小程式只支援https,而且需要在微信公眾平臺後臺設定配置伺服器域名 考慮到前期開發階段不一定弄了https支

小程式元件知識點GET

1. 可滾動檢視區域元件scroll-view 在滾動 scroll-view 時會阻止頁面回彈,所以在 scroll-view 中滾動,是無法觸發下拉重新整理事件 onPullDownRefresh,所以如果一定要使用下拉重新整理,請使用頁面的滾動,而不是

小程式----元件之switch

效果圖: 專案結構: 核心程式碼: index.wxml <view class="body-view"> <switch checked bindchange="sw

解決小程式元件scroll-view中bindscrolltolower事件觸發不了的問題

微信小程式學習了有一段時間了,在學習的過程中遇到了很多的問題。今天我就來和大家分享一下我在使用scroll-view元件時遇到的關於bindscrolltolower事件觸發不了的問題。 scroll-view是微信小程式中的一個檢視容器元件,用來顯示可滾動的

小程式】API之網路(一)發起請求

週末很早起來了,折騰一上午終於把小demo搞定了。1.微信小程式的發起網路請求,使用wx.request(OBJECT),OBJECT的引數說明如下引數名型別必填預設值說明urlString是開發者伺服