本文由得物技術厲飛雨、GavinX分享,原題“得物App白屏優化系列|網絡篇”,下文進行了排版和內容優化。
1、引言
圖片加載作為重中之重的App體驗指標,端側的白屏問題則是其中最為嚴重、也是最為常見的問題之一。想象一下如果你在瀏覽交易商品、社區帖子等核心場景下,圖片無法完成加載是多么糟糕的體驗。
如上圖所示,通過線上白屏問題歸因,我們看到網絡問題導致比例最高,占比達81.97%。除去常見的弱網/無網等問題外,還有很多各種各樣的網絡環境問題我們是可以進行優化的,例如設備不支持IPv6,CDN節點異常,證書超時等。
本文將要分享的是得物技術團隊針對移動端最常見的圖片加載導致的端側白屏問題,而進行的的移動網絡方向的技術優化實踐,希望能帶給你啟發。
2、網絡優化和監控概覽
網絡優化與監控體系:
3、網絡監控能力
3.1概述
網絡異常導致的圖片白屏問題,往往和當時的環境有關,例如用戶連的WIFI不支持IPv6但是DNS返回的大部分是V6的IP等,此時僅靠幾條帶有SocketTimeoutException的網絡日志根本無法排查出問題的根因,就如同ANR問題需要火焰圖一樣,白屏問題同樣需要一套完善的監控體系來記錄問題現場的信息,從而分析問題根因。
3.2OkHttp基礎監控
圖片網絡請求的階段信息,可以通過實現OkHttp自帶的EventListener來獲取,在各個階段開始和結束點記錄下時間戳即可。
其中需要重點關注connectFailed、requestFailed和responseFailed這三個失敗的回調,他們都會在失敗之后重試,并重復執行階段開始的回調(例如connectStart),因此針對這些需要單獨記錄好每一次失敗信息,避免被覆蓋。
例如某個圖片請求TCP建連失敗的記錄:
3.3APM流量監控
盡管我們有專門的網絡診斷工具,但是考慮到網絡異常的滯后性,故障往往是數十秒之前引入的(例如進電梯弱網),而發生問題之后才觸發的網絡診斷結果僅能作為參考,并不能作為最終問題歸因的證據。
因此我們需要一個更直觀的表達用戶設備網絡狀況的數據,那就是通過系統API獲取的當前App流量消耗,在排除了一些本地socket通信產生的干擾之后,最近N秒之內消耗的流量/N就可以認為是白屏問題發生時的網速。
我們以3秒為間隔分段計算并取最大值來排除波動,這樣我們就得到了白屏問題發生前一段時間內的網速狀態,過低的就可以判定為弱網/無網。
網速計算示意圖:
3.4CDN異常記錄
CDN廠商側的異常,例如服務器IP跨省,節點掛了等問題相對少見,在網絡日志中通常就表現為建連超時但是網絡正常,因此需要收集多個Host的請求記錄進行橫向對比來確認是單個CDN廠商的問題。
為此我們記錄了主站接口的域名,以及所有CDN域名在最近N分鐘內的請求統計,包括了正常請求、慢請求、失敗請求的數量和原因,以及失敗請求使用的具體IP。
例如圖中可以看到某CDN的請求全部都是建連失敗,而其他CDN以及我們主站域名請求均正常,那么此問題可以歸類為該CDN的單點問題,聯系CDN服務商剔除該IP所在節點即可解決問題。
3.5網絡庫請求緩存
常規的網絡監控方案為了節省內存,會在請求結束時將其從內存緩存隊列中移除,而白屏問題因為其滯后性,只有在屏幕中大面積出現白圖才會被判定,此時再去查詢加載失敗的圖片網絡請求日志自然早已被移出隊列,因此需要單獨使用一個LRU隊列來緩存最近N條網絡日志,圖片庫同理。
3.6網絡診斷工具
在逐步解決用戶問題的過程中,我們研發了網絡診斷工具,實現了基礎網絡信息采集、Ping、TraceRoute等。
當檢測到用戶發生白屏時自動觸發網絡診斷:

1)單域名診斷流程:我們會依次進行DNS、Http、Ping(ICPM)與Ping(TCP)、TraceRoute診斷。如果是雙棧客戶端(同時返回了IPv4與IPv6),那么我們會選擇首個IPv4與首個IPv6同時進行Ping/TraceRoute以確認不同IP類型的聯通情況。

2)多域名診斷流程:同時對3個得物核心域名發起診斷。為了輔助判斷,當得物核心域名診斷失敗時,也會對一些主流三方域名稍微Ping一下。
3)診斷數據的上報:由于網絡診斷可能耗時10秒以上,而如此長的時間內用戶如果退出App那我們的診斷數據可能就丟失了。因此,任意一個小階段的診斷完成時,我們就立即上報此結果,以避免此類情況的發生。
4、CDN質量監控
4.1概述
CDN作為整體白屏問題中重要的一環。在白屏排查過程中,經常遇到接口正常,但CDN訪問異常的case。而多云CDN的質量問題往往依賴于云廠商,如何衡量和監控云廠商的CDN情況成為了關鍵,故我們自定義了端側的CDN質量指標體系以及配合云廠商推進云端策略的優化。
4.2CDN質量大盤
在白屏監控平臺側,我們經常能夠發現用戶側的CDN單點問題,往往存在跨省、跨運營商調度的情況,或者是單節點的高負載響應超時。故我們基于此情況記錄了端側的本地IP&CDN遠端IP,建設了CDN質量大盤。
核心指標如下:

依賴CDN質量大盤,我們可以橫向、縱向對不同云廠商服務進行CDN質量評估工作,由于端側實際發起的總請求數量統計成本高、到CDN側的請求存在丟失等情況,我們采用了端側采樣上報、統計還原的思路,將得物側的整體CDN指標大盤采樣放大比例為1 / 千分之三(當前采樣比例)。過程指標的確會收到采樣用戶質量的影響,但整體CDN趨勢、各廠商CDN的橫向對比還是有很大的參考意義的。
平臺截圖展示:
如果說端側CDN指標是對CDN質量抽象評估的長周期指標,那么云端監控則是CDN質量的精確告警的短時效指標。
這部分就是一些較為常見的策略與手段了,如CDN節點撥測、CPU負載監控、異常錯誤碼監控等,并協同SRE團隊將其同飛書通知打通,實現及時告警。
4.3.1)節點撥測:
CDN節點撥測整體指定的圖片撥測閾值當前限制在200ms。


4.3.2)節點監控:
關于到節點后的請求監控,主要依賴于云廠商的監控能力,我們也協同云廠商對核心指標的告警閾值等進行了調優,核心主要為:

此處思考一個問題,如果建連請求無法抵達CDN節點,那么即使CDN廠商開啟了TCP級日志,也無法監測到此異常,也就無法及時進行調度調整。
故對于非到節點的建聯問題,基于TCP建連進行監控是很有必要的,我們基于客戶端建連的監控數據,以用戶網絡環境(網絡運營商,地域)視角,分析建連過程是否正常,及時發現異常連接線路,并通過推動CDN服務商調整區域DNS返回來恢復正常。
監控指標如下:
建聯失敗、異常率:

建聯p50、p99耗時分布:
5、網絡問題優化治理1:DNS策略優化
5.1概述
從網絡監控、白屏監控中我們可以清楚的觀察到DNS錯誤是網絡階段導致白屏的最大因素。此外,DNS錯誤是所有網絡錯誤中最多的,甚至可以占比80%以上。
連續DNS錯誤導致白屏:
5.2LocalDNS行為
為什么DNS階段如此脆弱,是我們使用不當,還是其本身性能如此?我們首要做的是觀察下Android平臺下LocalDNS的行為邏輯以確認性能較差的原因。
5.2.1)按照TTL緩存:
通過不停的向系統進行查詢DNS,在抓包中,我們很容易觀察到DNS的查詢邏輯。其中一個重要參數是TTL(Time to Live),這個參數表明當前查詢結果的有效時間。比如說TTL=7,表明在7秒鐘內當前結果是有效的、可被緩存的,即使7秒鐘內再次查詢,依然是此結果。
Android平臺嚴格遵守了此協議,按照TTL進行緩存。而較短的有效時間,也意味著我們需要進行域名查找時(冷熱啟動),大概率系統內沒有緩存可用。
DNS TTL與查詢頻率:
通常TTL設置為120秒以內,這是因為要考慮到節點故障,能夠快速切換。而到端側查詢時,TTL會在0~120之間。低于120是因為我們查詢此結果的DNS server自身已經緩存了一定的時間。
5.2.2)雙棧客戶端同時查詢:
雙棧客戶端上,同時發起A類(IPv4)、AAAA類(IPv4)查詢。
某運營商5G網絡下DNS查詢及響應:
當兩類查詢都成功響應時,Android會將兩類結果組合起來排序后返回給應用層。無論是A類地址先返回,還是AAAA類地址先返回,Android都會將IPv6地址放在前面(劃重點,后面要考)。
-- app.dewu.com 的DNS查詢結果
[app.dewu.com/2405:e000:1004:1::f3ff:4b49, app.dewu.com/203.107.32.125]
可以看到電信的LocalDNS服務器并未遵守TTL時間(app.dewu.com 的 TTL為60),而是進行了額外的緩存。這是部分運營商為了降低客戶端的查詢頻率(降成本)而搞出的小把戲,不在我們的討論范圍內。
5.2.3)DNS數據包錯誤與重試:
我們將DNS服務器設置為一個虛假的DNS服務器,來觀察Android平臺在DNS未返回時的重試邏輯。
DNS重試(主、備、隱藏款):
可以看到,0秒時向主DNS服務器發起查詢,5秒向備DNS服務器發起查詢,8秒向114發起查詢。
現在我們想一下,在我們DNS查詢時,只要0秒的第一次查詢向主沒有正常返回(比如丟包),即使備選正常,能成功查詢到結果也是5秒鐘后的事情了,而這意味著用戶在5秒內無法正常的請求網絡。然后,用戶熟練的打開設置、點擊用戶反饋、選擇白屏、輸入些表達欣喜的文字、提交!(嘿,來活了)
那么我們能否通過多次查詢來觸發系統來同時發起多個查詢請求呢?
不行。當前Android內有一個同步柵欄,多個線程同時查詢一個Host,只會允許一個通過。如果成功時,全部被阻攔的查詢一同返回結果;如果失敗時,再允許一個查詢。也就是說,在這5秒內,我們只能靜靜的等著,讓用戶也等著。
主/備DNS服務器我們可以通過在設置中修改,而114這個隱藏款只出現在部分國產品牌上,不同品牌內置的隱藏備用DNS服務器也并不相同。
5.2.4)iOS平臺的DNS行為:
眾所周知,iOS平臺下的DNS異常率遠遠低于Android,甚至差了一個數量級。
iOS為何如此優秀?
- 1)較長的緩存時長:當DNS的TTL較低時(比如3秒),iOS會忽略TTL值,直接緩存1分鐘以上;
- 2)積極的重試邏輯:只要1秒內未收到響應立即進行多次重試,其中主DNS 6次,備DNS 8次。
5.3DNS優化
通過對LocalDNS的行為分析,我們看到Android平臺下的LocalDNS查詢極其不可靠,好在我們并非只能通過LocalDNS來解析IP。任何將域名轉換為IP的手段都可以使用,比如通過http獲取、磁盤緩存等。
在LocalDNS的基礎上,我們增加了HttpDNS,磁盤緩存來優化DNS問題。其中HttpDNS在第60ms異步啟動,磁盤緩存在第6秒鐘時異步啟動。三種查詢方式任意一個返回,則DNS結束。

5.3.1)HttpDNS:
我們實現Http進行DNS查詢,并將其作為備用DNS使用。在LocalDNS 60ms未成功返回時,異步啟動HttpDNS進行查詢。這將能解決LocalDNS下主DNS失敗5秒后才會進行下一次重試的弊端。
HttpDNS的實現方式,我們暫時選擇接入成熟的三方HttpDNS,快速解決用戶在DNS方面的困境是主要原因之一。
當然,也并非全部域名通過HttpDNS查詢都會有效果,比如localhost、IP地址等就需要從HttpDNS的查詢中排除。
同步柵欄:當存在單個host的多個查詢線程時,僅允許第一個線程查詢,其他全部等待查詢結果。(類似于Android系統對多線程同時查詢時的處理)
5.3.2)磁盤緩存:
DNS查詢成功時,我們將查詢結果緩存到磁盤上。在LocalDNS、HttpDNS都未如期返回時,將磁盤上緩存的結果返回給應用層。如此以來,只要歷史上這個用戶查詢成功過,我們始終不會失敗在DNS階段。
需要注意的是:在磁盤上緩存IP時,需要按照網絡類型、運營商進行緩存。原因是不同網絡下的最佳IP往往不同,比如同一個CDN域名中國移動的CDN節點與中國電信的CDN節點會在各自的機房里。如果我們不分開存儲,那么用戶網絡切換后,我們磁盤緩存出的IP或許可用,但可能不是最佳(延遲更高)。
此外,我們的磁盤緩存也完全忽略了DNS TTL。原因是我們將磁盤緩存作為其他DNS查詢全部失敗后的兜底手段使用,而非主要手段。即使TTL過期的IP可能會存在一些問題,但也不會有更壞的結果了。
5.2.3)收益情況:
通過線上實驗觀察到,DNS異常率降低了60%以上。
不同配置下的DNS異常率對比:
6、網絡問題優化治理2:IPv6故障修復
6.1概述
前文提到對于雙棧客戶端,Android會將IPv6地址放在前面返回給應用層。本意上,IPv4地址即將枯竭而向IPv6過渡的方式,而這恰恰是Android平臺在TCP建連階段時遇到的最大問題。
用戶A-DNS結果:

我們來看一個例子:用戶A訪問 cdn.poizon.com 是DNS共響應了10個IPv6、10個IPv4,然而此用戶IPv6無法訪問,且給個IPv6都是建連10秒后超時,最終導致用戶在第100秒時才在第11個IPv4地址上成功。
RFC6555 摘要:

正如 RFC6555 摘要中描述的那樣,當服務器的 IPv4 路徑和協議正常,但IPv6 路徑和協議不工作時,相對于IPv4單棧客戶端,雙棧客戶端會經歷顯著的連接延遲。而這個延遲通常是10秒以上(IPv6的個數和建連超時時間的乘積)。
那么如何優化?
最簡單的方式是禁用IPv6,簡單到甚至只需要運維動動小手即可。但是由于眾所周知的原因,我們國內對于推動IPv6十分積極,對于IPv6的濃度動不得。
最合理的方式自然是按照RFC6555描述的那樣實現快樂眼球算法,但是開發成本和風險都較大。而線上時而有類似反饋。因此我們決定分兩步走,先上線低風險的IPv6探測&重排序解壓此困境,再實現快樂眼球算法。
OkHttp5.x 已支持RFC6555,但是目前尚未正式發布。且得物歷史上對源碼的修改較多,整體遷移到5.x成本較高。
6.2IPv6探測 & 重排序
此實現的思路是既然IPv6可能故障,那么提前探測潛在故障。通過調整即將建連的IP列表的順序來解決客戶端建連到IPv4的延遲問題。
此實現在DNS查詢成功之后,建連之前。因為不涉及修改OkHttp源碼修改所以風險可控。
1)IPv6探測:如果IP列表中同時包含IPv6&IPv4,則對第一個IPv6地址同步進行Ping探測。如果250ms內探測成功或曾經探測成功,則認為IPv6正常。
2)重排序:如果IPv6暢通,則按照IPv6優先進行交叉排序。如果IPv6故障,則按照IPv4優先進行交叉排序。
# 重排序前
[
"imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:13",
"imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:10",
"imagex-cdn.dewu.com\/183.240.183.19",
"imagex-cdn.dewu.com\/183.240.183.15"
]
# 重排序后(IPv6探測失敗)
[
"imagex-cdn.dewu.com\/183.240.183.19",
"imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:13",
"imagex-cdn.dewu.com\/183.240.183.15",
"imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:10"
]
# 重排序后(IPv6探測成功)
[
"imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:13",
"imagex-cdn.dewu.com\/183.240.183.19",
"imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:10",
"imagex-cdn.dewu.com\/183.240.183.15"
]
IPv6探測 & 重排序 流程圖:
6.3快樂眼球(HappyEyeball)
當前DNS返回的IP地址數量超過一個時,每250ms異步啟動一個新的TCP建連,任意一個TCP建連成功,則斷開其他TCP并開始同步TLS建連。
- 1)TLS 建連成功則返回此Connection,流程結束;
- 2)TLS 建連失敗時,如果是可恢復的TLS失敗(部分SSLException),則立即重試;
- 3)如果是不可恢復失敗,則再次啟動其他TCP的競速。
核心修改點是原流程的建連部分。原流程中,在網絡線程中同步的、依次進行TCP、TLS建連。新流程中,將TCP與TLS建連分開,競速建連TCP,TCP建連完成后建連TLS。
1)OkHttp3.x 建連流程:

注:OkHttp3.x 的nextRoute之間的循環在RetryAndFlow實現
2)OkHttp-HappyEyeball建連流程:

具體實現方式可以參考OkHttp5.x 代碼。
收益情況:自IPv6優化上線后,再無相關IPv6相關用戶反饋。
7、CDN質量問題治理概述
從白屏監控大盤中可以看到,CDN的問題可能只占據1%~2%,但是不同于通用網絡問題,用戶憑持著其他App可以用,但是得物App為什么白屏的疑問,是極容易引起線上反饋的一種case。
故我們也是協同SRE團隊、云廠商一起做了一些優化手段,力爭在現有條件下將端側的圖片CDN質量調整到最優。
8、CDN質量問題治理1:返回IP策略優化
從網絡優化來看,DNS優化以及IPv6探測&重排序為我們解決了雙棧IPv6問題、單IP不可用等問題,但CDN問題本身由于不同省份、不同區域等節點數量、網絡硬件質量均存在差異,這部分是端側無法彌補的,故我們想到了優化CDN返回IP策略的思路來規避以上問題。
返回IP優化策略:

過程中考慮到得物IPv6濃度問題,最早期是返回了3個v4 IP、3個v6 IP的策略,但發現v6 IP數量變多后,由于LocalDNS天然會把v6 IP排放在v4 IP前面。此舉在沒有建連競速優化的老版本下,會導致v6不通的建連時間被逐步拉長30s。故我們在盡可能保證v6濃度的情況下,將返回的v6 IP數量降低到了2個。
由于v6 IP優先請求的情況,我們考慮優先保證v6 IP的本省同大區覆蓋,故對v6 IP的本省的大區返回粒度會比v4 IP更細些。
跨省調度問題:
- 1)針對v4 IP本省2個大區至少返回2個IP;
- 2)針對v6 IP本省1個大區至少返回1個IP。
單ip返回、v4不返回問題:
- 1)針對v4 IP至少返回3個;
- 2)針對v6 IP至少返回2個。
9、CDN質量問題治理2:云廠商優化策略
為了保證圖片CDN的節點質量,我們積極同CDN廠商進行溝通協作,進行了部分優化嘗試。
9.1圖片專屬節點
將原有云廠商的圖片、視頻、文件等通用節點,遷移至圖片專屬的L1節點,最大程度保證圖片請求穩定性和效率。
9.2H2協議棧優化
過去發現請求耗時增大的一個case是H2弱網阻塞,在低質量的網絡環境下使用HTTP/2協議時可能遇到的性能問題或阻塞問題。
云廠商均多次調整了H2協議棧的算法優化邏輯,并對比了優化前后的慢請求監控情況。
觀察調整省份節點水平,調整后平均耗時下降15%、慢請求率下降23%左右。
請求耗時大于5000ms的優化情況:

9.3節點動態剔除細化
我們針對云廠商的到端鏈路監控以及白屏用戶反饋的潛在非到端問題,通過平臺歸因1min內的CDN請求 & 網絡診斷的情況,并經過人工確認為CDN單通問題后,進行節點剔除切換,優先處理節點抖動帶來的影響面問題。
具體是:
- 1)CDN節點的可用性探測判斷節點持續抖動并最終剔除的時效在15min內;
- 2)白屏反饋用戶的sop應急響應剔除時效從2~3小時降低到30min內。
10、 CDN質量問題治理3:CA證書問題
10.1概述
除了建連階段的問題外,TLS等證書認證問題上也有較多的用戶反饋,同時白屏平臺也有較多類似的錯誤。


歷史為了優化SSL耗時過長、證書認證安全等問題,接口、圖片域名均開啟了ocsp的功能,但實際監控發現部分用戶存在本地時間不對齊的情況,即超過了CA證書 ocsp校驗的有效期(一般7天) 或者超過了域名的證書有效期(一般2年)。此類用戶會導致所有圖片請求均被證書攔截而無法完成請求最終引發白屏。
10.2ocsp問題
主要是:
- 1)TLS耗時開啟前后并無實際收益,對于網絡耗時的優化可忽略不計;
- 2)證書的安全性問題,由于圖片域名訪問為CDN資源非接口核心信息,關閉風險可控;
- 3)對比主流站點的ocsp開啟情況,主流App的圖片域名大多未開啟。
ocsp的當前時間和下次更新時間:

故我們最終關閉了圖片域名的ocsp功能,整體請求異常數、請求耗時并無劣化且圖片ocsp的問題反饋數逐步清零。
10.3域名CA證書替換失效問題
得物目前有100+的域名,CA證書的申請對于CA機構來說,一般在證書有效期小于30天時會進行新證書的簽發流程,且證書有效期以申請期時間為準。但過往的證書有效期更新替換后,往往會面臨時間前置的用戶無法通過CA證書校驗,最終引起用戶白屏。

我們協同運維側制定了較為嚴格的圖片域名證書更新時效期,并在多個主域名按此標準落地。

具體是:
- 1)證書30天到期自動告警能力,及時跟進證書審批事項;
- 2)證書替換選擇在有效期提前3天,避免無規律的申請即更新的情況。
11、 本文小結
網絡問題作為用戶白屏的主要原因之一,如果不能及時針對性的優化將對大量用戶產生困擾。
我們在以解決用戶白屏為目標的行動中,逐步完善了客戶端網絡監控、云端CDN監控。并在此過程中完成DNS、建連、證書、運營商調度等方面的優化,杜絕了某些特定錯誤的發生,保障了更多用戶的網絡體驗。
近期我們會繼續推出一篇介紹如何在客戶端監控白屏問題,以及平臺如何對白屏問題自動化歸因的文章,敬請期待。
12、 參考資料
[1] 網絡編程懶人入門(十一):一文讀懂什么是IPv6
[2] IPv6技術詳解:基本概念、應用現狀、技術實踐(上篇)
[3] 網絡編程入門從未如此簡單(三):什么是IPv6?漫畫式圖文,一篇即懂!
[4] 不為人知的網絡編程(九):理論聯系實際,全方位深入理解DNS
[5] 移動端IM開發者必讀(一):通俗易懂,理解移動網絡的“弱”和“慢”
[6] 淘寶移動端統一網絡庫的架構演進和弱網優化技術實踐
[7] 得物自研移動端弱網診斷工具的技術實踐分享
[8] 全面了解移動端DNS域名劫持等雜癥:原理、根源、HttpDNS解決方案等
[9] 美圖App的移動端DNS優化實踐:HTTPS請求耗時減小近半
[10] 移動端網絡優化之HTTP請求的DNS優化
[11] 百度APP移動端網絡深度優化實踐分享(一):DNS優化篇
[12] 百度APP移動端網絡深度優化實踐分享(二):網絡連接優化篇
[13] 愛奇藝移動端網絡優化實踐分享:網絡請求成功率優化篇
[14] 美團點評的移動端網絡優化實踐:大幅提升連接成功率、速度等
[15] 騰訊原創分享(一):如何大幅提升移動網絡下手機QQ的圖片傳輸速度和成功率
[16] 騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇)
[17] iOS端移動網絡調優的8條建議
[18] 腦殘式網絡編程入門(五):每天都在用的Ping命令,它到底是什么?
13、 得物技術團隊的其它文章匯總
得物從0到1自研客服IM系統的技術實踐之路
得物自研客服IM中收發聊天消息背后的技術邏輯和思考實現
得物從零構建億級消息推送系統的送達穩定性監控體系技術實踐
得物基于Electron開發客服IM桌面端的技術實踐
得物自研移動端弱網診斷工具的技術實踐分享
(本文已同步發布于:http://www.52im.net/thread-4700-1-1.html)