讀者好,前面咱們在 《Android 架構之網絡鏈接與加速》 和《Android 架構之長鏈接技術》兩篇文章中,講解了 Http 短鏈接、TCP 長鏈接、鏈接複用與速度優化、數據壓縮
等方面的知識點。不過,真實的網絡環境是很複雜的,存在各類各樣的因素會致使網絡服務不可用,好比 DNS 劫持、服務器宕機、弱網等。換言之,若是服務都不可用,那上面這些優化也就沒有意義了。緩存
所以,本文主要談一下在真實的網絡環境下,存在哪些常見的網絡不可用緣由,以及大多數公司是如何解決並兜底,從而達到 高可用鏈接
這個目標的。安全
文章會從下面幾方面進行闡述:服務器
咱們知道,大多數的網絡請求第一步就是 DNS 過程,通過 1-RTT 的時間將域名轉化爲 IP 地址,而後再去發起請求。可是,有相關經驗的開發者應該瞭解,DNS 過程不只耗時不穩定(3G 下 200ms,4G 下 100ms),並且可能解析失敗,甚至被劫持,將用戶導入到了錯誤的 IP 地址。若是攻擊者本身作一個仿冒的網站,劫持你的 DNS 並將 IP 轉到這個假網站上,可能會形成很大的用戶數據泄漏和公司品牌損失。網絡
爲了解決這個問題,得到可靠的 IP 列表,現有大廠會採用下面一些方案:架構
好比阿里雲和騰訊雲都推出了本身的 HttpDNS 服務,在全國多地部署相關的服務器提供安全解析 DNS 服務。併發
基本的原理就是經過發起 Http 請求到 HttpDNS 服務器,獲取某個域名對應的可用 IP 列表。這個 IP 列表能夠根據用戶當前的地點進行返回,並且默認會進行 IP 測速,按速度排序。同時,伴隨這 IP 列表,服務器還會下發一個緩存有效時間 TTL,有了這個時間,客戶端能夠放心的將 IP 列表緩存在本地,並在即將過時前及時去更新 IP 列表,保證每次網絡請求均可以使用 當前最優 的 IP 地址。負載均衡
固然,自建 HttpDNS 服務須要必定規模的機房部署、大量的客戶端測速數據上報、全球 IP 庫收集等,須要很多的投入。所以,有些公司好比 攜程 就採用了更加輕量一點的方案:內置 IP 列表
。異步
具體原理以下:學習
在 APK 打包時會內置一份 IP 列表進去。當 App 啓動時,這些 IP 的權重相同,此時會隨機從裏面獲取 IP 來使用。可是這有個問題,對不一樣地區的用戶而言,最優 IP 確定是不一樣的。好比對於上海的用戶而言,上海區服務器的 IP 確定是最快的,而對於深圳的用戶而言,華南區 IP 纔是最快的。所以,在 App 運行過程當中,咱們會經過依次對 IP 列表逐個進行Ping 測速,根據測速結果動態變動 IP 的權重,而後提供給網絡鏈接使用。測試
經過 HttpDNS 或內置 IP 列表
的方案,咱們能夠爲網絡層提供一份相對可靠的 IP 地址做爲緩存,每次須要發起請求時,直接從緩存裏讀取這份 IP 列表便可創建 IP 直連。
那新的問題來了,移動網絡是在不斷變化的。最多見的場景,好比咱們從 Wi-Fi 切換到了 4G,獲取進入電梯後從 4G 降級成 3G,或者咱們從 A Wi-Fi 換到了 B Wi-Fi,這都意味着咱們的 網絡鏈路變動 了。那麼,以前緩存的 IP 列表是否仍然可用,或者仍然最優呢?
顯然並不必定,好比從 Wi-Fi 切到了移動 4G,背後整條網絡鏈路都不一樣了,以前的 IP 列表頗有可能不是最優的了,極端狀況下可能某些 IP 地址也不可用了。所以,咱們須要最好 IP 列表的及時更新,保證不管網絡如何切換,咱們都能使用最優的 IP 地址列表。
具體有下面幾種方式:
TTL 過時時間
。當 IP 列表即將過時前,發起請求獲取下一輪的 IP 列表並進行更新;另外,IP 列表緩存應該對不一樣網絡類型、網絡標識有對應的一份緩存,可使用 網絡類型(3G、4G、Wi-Fi 等)+ 網絡標識(SSID、ispCode 等)
做爲 緩存 Key
,當網絡切換時,使用 Key 去查詢緩存。
這些緩存能夠持久化到多個文件,以 Key 做爲文件名,同時能夠基於當前網絡狀態,緩存一份 IP 列表到內存供使用,當網絡狀態變化,則刷新內存緩存。
經過更新機制,咱們能夠保證本地 IP 列表緩存動態更新的及時性。那麼,若是 HttpDNS 服務器出現故障呢,或者首次打開 App,HttpDNS 尚未完成,或者大面積 DNS 劫持等,怎麼辦呢?
因此說,除了及時獲取最優 IP 列表,咱們還要考慮,若是獲取不到 IP 列表,如何進行兜底?保證用戶的網絡請求不受影響。
在線上運行中,能夠採起下面四組 IP 兜底策略,按優先級排列以下:
前面兩種動態 IP 不用多說,你們都清楚,這二者能夠動態獲取 IP,效果最好。可是,若是發生故障,致使這兩個方案都不可用,好比大面積 DNS 劫持之類的,這時客戶端必須可以自動降級到 靜態兜底 IP
,保證網絡服務可用。
但這也可能存在一個問題,就是 靜態兜底 IP
對應服務器訪問量可能會忽然暴增,若是峯值過高可能形成更大的危害如 雪崩
。所以,除了內置靜態兜底 IP,還須要爲客戶端提供一個可經過 配置動態下發
的兜底 IP 列表
,能夠作到負載均衡,將流量分散到不一樣機器上。並且這些靜態 IP 貴精不貴多,而且要有高可用的後臺服務保證,做爲全局網絡服務的兜底。
經過上面的幾套方案,能夠保證用戶可以 高可用的獲取最優 IP 列表
,提升用戶訪問速度,並且能應對各類複雜的網絡狀態。
那麼如今考慮這樣一種狀況,上面的 IP 列表咱們可以正常的獲取,可是,用戶處於弱網狀態下,IP 鏈接成功率很低,怎麼辦呢?
針對弱網通常有兩種方式:
這兩種方案的缺點是:串行鏈接可能須要很長時間的試錯,才能找到可用的 IP,並且這裏還取決於如何選擇超時時間,若是超時時間較長,則須要很長時間才能找到可用 IP;若是很短,則可能會漏掉一些相對優質的 IP,不斷去嘗試新 IP,惡性循環;而並行鏈接則會對服務端形成極大的鏈接負載壓力和必定程度的浪費,對於電量也有必定程度開銷。
所以,這裏咱們介紹下 Mars 裏的複合鏈接策略
做爲學習參考:
在弱網狀態下,依次發起對 5 組 IP+Port 的鏈接,10s 做爲超時時間。當前一個鏈接發起了 4s 鍾還未成功,則當即發起下一個鏈接,以此類推。當其中有一個鏈接創建成功,則當即中止其餘鏈接。這樣的方式能夠兼備串行鏈接和並行鏈接的優點:較快找到可用 IP,同時對於服務器不會形成過大的鏈接壓力。至於這個超時時間 10s,則能夠經過上報數據來動態統計,找到一個合理的超時時間。
在真實的線上環境咱們發現,即便 IP 和後臺服務均有效,仍有一部分用戶的網絡鏈接會出現失敗。而此時單純從 IP 地址已經分析不出緣由,頗有多是該用戶的網絡鏈路上存在問題致使鏈接失敗。
這時就須要咱們主動去探測這個用戶的網絡鏈接並診斷整條鏈接鏈路。
所以,爲了準確瞭解線上網絡錯誤的用戶的真實狀況,咱們會在客戶端裏內置網絡診斷策略,經過 Ping
或者 TraceRoute
探測用戶手機到服務器的整條網絡鏈路上的狀況,並將數據存儲上報,用於分析用戶的真實網絡錯誤緣由。
Ping你們比較熟悉,目的是爲了測試另外一臺主機是否可達,向目標主機發送 Echo 包並等待回包;而 TraceRoute 能夠獲取數據包在 IP 網絡通過的路由器的 IP 地址,原理以下:
在 Android 上通常有兩種方式來實現這個診斷:
iputils
C 代碼的方式對 traceroute 進行了套接字發送 ICMP 報文模擬。感興趣的能夠參考文末提供的開源項目LDNetDiagnoService
,經過診斷能夠把日誌上報用於分析,並做出相關的調整和優化。
本文針對如何提升網絡鏈接的高可用性作了講解和分析,線上方案最重要考慮的就是兜底,不管發生何種問題,都要保證網絡服務可用。若是用戶連咱們的服務器都鏈接不上,那可能會帶來很是嚴重的災難;固然,咱們也要考慮服務器負載,不能形成服務器壓力過大,致使雪崩之類的問題。