移動端IM開發者必讀(二):史上最全移動弱網路優化方法總結

移動端IM開發者必讀(二):史上最全移動弱網路優化方法總結

來自專欄 即時通訊技術分享

本系列文章引用了騰訊技術專家樊華恆《海量之道系列文章之弱聯網優化 gad.qq.com/article/deta》的章節,感謝原作者。

1、前言

本文接上篇《移動端IM開發者必讀(一):通俗易懂,理解移動網路的「弱」和「慢」》,關於移動網路的主要特性,在上篇中已進行過詳細地闡述,本文將針對上篇中提到的特性,結合我們的實踐經驗,總結了四個方法來追求極致的「爽快」:快鏈路、輕往複、強監控、多非同步,從理論講到實踐、從技術講到產品,理論聯繫實際,舉一反三,希望給您帶來啟發

如果您還未閱讀完上篇《移動端IM開發者必讀(一):通俗易懂,理解移動網路的「弱」和「慢」》,建議您先行讀完後再續本文。

本篇的目的,就是希望以通俗易懂的語言,幫助移動端IM開發者更好地針對性優化移動網路的各種特性,使得開發出的功能給用戶帶來更好的使用體驗。

本文乃全網同類文章中,唯一內容最全、「糞」量最重者,請做好心理準備耐心讀下去,不要辜負作者已打上石膏的雙手和用廢的鍵盤。

另外,《現代移動端網路短連接的優化手段總結:請求速度、弱網適應、安全保障》這篇文章也提到了本文所闡述的相關內容,強烈建議閱讀。

學習交流:

- 即時通訊開發交流3群:185926912[推薦]

- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》

(本文同步發佈於:52im.net/thread-1588-1-

2、系列文章

本文是《移動端IM開發者必讀》系列文章的第2篇:

  • 《移動端IM開發者必讀(一):通俗易懂,理解移動網路的「弱」和「慢」》
  • 《移動端IM開發者必讀(二):史上最全移動弱網路優化方法總結》(本文)

如果您是IM開發初學者,強烈建議首先閱讀《新手入門一篇就夠:從零開發移動端IM》。

3、相關文章

1)關於網路通信的基礎文章:

  • 如果您對網路通信知識了解甚少,建議閱讀《網路編程懶人入門系列文章》,更高深的網路通信文章可以閱讀《不為人知的網路編程系列文章》。

2)涉及移動端網路特性的文章:

  • 《現代移動端網路短連接的優化手段總結:請求速度、弱網適應、安全保障》
  • 《談談移動端 IM 開發中登錄請求的優化》
  • 《移動端IM開發需要面對的技術問題(含通信協議選擇)》
  • 《簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端》
  • 《微信對網路影響的技術試驗及分析(論文全文)》
  • 《騰訊原創分享(一):如何大幅提升移動網路下手機QQ的圖片傳輸速度和成功率》
  • 《騰訊原創分享(二):如何大幅壓縮移動網路下APP的流量消耗(上篇)》
  • 《騰訊原創分享(二):如何大幅壓縮移動網路下APP的流量消耗(下篇)》
  • 《如約而至:微信自用的移動端IM網路層跨平台組件庫Mars已正式開源》

4、優化方法一:「快鏈路」

我們需要有一條(相對)快速、(相對)順暢、(相對)穩定的網路通道承載業務數據的傳輸,這條路的最好是傳輸快、不擁堵、帶寬大、收費少。生活中做個類比,我們計劃驅車從深圳到廣州,如果想當然走廣深高速十之八九要杯具,首先這個高速略顯破敗更像省道,路況不佳不敢提速;其次這條路上的車時常如過江之鯽,如果身材不好操控不便,根本就快不起來;最後雙向六車道雖然勉強可以接受,但收費居然比廣深沿江高速雙向八車道還貴;正確的選路方案目前看是走沿江高速,雖然可能要多跑一段里程,但是通行更暢快。

實際上,真實情況要更複雜,就如同上篇中【圖二 有線互聯網和移動互聯網網路質量差異】所示(就是下圖),漫漫征途中常常會在高速、國道、省道、田間小道上切換。

4.1TCP/IP協議棧參數調優

純技術活,直接上建議得了,每個子項爭取能大致有個背景交待,如果沒說清楚,可以先看看以下資料:

《TCP/IP詳解 - 第17章·TCP:傳輸控制協議》

《TCP/IP詳解 - 第18章·TCP連接的建立與終止》

《TCP/IP詳解 - 第21章·TCP的超時與重傳》

《通俗易懂-深入理解TCP協議(上):理論基礎》

《通俗易懂-深入理解TCP協議(下):RTT、滑動窗口、擁塞處理》

《理論經典:TCP協議的3次握手與4次揮手過程詳解》

《不為人知的網路編程(一):淺析TCP協議中的疑難雜症(上篇)》

《不為人知的網路編程(二):淺析TCP協議中的疑難雜症(下篇)》

《不為人知的網路編程(三):關閉TCP連接時為什麼會TIME_WAIT、CLOSE_WAIT》

《網路編程懶人入門(三):快速理解TCP協議一篇就夠》

① 控制傳輸包大小

控制傳輸包的大小在1400位元組以下。暫時不講為什麼這樣建議,先舉個例子來類比一下,比如一輛大卡車滿載肥豬正在高速上趕路,豬籠高高層疊好不壯觀,這時前方突然出現一個隧道限高標識,司機發現卡車超限了,這下咋整。方案一,停車調頭重新找路,而且十之八九找不到,最後只能哪來回哪;方案二,把其中一群豬卸下來放本地找人代養,到達目的地卸完貨回來再取,你別說,這個機制在TCP/IP協議棧中也有,學名「IP分片」,後面會專門介紹。這個故事側面證實美國計算機科學家也曾經蹲在高速路邊觀察生豬超載運輸的過程,並飽受啟發。且慢,每次遇到問題,想到一些方案後我們都應該再捫心自問:「還有沒有更好的辦法呢?」。當然有,參照最近流行的說法,找個颱風眼,把豬都趕過去,飛一會就到了,此情此景想想也是醉了。

回歸正題,概括的說,我們設定1400這個閾值,目的是減少往複,提高效能。因為TCP/IP網路中也有類似高速限高的規定,如果在超限時想要繼續順暢傳輸,要麼做IP分片要麼把應用數據拆分為多個數據報文(意指因為應用層客戶端或伺服器向對端發送的請求或響應數據太大時,TCP/IP協議棧控制機制自動將其拆分為若干獨立數據報文發送的情況,後面為簡化討論,都以IP分片這個分支為代表,相關過程分析和結論歸納對二者均適用)。而一旦一個數據報文發生了IP分片,便會在數據鏈路層引入多次的傳輸和確認,加上報文的拆分和拼接開銷,令得整個數據包的發送時延大大增加,並且,IP分片機制中,任何一個分片出現丟失時還會帶來整個IP數據報文從最初的發起端重傳的消耗。有點枯燥了,更深入的理解,請參見:《海量之道系列文章之弱聯網優化 (二)》。

我們可以得出如下結論,TCP/IP數據報文大小超過物理網路層的限制時,會引發IP分片,從而增加時空開銷。

因此,設定合理的MSS至關重要,對於乙太網MSS值建議是1400位元組。什麼,你的數學是體育老師教的嗎?前面說乙太網最大的傳輸數據大小是1500位元組,IP數據報文包頭是20位元組,TCP報文包頭是20位元組,算出來MSS怎麼也得是1460位元組呀。如果回答是因為很多路由設備比如CISCO路由器把MSS設定為1400位元組,大夥肯定不幹,回憶一下IP和TCP的數據報包頭都各有40位元組的可選項,MTU中還需要為這些可選項留出空間,也就壓縮了MSS的空間。要是再追問為啥這個值不是1380位元組,那就有點過分了。

知識加油站:什麼是MSS?

TCP MSS(TCP Maximum Segment Size,TCP最大報文段長度,後面均簡稱MSS)表示TCP/IP協議棧一次可以傳往另一端的最大TCP數據長度,注意這個長度是指TCP報文中的有效「數據」(即應用層發出的業務數據)部分,它不包括TCP報文包頭部分,我們可以把它理解為卡車能裝運生豬的最大數量或重量。它是TCP選項中最經常出現,也是最早出現的選項,佔4位元組空間。

MSS是在建立TCP鏈接的三次握手過程中協商的,每一方都會在SYN或SYN/ACK數據報文中通告其期望接收數據報文的MSS(MSS也只能出現在SYN或SYN/ACK數據報中),說是協商,其實也沒太多迴旋的餘地,原因一會講。如果協商過程中一方不接受另一方的MSS值,則TCP/IP協議棧會選擇使用默認值:536位元組。

那麼問題來了,控制「限高」哪種方案才最強。我們嘗試探討一下。

首先,可以在我們自己IDC內將各種路由交換設備的MSS設定小於或等於1400位元組,並積极參与TCP三次握手時的MSS協商過程,期望達到自動控制伺服器收發數據報文大小不超過路徑最小MTU從而避免IP分片。這個方案的問題是如果路由路徑上其它設備不積极參与協商活動,而它的MTU(或MSS設置值)又比較low,那就白乾了。這就好比國家制定了一個高速沿途隧道限高公示通告標準,但是某些地方政府就是不告訴你,沒轍。

其次,可以在業務服務中控制應用數據請求/響應的大小在1400位元組以下(註:也無法根本避免前述方案中間路由MTU/MSS low的問題),在應用層數據寫入時就避免往返數據包大小超過協商確定的MSS。但是,歸根到底,在出發前就把數據拆分為多個數據報文,同IP分片機制本質是相同的,交互響應開銷增加是必然的。考慮到人在江湖,安全第一,本方案從源頭上控制,顯得更實際一些。

當然,最靠譜的還是做簡法,控制傳輸數據的慾望,用曼妙的身姿騰挪有致,相關的內容放到輕往複章節探討。

對應到前面的快樂運豬案例,就是要麼在生豬裝車之前咱們按照這條路上的最低限高來裝車(問題是怎麼能知道整個路上的最低限高是多少),要麼按照國家標準規定允許的最小限高來裝車,到這裡,肥豬們終於可以愉快的上路了,風和日麗,通行無阻,嗯,真的嗎?

② 放大TCP擁塞窗口

把TCP擁塞窗口(cwnd)初始值設為10,這也是目前Linux Kernel中TCP/IP協議棧的預設值。放大TCP擁塞窗口是一項有理有據的重要優化措施,對移動網路尤其重要,我們同樣從一些基本理論開始逐步深入理解它。

TCP是個傳輸控制協議,體現控制的兩個關鍵機制分別是基於滑動窗口的端到端之間的流量控制和基於RTT/RTO測算的端到網路之間的擁塞控制。

流量控制目標是為了避免數據發送太快對端應用層處理不過來造成SOCKET緩存溢出,就像一次發了N車肥豬,買家那邊來不及處理,然後臨時囤貨的豬圈又已客滿,只好拒收/拋棄,相關概念和細節我們不展開了,有興趣可以研讀《TCP/IP詳解 卷一:協議》。

擁塞控制目標是在擁塞發生時能及時發現並通過減少數據報文進入網路的速率和數量,達到防止網路擁塞的目的,這種機制可以確保網路大部分時間是可用的。擁塞控制的前提在於能發現有網路擁塞的跡象,TCP/IP協議棧的演算法是通過分組丟失來判斷網路上某處可能有擁塞情況發生,評判的具體指標為分組發送超時和收到對端對某個分組的重複ACK。在有線網路時代,丟包發生確實能比較確定的表明網路中某個交換設備故障或因為網路埠流量過大,路由設備轉發處理不及時造成本地緩存溢出而丟棄數據報文,但在移動網路中,丟包的情況就變得非常複雜,其它因素影響和干擾造成丟包的概率遠遠大於中間路由交換設備的故障或過載。比如短時間的信號干擾、進入一個信號屏蔽的區域、從空閑基站切換到繁忙基站或者移動網路類型切換等等。網路中增加了這麼多不確定的影響因素,這在TCP擁塞控制演算法最初設計時,是無法預見的,同時,我們也確信未來會有更完善的解決方案。這是題外話,如有興趣可以找些資料深入研究(詳見:《TCP/IP詳解 - 第21章·TCP的超時與重傳》、《通俗易懂-深入理解TCP協議(下):RTT、滑動窗口、擁塞處理》、《海量之道系列文章之弱聯網優化 (三)》)。

擁塞控制是TCP/IP協議棧最經典的和最複雜的設計之一,互聯網自我犧牲的利他精神表露無遺,設計者認為,在擁塞發生時,我們應該減少數據報文進入網路的速率和數量,主動讓出道路,令網路能儘快調整恢復至正常水平。

③ 調大SOCKET讀寫緩衝區

把SOCKET的讀緩衝區(亦可稱為發送緩衝區)和寫緩衝區(亦可稱為接收緩衝區)大小設置為64KB。在Linux平台上,可以通過 setsockopt 函數設置SO_RCVBUF和SO_SNDBUF選項來分別調整SOCKET讀緩衝區和寫緩衝區的大小。

這兩個緩衝區跟我們的TCP/IP協議棧到底有怎麼樣的關聯呢。我們回憶一下TCP數據報格式及首部中的各欄位裡面有個16位窗口大小(見下圖),還有我們前面提到的流量控制機制和滑動窗口的概念,大幕徐徐拉開,主角紛紛粉墨登場。在正式詳細介紹之前,按照傳統,我們還是先站在豬場老闆的角度看一下,讀緩衝區就好比買家用來囤貨的臨時豬圈,如果貨到了買家使用部門來不及處理,就先在這裡臨時囤著,寫緩衝區就好比養豬場根據訂單裝好車準備發貨,如果買家說我現在可以收貨便可速度發出,有點明白了吧。

④ 調大RTO(Retransmission TimeOut)初始值

將RTO(Retransmission TimeOut)初始值設為3s。

TCP為每一個報文段都設定了一個定時器,稱為重傳定時器(RTO),當RTO超時且該報文段還沒有收到接收端的ACK確認,此時TCP就會對該報文段進行重傳。當TCP鏈路發生超時時,意味著很可能某個報文段在網路路由路徑的某處丟失了,也因此判斷此時網路出現擁塞的可能性變得很大,TCP會積極反應,馬上啟動擁塞控制機制。

RTO初始值設為3s,這也是目前Linux Kernel版本中TCP/IP協議棧的預設值,在鏈路傳輸過程中,TCP協議棧會根據RTT動態重新計算RTO,以適應當前網路的狀況。有很多的網路調優方案建議把這個值盡量調小,但是,我們開篇介紹移動網路的特點之一是高時延,這也意味著在一個RTT比較大的網路上傳輸數據時,如果RTO初始值過小,很可能發生不必要的重傳,並且還會因為這個事件引起TCP協議棧的過激反應,大炮一響,擁塞控制閃亮登場。

豬場老闆的態度是什麼樣的呢:曾經有一份按時發貨的合同擺在我的面前,我沒有去注意,等到重新發了貨才追悔莫及,塵世間最痛苦的事莫過於此,如果上天能給我一個再來一次的機會,我希望對甲方說耐心點,如果非要給這個耐心加一個期限的話,我希望是一萬年。

⑤ 禁用TCP快速回收

TCP快速回收是一種鏈接資源快速回收和重用的機制,當TCP鏈接進入到TIME_WAIT狀態時,通常需要等待2MSL的時長,但是一旦啟用TCP快速回收,則只需等待一個重傳時間(RTO)後就能夠快速的釋放這個鏈接,以被重新使用。

Linux Kernel的TCP/IP協議棧提供了一組控制參數用於配置TCP埠的快速回收重用,當把它們的值設置為1時表示啟用該選項:

  • 1) net.ipv4.tcp_tw_reuse = 1
  • 2) net.ipv4.tcp_tw_recycle = 1
  • 3) net.ipv4.tcp_timestamps = 1(tcp_tw_recycle啟用時必須同時啟用本項,反之則不然,timestamps用於RTT計算,在TCP報文頭部的可選項中傳輸,包括兩個參數,分別為發送方發送TCP報文時的時間戳和接收方收到TCP報文響應時的時間戳。Linux系統和移動設備上的Android、iOS都預設開啟了此選項,建議不要隨意關閉)

以上參數中tw是TIME_WAIT的縮寫,TIME_WAIT與TCP層的鏈接關閉狀態機相關。具體TIME_WAIT是誰,從哪裡來,往哪裡去,可以詳見:《海量之道系列文章之弱聯網優化 (四)》。

⑥ HTTP協議:打開SOCKET的TCP_NODELAY選項

TCP/IP協議棧為了提升傳輸效率,避免大量小的數據報文在網路中流竄造成擁塞,設計了一套相互協同的機制,那就是Nagles Algorithm和TCP Delayed Acknoledgement。

Nagle演算法(Nagles Algorithm)是以發明人John Nagle的名字來命名。John Nagle在1984年首次用這個演算法來嘗試解決福特汽車公司的網路擁塞問題(RFC 896),該問題的具體描述是:如果我們的應用程序一次產生1個位元組的數據(典型的如telnet、XWindows等應用),而這個1個位元組數據又以網路數據包的形式發送到遠端伺服器,那麼就很容易使網路中有太多微小分組而導致過載。

因為傳輸1個位元組有效數據的微小分組卻需花費40個位元組的額外開銷(即IP包頭20位元組 + TCP包頭20位元組),這種有效載荷利用率極其低下的情況被統稱為愚蠢窗口症候群(Silly Window Syndrome),前面我們在談MSS時也提到過,如果為一頭豬開個大卡車跑一趟,也夠愚鈍的。對於輕負載廣域網或者區域網來說,尚可接受,但是對於重負載的廣域網而言,就極有可能引起網路擁塞導致癱瘓。

現代TCP/IP 協議棧默認幾乎都啟用了這兩個功能。

我們在移動APP的設計實現中,請求大部分都很輕(數據大小不超過MSS),為了避免上述分析的問題,建議開啟SOCKET的TCP_NODELAY選項,同時,我們在編程時對寫數據尤其要注意,一個有效指令做到一次完整寫入(後面會講協議合併,是多個指令一次完整寫入的設計思想),這樣伺服器會馬上有響應數據返回,順便也就捎上ACK了。

4.2接入調度

① 就快接入

在客戶端接入伺服器調度策略的演化過程中,我們最早採用了「就近接入」的策略,在距離客戶端更近的地方部署伺服器或使用CDN,期望通過減少RTT來提高網路交互響應性能。這個策略在國內的落地執行還需要加一個前綴:「分省分運營商」,這就給廣大負責IDC建設的同學帶來了巨大的精神和肉體折磨。

在持續運營的過程中,根據觀察到的數據,發現並非物理距離最近的就是最快的。回憶一下前面談到的吞吐量指標BDP,它與鏈路帶寬和RTT成正比關係,而RTT是受物理距離、網路擁塞程度、IDC吞吐量、跨網時延等諸多因素綜合影響的,單純的就近顯然不夠精細了。

「就快接入」在「就近接入」策略的基礎上改善提升,它利用客戶端測速和報告機制,通過後台大數據分析,形成與客戶端接入IP按就快原則匹配接入伺服器的經驗調度策略庫,令客戶端總能優先選擇到最快的伺服器接入點。

有關就快接入的更詳細方案,請參見:《海量之道系列文章之弱聯網優化(五)》一文的「3.1.2節」。

② 去DNS的IP直連

DNS不但需要1個RTT的時間消耗,而且移動網路下的DNS還存在很多其它問題:

  • 1) 部分DNS承載全網用戶40%以上的查詢請求,負載重,一旦故障,影響巨大,這樣的案例在PC互聯網也有很多,Google一下即可感受觸目驚心的效果;
  • 2) 山寨、水貨、刷ROM等移動設備的LOCAL DNS設置錯誤;
  • 3) 終端DNS解析濫用,導致解析成功率低;
  • 4) 某些運營商DNS有域名劫持問題,實際上有線ISP也存在類似問題。域名劫持對安全危害極大,產品設計時要注意服務端返回數據的安全校驗(如果協議已經建立在安全通道上時則不用考慮,安全通道可以基於HTTPS或者私有安全體系)。對於劫持的判斷需要客戶端報告實際拉取服務數據的目標地址IP等信息;
  • 5) DNS污染、老化、脆弱。

綜上就是在前述就快接入小節中,接入調度FSM會優先使用動態伺服器列表的原因。

③ 網路可達性探測

在連接建立過程中如果出現連接失敗的現象,而終端系統提供的網路狀態介面反饋網路可用時,我們需要做網路可達性探測(即向預埋的URL或者IP地址發起連接嘗試),以區別網路異常和接入服務異常的情況,為定位問題,優化後台接入調度做數據支持。

探測數據可以非同步報告到伺服器,至少應該包含以下欄位:

  • 1) 探測事件ID,要求全局唯一不重複;
  • 2) 探測發生時間;
  • 3) 探測發生時網路類型和其它網路信息(比如WIFI時的SSID等);
  • 4) 本地調度的接入伺服器集合類型;
  • 5) 本地調度的接入伺服器IP(如使用域名接入,可忽略);
  • 6) 探測的目標URL或IP地址
  • 7) 本次探測的耗時。

4.3鏈路管理

鏈路就是運肥豬的高速路,就快接入是選路,鏈路管理就是如何高效的使用這條路。下面是一些實踐總結:

① 鏈路復用

我們在開篇討論無線網路為什麼慢的時候,提到了鏈接建立時三次握手的成本,在無線網路高時延、頻抖動、窄帶寬的環境下,用戶使用趨於碎片化、高頻度,且請求響應又一次性往返居多、較頻繁發起等特徵,建鏈成本顯得尤其顯著。

因此,我們建議在鏈路創建後可以保持一段時間,比如HTTP短鏈接可以通過HTTP Keep-Alive,私有協議可以通過心跳等方式來保持鏈路。

具體要點建議如下:

  • 1) 鏈路復用時,如果服務端按就快策略機制下發了新的接入動態伺服器列表,則應該按照接入調度FSM的狀態變遷,在本次交互數據完成後,重建與新的接入伺服器的IP鏈路,有三個切換方案和時機可選擇:

    - a. 關閉原有鏈接,暫停網路通訊,同時開始建立與新接入伺服器的TCP鏈路,成功後恢復與伺服器的網路交互;

    - b. 關閉原有鏈接,暫停網路通訊,待有網路交互需求時開始建立與新接入伺服器的IP鏈路;

    - c. 原有鏈接繼續工作,並同時開始建立與新接入伺服器的TCP鏈路,成功後新的請求切換到新建鏈路上,這個方式或可稱為預建鏈接,原鏈接在空閑時關閉。
  • 2) 鏈路復用時區分輕重數據通道,對於業務邏輯等相關的信令類輕數據通道建議復用,對於富媒體拉取等重數據通道就不必了;
  • 3) 鏈路復用時,如與協議合併(後面會討論)結合使用,效果更佳。

② 區分網路類型的超時管理

在不同的網路類型時,我們的鏈路超時管理要做精細化的區別對待。鏈路管理中共有三類超時,分別是連接超時、IO超時和任務超時。

我們有一些經驗建議,提出來共同探討:

  • 1) 連接超時:2G/3G/4G下5 ~ 10秒,WIFI下5秒(給TCP三次握手留下1次超時重傳的機會,可以研究一下《TCP/IP詳解 卷一:協議》中TC P的超時與重傳部分);
  • 2) IO超時:2G/3G/4G下15 ~ 20秒(無線網路不穩定,給抖動留下必要的恢復和超時重傳時間),WIFI下15秒(1個MSL);
  • 3) 任務超時:根據業務特徵不同而差異化處理,總的原則是前端面向用戶交互界 面的任務超時要短一些(盡量控制在30秒內並有及時的反饋),後台任務可以長一些,輕數據可以短一些,重數據可以長一些;
  • 4) 超時總是伴隨著重試,我們要謹慎小心的重試,後面會討論。

超時時間宜短不宜長,在一個合理的時間內令當前鏈路因超時失效,從而驅動調度FSM狀態的快速變遷,效率要比痴痴的等待高得多,同時,在用戶側也能得到一個較好的正反饋。

各類超時參數最好能做到雲端可配可控。

③ 優質網路下的並發鏈路

當我們在4G、WIFI(要區分是WIFI路由器還是手機熱點)等網路條件較優時,對於請求隊列積壓任務較多或者有重數據(富媒體等下載類數據)請求時,可以考慮並發多個鏈路並行執行。

對於單一重數據任務的多鏈接並發協同而言,需要伺服器支持斷點續傳,客戶端支持任務協同調度;

④ 輕重鏈路分離

輕重鏈路分離,也可以說是信令和數據分離,目的是隔離網路通訊的過程,避免重數據通訊延遲而阻塞了輕數據的交互。在用戶角度看來就是信息在非同步載入,控制指令響應反饋及時。

移動端大部分都是HTTP短鏈接模式工作,輕重數據的目標URL本身就不同,比較天然的可以達到分離的要求,但是還是要特別做出強調,是因為實踐中有些輕數據協議設計裡面還會攜帶類似頭像、驗證碼等的實體數據。

⑤ 長鏈接

長鏈接對於提升應用網路交互的及時性大有裨益,一方面用戶使用時,節省了三次握手的時間等待,響應快捷;另一方面伺服器具備了實時推送能力,不但可以及時提示用戶重要信息,而且能通過推拉結合的非同步方案,更好的提升用戶體驗。

長鏈接的維護包括鏈接管理、鏈接超時管理、任務隊列管理等部分,設計實施複雜度相對高一些,尤其是在移動網路環境下。為了保持鏈路還需要做心跳機制(從另外一個角度看,這也是針對簡單信息一個不錯的PULL/PUSH時機,,但需注意數據傳輸要夠輕,比如控制在0.5KB以內),而心跳機制是引入長鏈接方案複雜度的一個重要方面,移動網路鏈路環境複雜,國內網關五花八門,鏈路超時配置各有千秋,心跳時長選擇學問比較大,不但要區分網路類型,還得區分不同運營商甚至不同省市,歷史上曾經實踐了2分鐘的心跳間隔,最近比較多的產品實踐選擇4.5分鐘的心跳間隔。而且長鏈接除了給移動網路尤其是空中信道帶來負擔外,移動設備自身的電量和流量也會有較大的消耗,同時還帶來後端帶寬和伺服器投入增加。

有關Android的推送問題,可以參考:

《應用保活終極總結(三):Android6.0及以上的保活實踐(被殺復活篇)》

《Android進程保活詳解:一篇文章解決你的所有疑問》

《Android端消息推送總結:實現原理、心跳保活、遇到的問題等》

《深入的聊聊Android消息推送這件小事》

《為何基於TCP協議的移動端IM仍然需要心跳保活機制?》

《微信團隊原創分享:Android版微信後台保活實戰分享(網路保活篇)》

《移動端IM實踐:實現Android版微信的智能心跳機制》

《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》)

⑥ 小心重試

自動重試是導致後台雪崩的重要因素之一。在移動網路不穩定的條件下,大量及時的重試不但不能達到預期,反而無謂的消耗移動設備的電量甚至流量。

因此,我們在重試前要有一些差異化的考慮:

  • 1) 當前移動設備的網路狀況如何,如果沒有網路,則不必重試;
  • 2) 重試設定必要的時間間隔,因為移動接入網路抖動到恢復可能需要一點時間,馬上重試並非最佳策略,反而可能無謂的消耗電量。實踐中,可以在一次連接或IO失敗(立即失敗或超時)時,過3 ~ 5秒後再試;
  • 3) 重試應設定必要的總時限,因為三個伺服器列表比較長,每個伺服器地址都要重試和等待若干次,最終可能導致接入調度FSM和伺服器列表調度FSM流轉耗時過長,此時用戶側體驗表現為長時間等待無響應。總時限參數可以參考前述區分網路類型的超時管理中的任務超時值。一旦某次重試成功,重試總時限計時器要歸零;
  • 4) 伺服器下發特定錯誤碼(比如伺服器故障、過載或高負載)時,提示客戶端停止重試並告知安撫用戶,我們在強監控這個主題下有詳細的討論。

每個目標伺服器地址的重試次數、重試總時限和重試時間間隔最好能做到雲端可配可控。

特別需要提出的一點是,移動APP採用HTTP短鏈接模式實現CS交互時,廣泛的使用了系統原生組件或者開源組件,這些友好的模塊把超時和重試都封裝起來,其預設值是否適合自己的業務特點,需要多多關注。使用前,最好能知其然更知其所以然。

⑦ 及時反饋

透明和尊重,會帶來信任和默契,家庭如此、團隊如此、用戶亦如此。欲蓋彌彰和裝傻充愣也許短暫取巧,拉長時間軸來看,肯定要付出慘重的代價。及時和真誠的告知狀況,贏得諒解和信任,小付出,大回報,試過都知道。

當發現因為網路不存在或者其它屬於移動端設備鏈路的異常時,應該及時和顯著的提示用戶,讓用戶注意到當前有諸如網路不存在、FREE WIFI接入認證頁面需確認等等問題,使用戶可以及時處理或理解問題狀態。

當發現是伺服器問題時,應及時、顯著和真誠的告知用戶,爭取用戶的諒解。

網路異常提示或伺服器故障通告等信息的呈現要做到一目了然,無二義和二次交互。

4.4IO管理

基於一個快速和高效管理的鏈路之上,做好IO調度和控制,也是提升效能和改善用戶體驗的重要環節。

要探討的內容包括:

① 非同步IO

非同步化IO的目的就是避免資源的集中競爭,導致關鍵任務響應緩慢。我們在後面差異服務個大的分類中會重點探討。這裡特別先提出來,是建議在程序架構頂層設計時,要在整體機制上支持非同步化,設計必要的非同步匯流排來聯繫各個層級模塊,匯流排可能會涉及包括隊列管理(優先順序、超時、CRUD等)、事件驅動、任務調度等。

非同步IO除了網路方面外,對移動設備,我們還特別要考慮一下磁碟IO的非同步。因為頻繁、大吞吐量的磁碟IO會造成APP的UI卡頓,從用戶體驗上看就是交互響應遲鈍或者滑動幀率下降。一般來說,磁碟IO非同步會選用空間換時間的方案,即緩存數據批量定時寫入磁碟。

② 並發控制

有了非同步IO,並發控制就顯得尤為重要。把非同步機制當作銀彈任意使用,就如同我們給移動APP設計了一個叫「發現」的地方一樣,很可能各種膨脹的需求、不知道如何歸類的需求就紛至沓來,期待有朝一日被「發現」。

非同步IO提供了一個很好的發射後不用管的機制,這就會造成使用者的膨脹,無論是否必要、無論輕重緩急,把請求一股腦的丟給非同步隊列,自己瀟洒的轉身就走。這樣不但會帶來效率和交互響應性能的下降,也會造成資源的無謂消耗。

在後面多非同步這個大分類的討論中會涉及到輕重緩急的話題,在前述非同步IO的磁碟IO的時空效率轉換話題中,還應該包括IO並發的控制,我們即不能因為並發過多的鏈路造成網路帶寬的獨佔消耗影響其它APP的使用,也不可因快速、大量的非同步數據造成緩寫機制形同虛設或是佔用過大的內存資源。

③ 推拉結合

PUSH機制應該是蘋果公司在移動設備上取得輝煌成就的最重要兩個機制之一,另外一個是移動支付體系。我們這裡的討論不包括iOS和APPLE移動設備的擬人化交互體驗,只側重根基性的機制能力。APNS解決了信息找人的問題,在過去,只有運營商的簡訊有這個能力,推送和拉取使得我們具備了實時獲取重要信息的能力。

為何要推拉結合。因為系統級的推送體系也必須維持一個自己的鏈路,而這個鏈路上要承載五花八門的APP推送數據,如果太重,一方面會在設計上陷入個性化需求的繁瑣細節中,另外一方面也會造成這條鏈路的擁堵和性能延遲。因此,通過PUSH通知APP,再由APP通過自己的鏈路去PULL數據,即有效的利用了PUSH機制,又能使得APP能按需使用網路,不但簡化了鏈路管理,而且節省了電量和流量。

④ 斷點續傳

一方面,在討論鏈路管理時,我們建議了優質網路下的並發鏈路來完成同一個重數據拉取任務。這就會涉及到任務的拆分和並行執行,基礎是後台能支持斷點續傳。

另外一方面,從客戶端的角度而言,移動網路的不穩定特點,可能會造成某個重數據拉取任務突然失敗,無論是自動重試還是用戶驅動的重試,如果能從上次失效的上下文繼續任務,會有省時間、省電量和省流量的效果,想想也會覺得十分美好。

5、優化方法二:「輕往複」

「技」止此爾。強調網路交互的「少」,更應強調網路交互的「簡」。

我們在一條高時延易抖動的通道上取得效率優勢的關鍵因素就是減少在其上的往複交互,最好是老死不相往來(過激),並且這些往複中交換的數據要盡量的簡潔、輕巧,輕車簡從。這個概念是不是有點像多干多錯,少干少錯,不幹沒錯。

把我們實踐過的主要手段提出來探討:

① 協議二進位化

二進位比較緊湊,但是可讀性差,也因此形成可維護性和可擴展性差、調測不便的不良印象。這也造成了大量可見字符集協議的出現。計算機是0和1的世界,她們是程序猿的水和電,任何一個整不明白,就沒法愉快的生活了。

② 高效協議

高效的協議可以從兩個層面去理解,一是應用層標準協議框架,二是基於其上封裝的業務層協議框架,有時候也可以根據需要直接在TCP之上把這兩個層面合併,形成純粹的業務層私有協議框架。不過,為了簡化網路模塊的通訊機制和一些通用性、兼容性考慮,目前大多數情況下,我們都會選擇基於HTTP這個應用層標準協議框架之上承載業務層協議框架。下面我們針對上述兩個層面展開探討。

首先是應用層的標準協議優化:比如HTTP/1.1的Pipeline、WebSocket(在HTML5中增加)、SPDY(由Google提出)、HTTP/2等,其中特別需要關注的是處在試驗階段的SPDY和草案階段的HTTP/2。

SPDY是Google為了規避HTTP/1.1暨以前版本的局限性開展的試驗性研究,主要包括以下四點:

  • 1) 鏈路復用能力:HTTP協議最早設計時,選擇了一問一答一連接的簡單模式,這樣對於有很多並發請求資源或連續交互的場景,鏈路建立的數量和時間成本就都增加了;
  • 2) 非同步並發請求的能力:HTTP協議最早的設計中,在拉取多個資源時,會對應並發多個HTTP鏈路(HTTP/1.1的Pipeline類似)時,服務端無法區分客戶端請求的優先順序,會按照先入先出(FIFO)的模式對外提供服務,這樣可能會阻塞客戶端一些重要優先資源的載入,而在鏈路復用的通道上,則提供了非同步並發多個資源獲取請求指令的能力,並且可以指定資源載入的優先順序,比如CSS這樣的關鍵資源可以比站點ICON之類次要資源優先載入,從而提升速度體驗;
  • 3) HTTP包頭欄位壓縮:(註:特指欄位的合併刪減,並非壓縮演算法之意)精簡,HTTP協議中HEAD中欄位多,冗餘大,每次請求響應都會帶上,在不少業務場景中,傳遞的有效數據尺寸遠遠小於HEAD的尺寸,帶寬和時間成本都比較大,而且很浪費;
  • 4) 伺服器端具備PUSH能力:伺服器可以主動向客戶端發起通信向客戶端推送數據。

HTTP/2由標準化組織來制定,是基於SPDY的試驗成果開展的HTTP協議升級標準化工作,有興趣了解詳細情況可以參考HTTP/2的DRAFT文檔。

其次是業務層的協議框架優化:它可以從三個方面考察

  • 一是協議處理性能和穩定性好,包括諸如協議緊湊佔用空間小,編碼和解碼時內存佔用少CPU消耗小計算快等等,並且bad casae非常少;
  • 二是可擴展性好,向下兼容自不必說,向上兼容也並非不能;
  • 三是可維護性強,在協議定義、介面定義上,做到可讀性強,把二進位協議以可讀字元的形式展示,再通過預處理轉化為源碼級文件參與工程編譯。

可能會有同學強調協議調測時的可閱讀、可理解,既然讀懂01世界應該是程序員的基本修養,這一項可能就沒那麼重要了。

高效的業務層協議框架從分散式系統早期代表Corba的年代就有很多不錯的實踐項目,目前最流行的開源組件應屬ProtoBuf,可以學習借鑒。

正所謂殊途同歸、心有靈犀、不謀而合,英雄所見略同......,說來說去,高效協議的優化思路也都在鏈路復用、推拉結合、協議精簡、包壓縮等等奇技淫巧的範疇之內。

有關Protobuf等技術的詳細文章,請參見:

《Protobuf通信協議詳解:代碼演示、詳細原理介紹等》

《如何選擇即時通訊應用的數據傳輸格式》

《強列建議將Protobuf作為你的即時通訊應用數據傳輸格式》

《全方位評測:Protobuf性能到底有沒有比JSON快5倍?》

《移動端IM開發需要面對的技術問題(含通信協議選擇)》

《簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端》

《理論聯繫實際:一套典型的IM通信協議設計詳解》

《58到家實時消息系統的協議設計等技術實踐分享》

《技術掃盲:新一代基於UDP的低延時網路傳輸層協議——QUIC詳解》

《金蝶隨手記團隊分享:還在用JSON? Protobuf讓數據傳輸更省更快(原理篇)》

《金蝶隨手記團隊分享:還在用JSON? Protobuf讓數據傳輸更省更快(實戰篇)》

③ 協議精簡

協議精簡的目的就是減少無謂的數據傳輸,提升網路效能。俗話說「千里不捎針」,古人誠不我欺也。

我們實踐總結以下三點供參考:

  • 1) 能不傳的就不傳:把需要的和希望有的數據都列出來,按照對待產品需求的態 度,先砍掉一半,再精簡一半,估計就差不多了。另外,高效協議提供了比較好的擴展性,預留欄位越少越好,移動互聯網演化非常快,經常會發現前瞻的預留總是趕不上實際的需求;
  • 2) 抽象公共數據:把各協議共性的屬性數據抽象出來,封裝在公共數據結構中, 即所謂包頭一次就傳一份,這個想法不新鮮,TCP/IP的設計者們早就身體力行了。除了帶來數據冗餘的降低外,還降低了維護和擴展的複雜度,一石二鳥,且抽且行;
  • 3) 多用整數少用字元:數字比文字單純,即簡潔又清晰,還不需要擔心英文不好被後繼者BS;
  • 4) 採用增量技術:通知變化的數據,讓接收方處理差異,這是個很好的設計思想,實踐中需要注意數據一致性的校驗和保障機制,後面會有專門的細節討論。

④ 協議合併

協議合併的目標是通過將多條交互指令歸併在一個網路請求中,減少鏈路創建和數據往複,提升網路效能。

把實戰總結的六點提出來供參考:

  • 1) 協議合併結合協議精簡,效率翻番;
  • 2) 協議合併的基礎是業務模型的分析,在分類的基礎上去做聚合。首先得區分出來緩急,把實時和非同步的協議分類出來分別去合併;其次得區分出來輕重,協議請求或協議響應的數據規模(指壓縮後),盡量確保在一個數據報文中可完成推拉;
  • 3) 協議合併在包的封裝上至少有兩種選擇,一是明文協議合併後統一打包(即壓縮和解密);二是明文協議分別打包,最後匯總;前者效率高一些,在實戰中用的也較普遍;後者為流式處理提供可能;
  • 4) 協議合併對伺服器的非同步處理架構和處理性能提出了更高的要求,特別需要權衡網路交互效率和用戶對後台處理返迴響應期待之間的取捨;
  • 5) 協議間有邏輯順序關係時,要認真考慮設計是否合理或能否合併;
  • 6) 重數據協議不要合併。

⑤ 增量技術

增量技術準確分類應該算是協議精簡的一個部分,它與業務特點結合的非常緊密,值得單獨討論一下。增量技術在CS數據流交互比較大的時候有充分發揮的空間,因為這個技術會帶來客戶端和伺服器計算、存儲的架構複雜度,增加資源消耗,並且帶來許多保障數據一致性的挑戰,當然,我們可以設計的更輕巧,容許一些不一致。

我們用一個案例來看看增量技術的運用。

在應用分發市場產品中,都有一個重要功能,叫更新提醒。它的實現原理很簡單,以Android設備為例,客戶端把用戶移動設備上安裝的APP包名、APP名稱、APP簽名、APP版本號等信息發送到伺服器,伺服器根據這些信息在APP庫中查找相應APP是否有更新並推送到客戶端。這個過程非常簡單,但如果用戶手機上裝了50個APP,網路上交互的數據流就非常客觀了,即浪費流量和電量,又造成用戶體驗的緩慢,顯得很笨重。

這個時候,增量技術就可以派上用場了,比如下面的方案:

  • 1) 每個自然日24小時內,客戶端選擇一個時間(優先選擇駐留在後台的時候)上報一次全量數據;
  • 2) 在該自然日24小時的其它時間,客戶端可以定時或在用戶使用時發送增量數據,包括卸載、安裝、更新升級等帶來的變化;
  • 3) 作為弱一致性的保障手段,客戶端在收到更新提示信息後,根據提醒的APP列表對移動設備上實際安裝和版本情況做一次核對;
  • 4) 上述擇機或定時的時間都可以由雲端通過下發配置做到精細化控制。

⑥ 包壓縮

前面精打細算完畢,終於輪到壓縮演算法上場了。選擇什麼演算法,中間有哪些實戰的總結,下面提出來一起探討:

  • 1) 壓縮演算法的選擇:我們比較熟悉的壓縮演算法deflate、gzip、bzip2、LZO、Snappy、FastLZ等等,選擇時需要綜合考慮壓縮率、內存和CPU的資源消耗、壓縮速率、解壓速率等多個緯度的指標,對於移動網路和移動設備而言,建議考慮使用gzip。另外需要注意的是,輕數據與重數據的壓縮演算法取捨有較大差異,不可一概而論;
  • 2) 壓縮和加密的先後秩序:一般而言,加密後的二進位數據流壓縮率會低一些,建議先壓縮再加密;
  • 3) 注意一些協議組件、網路組件或數據本身是否已經做過壓縮處理,要避免重複工作,不要造成性能和效率的下降:比如一些圖片格式、視頻或APK文件都有自己的壓縮演算法。

說到這,問題又來了,如果應用層標準協議框架做了壓縮,那麼基於其上封裝的業務層協議框架還需要壓縮嗎,壓縮技術到底哪家強?這個問題真不好回答,考慮到HTTP/2這樣的應用層標準協議框架定稿和普及尚需時日,建議在業務層協議框架中做壓縮機制。或者追求完美,根據後端應用層標準協議框架響應是否支持壓縮及在支持時的壓縮演算法如何等信息,動態安排,總的原則就是一個字:只選對的,不選貴的。

5、優化方法三:「強監控」

可監方可控,我們在端雲之間,要形成良好的關鍵運營數據的採集、匯總和分析機制,更需要設計雲端可控的配置和指令下發機制。

本篇重點討論與主題網路方面相關關鍵指標的「監」和「控」。

以就快接入為例來探討一下強監控能力的構建和使用:

  • 1) 接入質量監控:客戶端匯總接入調度FSM執行過程元信息以及業務請求響應結果的元信息,並由此根據網路類型不同、運營商不同、網路接入國家和省市不同分析接入成功率、業務請求成功率(還可細化按業務類型分類統計)、前述二者失敗的原因歸類、接入302重定向次數分布暨原因、接入和業務請求測速等;
  • 2) 建設雲端可控的日誌染色機制:便於快速有針對性的定點排查問題;
  • 3) 終端硬體、網路狀態的相關參數採集匯總;
  • 4) 建設雲端可控的接入調度(比如接入IP列表等)和網路參數(比如連接超時、IO超時、任務超時、並發鏈接數、重試間隔、重試次數等)配置下發能力;
  • 5) 伺服器根據匯總數據。

通過數據分析,結合伺服器自身的監控機制,可以做到:

  • a. 支持細粒度的接入調度和網路參數的優化雲控;
  • b. 支持伺服器的部署策略優化;
  • c. 發現移動運營商存在的一些差異化問題比如URL劫持、網路設備超時配置不當等問題便於推動解決;
  • d. 發現分省市伺服器服務質量的異常情況,可以動態雲端調度用戶訪問或者降級服務,嚴重時可以及時提示客戶端發出異常安撫通告,避免加劇伺服器的負載導致雪崩。安民告示的快速呈現能力,考驗了一個團隊對可「控」理解的深度,我們在實踐中,提供了三級措施來保障:

    - 第一級是伺服器端通過協議或跳轉URL下發的動態通告,這在非IDC公網故障且業務接入伺服器正常可用時適用;

    - 第二級是預埋靜態URL(可以是域名或IP形式,優先IP)拉取動態通告,適用其它故障,靜態URL部署的IP地址最好同本業務系統隔離,避免因為業務服務所在IDC公網故障不可用時無法訪問;

    - 第三級是客戶端本地預埋的靜態通告文案,內容會比較模糊和陳舊,僅作不時之需;
  • e. 支持非同步任務的雲端可配可控,比如下載類APP的下載時間、下載標的和下載條件約束(磁碟空間、移動設備電量、網路類型等)的差異化配置,通過錯峰調度,達到削峰平谷並提升用戶體驗的效果。

特別需要注意的是,客戶端數據報告一定要有數據篩選控制和信息過濾機制,涉及用戶隱私的敏感信息和使用記錄必須杜絕採樣上報。在我們的日誌染色機制中要特別注意,為了排查問題極可能把關鍵、敏感信息記錄報告到後端,引入安全風險。

6、優化方法四:「多非同步」

經過前面不懈的努力,初步打造了一個比較好的技術根基,好馬配好鞍,好車配風帆,怎麼就把領先優勢拱手送與特斯拉了。

用戶欲壑難平,資源供不應求,靠「術」並無法優雅的解決。跳出來從產品角度去觀察,還有些什麼能夠觸動我們思考的深度呢。根據不同的需求和使用場景,用有損服務的價值觀去權衡取捨,用完美的精神追求不完美,此乃道的層面。

所謂大道至簡,完美之道,不在無可添加,而在無可刪減。通過多非同步和各類緩存機制,提供區分網路、區分業務場景下的差異化服務,是我們孜孜以求的大「道」。

下面通過一些實踐案例的總結,來探索簡潔優雅的弱聯網體驗改善之道(開始肆無忌憚的吹噓了)。

① 網路交互可否延後

微博客戶端某個版本啟動時,從閃屏載入到timeline界面需要6秒+。這樣的體驗是無法接受的,與用戶2秒以內的等待容忍度是背道而馳的。從技術角度去分析,很容易發現問題,諸如我們在啟動時有10+個並發的網路請求(因為是HTTP短鏈接,意味著10+個並發的網路鏈接)、閃屏載入、主UI創建、本地配置載入、本地持久化數據載入至Cache等等程序行為,優化的目標很自然就集中在網路請求和本地配置、持久化數據載入上。

梳理並發網路請求,可以從以下三個方面考察:

  • 1) 哪些請求是要求實時拉取的,比如timeline & 提及 & 私信的數字、身份校驗;
  • 2) 哪些請求是可以非同步拉取的,比如timeline、用戶Profile、雲端配置、雙向收聽列表、閃屏配置、timeline分組列表、相冊tag列表等;
  • 3) 哪些請求是可以精簡或合併的,比如timeline & 提及 & 私信的數字與身份校驗合併。

此時,取捨就非常簡單和清晰了,啟動時1~2個網路請求足夠應對。所做的僅僅是把一些請求延後發起,這是一種非同步機制。

在移動APP裡面還有大量類似的場景,比如用戶更新了APP的某個設置項或者自己Profile的某個欄位,是停在界面上轉菊花等網路交互返回後再提示結果,亦或是把界面交互馬上還給用戶,延後非同步向伺服器提交用戶請求,這裡面的價值取向不同,「快」感也便不同。

② 網路內容可否預先載入

微博客戶端在timeline刷新時,用戶向上快速滑屏,到達一個邏輯分頁(比如30條微博消息)時,有兩個取捨,一是提前預載入下個分頁內容並自動拼接,給用戶無縫滑動的體驗;二是等到用戶滑動到達分頁臨界點時現場轉菊花,卡不卡看當時的網路狀況。實踐中選擇了方案一。用戶在滑動瀏覽第一個邏輯分頁時,APP就利用這個時間窗主動預先拉取下一個邏輯分頁的內容,使得用戶能享受一個順暢的「刷」的體驗。

所做的僅僅是把一個請求提前發起了,這也是一種非同步機制。思考的要點是:

  • 1) 預先載入的內容是用戶預期的嗎,預先載入和自動下載之間,失之毫釐謬以千里;
  • 2) 預先載入的內容對用戶移動設備的資源(比如流量、電量等)和後端伺服器的資源(比如帶寬、存儲、CPU等)消耗要做好估算和判斷,體貼和惡意之間,也就一步之遙;
  • 3) 預先載入區分輕重數據,輕數據可以不區分網路狀況,重數據考慮僅限優質網路下執行,最好這些策略雲端可以控制;
  • 4) 預先通過網路拉取載入或存儲的過程中,不要打攪用戶的正常使用。

在移動APP中,預載入有大量的實踐,比較典型的就是升級提醒,大家都採用了先下載好升級包,再提示用戶有新版本的策略,讓你順暢到底。

③ 用戶體驗可否降級

微博客戶端在香港公共WIFI下刷新timeline總是失敗,通過後台用戶接入請求和響應日誌分析,判斷是香港IDC到香港公共WIFI的匯介面帶寬窄、時延大,此時該如何應對。

從前面探討的TCP/IP網路知識,可以知道,在一個窄帶寬高時延網路中,吞吐量BDP必然很小,也就是說單位大小的數據傳輸所需的時間會很長。如果按照通常一次下發一個邏輯分頁timeline數據的策略,那麼從伺服器到客戶端傳輸,整個數據需要拆分成多個TCP數據報文,在緩慢的傳輸過程中,可能一個數據報文還未傳輸完成,客戶端的鏈路就已經超時了。

如果在弱網路(需要在應用層有測速機制,類似TCP/IP的RTT機制,測速時機可以是拉取微博消息數字時)下,把邏輯分頁的微博消息數由30調整為5會如何,如果方案成立,用戶刷微博的體驗是不是會下降,因為滑動一屏就要做一次網路交互,即便是配合預載入,也可能因為網路太慢,操控太快而又見菊花。外團在香港實測了這個版本,感嘆,終於可以刷了。

在饑渴難耐和美酒佳肴之間,似乎還有很多不同層級的體驗。聊勝於無,這個詞很精準的表述了服務分層,降級取捨的重要性。思考的要點是:

  • 1) 產品的核心體驗是什麼,即用戶最在乎的是什麼,在做宏觀分層設計時要充分保障核心體驗;
  • 2) 每個產品交互界面中,什麼數據是無法容忍短時間不一致的,即什麼是用戶不能容忍的錯誤,在做微觀分層設計時要充分考慮正確性;
  • 3) 在宏觀和微觀分層的基礎上,開始設想在什麼條件下,可以有什麼樣的降級取捨,來保障可用,保障爽快的體驗;
  • 4) 分層不宜太多太細,大部分產品和場景,3層足矣。

在移動弱網路條件下,處處可見降級取捨的案例。比如網路條件不佳時,降低拉取縮略圖的規格,甚至乾脆不自動拉取縮略圖等等,分層由心,降級有意。

④ 端和雲孰輕孰重

移動APP時代,絕對的輕端重雲或者輕雲重端都是不可取的,只有端雲有機的配合,才能在一個受限的網路通道上做出更好的用戶體驗。正所謂東家之子,胖瘦有致。

比如移動網遊APP,如取向選擇輕端重雲,那麼玩家的戰鬥計算就會大量的通過網路遞交給伺服器處理並返回,卡頓家常便飯,操控感盡失。

比如微博客戶端,如果取向選擇重端輕雲,微博timeline所有的消息都拉取元數據(比如微博正文包括文字、各類URL、話題、標籤、@、消息的父子關係、消息中用戶profile、關係鏈等等),由客戶端實時計算拼裝,不但客戶端用戶需要消耗大量流量計算量,而且給後端伺服器帶來巨大的帶寬成本和計算壓力,如果過程中網路狀況不佳,還會非常卡頓。

通過實踐總結,端和雲孰輕孰重,取捨的關鍵是在數據計算規模可控和數據安全有保障的前提下:

  • 1) 減少網路往複,要快;
  • 2) 減少網路流量,要輕。

端雲有機結合,可以很好的演繹機制與策略分離的設計思想,從而使系統具備足夠的柔韌性。

不得不再次特別提到的一點是,緩存技術是非同步化的基礎,它滲透在性能和體驗提升的方方面面,從持久化的DB、文件,到短周期的內存數據結構,從業務邏輯數據,到TCP/IP協議棧,它無所不在。緩存涉及到數據結構組織和演算法效能(耗時、命中率、內存使用率等)、持久化和啟動載入、更新、淘汰、清理方案等,有機會我們可以展開做專題的介紹。牢記一個字,緩存是讓用戶爽到極致的利器,但千萬別留下垃圾。

提倡多非同步,實際上是要求團隊認真審視產品的核心能力是什麼,深入思考和發現什麼是用戶最關心的核心體驗,把有限的資源聚焦在它們身上。通過考察用戶使用產品時的心理模型,體驗和還原用戶使用場景,用追求完美的精神探索不完美之道。

互聯網服務核心價值觀之一「不要我等」,在移動互聯網時代仍應奉為圭臬,如何面對新的挑戰,需要更多的學習、思考、實踐和總結,這篇文章即是對過去實踐的總結,亦作為面對未來挑戰的思考基點。

老子曰過:上士聞道,勤而行之;中士聞道,若存若亡;下士聞道,大笑之。不笑不足以為道。求求你了,笑一個。

知易行難,故知行合一似(jiu)為扯蛋,那麼我們就且扯且珍惜吧。

(上篇看了嗎?沒看請戳這裡:《移動端IM開發者必讀(一):通俗易懂,理解移動網路的「弱」和「慢」》)

附錄:更多計算機網路方面的資料

《TCP/IP詳解-第11章·UDP:用戶數據報協議》

《TCP/IP詳解-第17章·TCP:傳輸控制協議》

《TCP/IP詳解-第18章·TCP連接的建立與終止》

《TCP/IP詳解-第21章·TCP的超時與重傳》

《技術往事:改變世界的TCP/IP協議(珍貴多圖、手機慎點)》

《通俗易懂-深入理解TCP協議(上):理論基礎》

《通俗易懂-深入理解TCP協議(下):RTT、滑動窗口、擁塞處理》

《理論經典:TCP協議的3次握手與4次揮手過程詳解》

《理論聯繫實際:Wireshark抓包分析TCP 3次握手、4次揮手過程》

《計算機網路通訊協議關係圖(中文珍藏版)》

《UDP中一個包的大小最大能多大?》

《P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介》

《P2P技術詳解(二):P2P中的NAT穿越(打洞)方案詳解》

《P2P技術詳解(三):P2P技術之STUN、TURN、ICE詳解》

《通俗易懂:快速理解P2P技術中的NAT穿透原理》

《高性能網路編程(一):單台伺服器並發TCP連接數到底可以有多少》

《高性能網路編程(二):上一個10年,著名的C10K並發連接問題》

《高性能網路編程(三):下一個10年,是時候考慮C10M並發問題了》

《高性能網路編程(四):從C10K到C10M高性能網路應用的理論探索》

《不為人知的網路編程(一):淺析TCP協議中的疑難雜症(上篇)》

《不為人知的網路編程(二):淺析TCP協議中的疑難雜症(下篇)》

《不為人知的網路編程(三):關閉TCP連接時為什麼會TIME_WAIT、CLOSE_WAIT》

《不為人知的網路編程(四):深入研究分析TCP的異常關閉》

《不為人知的網路編程(五):UDP的連接性和負載均衡》

《不為人知的網路編程(六):深入地理解UDP協議並用好它》

《不為人知的網路編程(七):如何讓不可靠的UDP變的可靠?》

《網路編程懶人入門(一):快速理解網路通信協議(上篇)》

《網路編程懶人入門(二):快速理解網路通信協議(下篇)》

《網路編程懶人入門(三):快速理解TCP協議一篇就夠》

《網路編程懶人入門(四):快速理解TCP和UDP的差異》

《網路編程懶人入門(五):快速理解為什麼說UDP有時比TCP更有優勢》

《技術掃盲:新一代基於UDP的低延時網路傳輸層協議——QUIC詳解》

《讓互聯網更快:新一代QUIC協議在騰訊的技術實踐分享》

《現代移動端網路短連接的優化手段總結:請求速度、弱網適應、安全保障》

《聊聊iOS中網路編程長連接的那些事》

《移動端IM開發者必讀(一):通俗易懂,理解移動網路的「弱」和「慢」》

《移動端IM開發者必讀(二):史上最全移動弱網路優化方法總結》

>>更多同類文章 ……

(本文同步發佈於:52im.net/thread-1588-1-


推薦閱讀:

TAG:Android開發 | 開發者 | 移動應用 |