17 | 網絡優化(下):大數據下網絡該如何監控?


通過上一期的學習,我們對如何打造一個高質量的網絡已經有了一個整體的認識。但是這就足夠了嗎?回想一下,一個網絡請求從手機到后台服務器,會涉及基站、光纖、路由器這些硬件設施,也會跟運營商和服務器機房有關。

不論是基站故障、光纖被挖斷、運營商挾持,還是我們的機房、CDN 服務商出現故障,都有可能會引起用戶網絡出現問題。你有沒有感覺線上經常突發各種千奇百怪的網絡問題,很多公司的運維人員每天過得膽戰心驚、疲於奔命。

“善良”的故障過了一段時間之后莫名其妙就好了,“頑固”的故障難以定位也難以解決。這些故障究竟是如何產生的?為什么突然就恢復了?它們影響了多少用戶、哪些用戶?想要解決這些問題離不開高質量的網絡,而高質量的網絡又離不開強大的監控。今天我們就一起來看看網絡該如何監控吧。

移動端監控

對於移動端來說,我們可能會有各種各樣的網絡請求。即使使用了 OkHttp 網絡庫,也可能會有一些開發人員或者第三方組件使用了系統的網絡庫。那應該如何統一的監控客戶端的所有的網絡請求呢?

1. 如何監控網絡

第一種方法:插樁。

為了兼容性考慮,我首先想到的還是插樁。360 開源的性能監控工具ArgusAPM就是利用 Aspect 切換插樁,實現監控系統和 OkHttp 網絡庫的請求。

系統網絡庫的插樁實現可以參考TraceNetTrafficMonitor,主要利用Aspect的切面功能,關於 OkHttp 的攔截可以參考OkHttp3Aspect,它會更加簡單一些,因為 OkHttp 本身就有代理機制。

@Pointcut("call(public okhttp3.OkHttpClient build())") public void build() { } @Around("build()") public Object aroundBuild(ProceedingJoinPoint joinPoint) throws Throwable { Object target = joinPoint.getTarget(); if (target instanceof OkHttpClient.Builder && Client.isTaskRunning(ApmTask.TASK_NET)) { OkHttpClient.Builder builder = (OkHttpClient.Builder) target; builder.addInterceptor(new NetWorkInterceptor()); } return joinPoint.proceed(); } 

插樁的方法看起來很好,但是並不全面。如果使用的不是系統和 OkHttp 網絡庫,又或者使用了 Native 代碼的網絡請求,都無法監控到。

第二種方法:Native Hook。

跟 I/O 監控一樣,這個時候我們想到了強大的 Native Hook。網絡相關的我們一般會 Hook 下面幾個方法 :

  • 連接相關:connect。
  • 發送數據相關:send 和 sendto。
  • 接收數據相關:recv 和 recvfrom

Android 在不同版本 Socket 的邏輯會有那么一些差異,以 Android 7.0 為例,Socket 建連的堆棧如下:

java.net.PlainSocketImpl.socketConnect(Native Method) java.net.AbstractPlainSocketImpl.doConnect java.net.AbstractPlainSocketImpl.connectToAddress java.net.AbstractPlainSocketImpl.connect java.net.SocksSocketImpl.connect java.net.Socket.connect com.android.okhttp.internal.Platform.connectSocket com.android.okhttp.Connection.connectSocket com.android.okhttp.Connection.connect 

“socketConnect”方法對應的 Native 方法定義在PlainSocketImpl.c,查看makefile可以知道它們會編譯在 libopenjdk.so 中。不過在 Android 8.0,整個調用流程又完全改變了。為了兼容性考慮,我們直接 PLT Hook 內存的所有 so,但是需要排除掉 Socket 函數本身所在的 libc.so。

hook_plt_method_all_lib("libc.so", "connect", (hook_func) &create_hook); hook_plt_method_all_lib("libc.so, "send", (hook_func) &send_hook); hook_plt_method_all_lib("libc.so", "recvfrom", (hook_func) &recvfrom_hook); ... 

這種做法不好的地方在於會把系統的 Local Socket 也同時接管了,需要在代碼中增加過濾條件。在今天的 Sample 中,我給你提供了一套簡單的實現。其實無論是哪一種 Hook,如果熟練掌握之后你會發現它並不困難。我們需要耐心地尋找,梳理清楚整個調用流程。

第三種方法:統一網絡庫。

盡管拿到了所有的網絡調用,想想會有哪些使用場景呢?模擬網絡數據、統計應用流量,或者是單獨代理 WebView 的網絡請求。

network_3_1

一般來說,我們不會非常關心第三方的網絡請求情況,而對於我們應用自身的網絡請求,最好的監控方法還是統一網絡庫。不過我們可以通過插樁和 Hook 這兩個方法,監控應用中有哪些地方使用了其他的網絡庫,而不是默認的統一網絡庫。

在上一期內容中,我說過“網絡質量監控”應該是客戶端網絡庫中一個非常重要的模塊,它也會跟大網絡平台的接入服務共同協作。通過統一網絡庫的方式,的確無法監控到第三方的網絡請求。不過我們可以通過其他方式拿到應用的整體流量使用情況,下面我們一起來看看。

2. 如何監控流量

應用流量監控的方法非常簡單,一般通過 TrafficStats 類。TrafficState 是 Android API 8 加入的接口,用於獲取整個手機或者某個 UID 從開機算起的網絡流量。至於如何使用,你可以參考 Facebook 一個歷史比較久遠的開源庫network-connection-class

getMobileRxBytes() //從開機開始Mobile網絡接收的字節總數,不包括Wifi getTotalRxBytes() //從開機開始所有網絡接收的字節總數,包括Wifi getMobileTxBytes() //從開機開始Mobile網絡發送的字節總數,不包括Wifi getTotalTxBytes() //從開機開始所有網絡發送的字節總數,包括Wifi 

它的實現原理其實也非常簡單,就是利用 Linux 內核的統計接口。具體來說,是下面兩個 proc 接口。

// stats接口提供各個uid在各個網絡接口(wlan0, ppp0等)的流量信息
/proc/net/xt_qtaguid/stats
// iface_stat_fmt接口提供各個接口的匯總流量信息
proc/net/xt_qtaguid/iface_stat_fmt

TrafficStats 的工作原理是讀取 proc,並將目標 UID 下面所有網絡接口的流量相加。但如果我們不使用 TrafficStats 接口,而是自己解析 proc 文件呢?那我們可以得到不同網絡接口下的流量,從而計算出 WiFi、2G/3G/4G、VPN、熱點共享、WiFi P2P 等不同網絡狀態下的流量。

不過非常遺憾的是,Android 7.0 之后系統已經不讓我們直接去讀取 stats 文件,防止開發者可以拿到其他應用的流量信息,因此只能通過 TrafficStats 拿到自己應用的流量信息。

除了流量信息,通過 /proc/net 我們還可以拿到大量網絡相關的信息,例如網絡信號強度、電平強度等。Android 手機跟 iPhone 都有一個網絡測試模式,感興趣的同學可以嘗試一下。

  • iPhone:打開撥號界面,輸入“*3001#12345#*”,然后按撥號鍵。
  • Android 手機:打開撥號界面,輸入“*#*#4636#*#*”,然后按撥號鍵(可進入工程測試模式,部分版本可能不支持)。

network_3_2

為什么系統可以判斷此時的 WiFi“已連接,但無法訪問互聯網”?回想一下專欄第 15 期我給你留的課后作業:

iPhone 的無線網絡助理、小米和一加的自適應 WLAN 它們在檢測 WiFi 不穩定時會自動切換到移動網絡。那請你思考一下,它們是如何實現偵測,如何區分是應用后台服務器出問題還是 WiFi 本身有問題呢?

我看了一下同學們的回復,大部分同學認為需要訪問一個公網 IP 的方式。其實對於手機廠商來說根據不需要,它在底層可以拿到的信息有很多。

  • 網卡驅動層信息。如射頻參數,可以用來判斷 WiFi 的信號強度;網卡數據包隊列長度,可以用來判斷網絡是否擁塞。
  • 協議棧信息。主要是獲取數據包發送、接收、時延和丟包等信息。

如果一個 WiFi 發送過數據包,但是沒有收到任何的 ACK 回包,這個時候就可以初步判斷當前的 WiFi 是有問題的。這樣系統可以知道當前 WiFi 大概率是有問題的,它並不關心是不是因為我們后台服務器出問題導致的。

大網絡平台監控

前面我講了一些應用網絡請求和流量的監控方法,但是還沒真正回答應該如何去打造一套強大的網絡監控體系。跟網絡優化一樣,網絡監控不是客戶端可以單獨完成的,它也是整個大網絡平台的一個重要組成部分。

不過首先我們需要在客觀上承認這件事情做起來並不容易,因為網絡問題會存在下面這些特點:

  • 實時性。部分網絡問題過時不候,可能很快就丟失現場。
  • 復雜性。可能跟國家、地區、運營商、版本、系統、機型、CDN 都有關,不僅維度多,數據量也巨大。
  • 鏈路長。整個請求鏈條非常長,客戶端故障、網鏈障絡、服務故障都有可能。

因此所謂的網絡監控,並不能保證可以明確找到故障的原因。而我們目標是希望快速發現問題,盡可能拿到更多的輔助信息,協助我們更容易地排查問題。

下面我分別從客戶端與接入層的角度出發,一起來看看哪些信息可以幫助我們更好地發現問題和解決問題。

1. 客戶端監控

客戶端的監控使用統網絡庫的方式,你可以想想我們需要關心哪些內容:

  • 時延。一般我們比較關心每次請求的 DNS 時間、建連時間、首包時間、總時間等,會有類似 1 秒快開率、2 秒快開率這些指標。
  • 維度。網絡類型、國家、省份、城市、運營商、系統、客戶端版本、機型、請求域名等,這些維度主要用於分析問題。
  • 錯誤。DNS 失敗、連接失敗、超時、返回錯誤碼等,會有 DNS 失敗率、連接失敗率、網絡訪問的失敗率這些指標。

通過這些數據,我們也可以匯總出應用的網絡訪問大圖。例如在國內無論我們去到哪里都會問有沒有 WiFi,WiFi 的占比會超過 50%。這其實遠遠比海外高,在印度 WiFi 的占比僅僅只有 15% 左右。

network_3_3

同樣的我們分版本、分國家、分運營商、分域名等各種各樣的維度,來監控我們的時延和錯誤這些訪問指標。

由於維度太多,每個維度的取值范圍也很廣,如果是實時計算整個數據量會非常非常大。對於客戶端的上報數據,微信可以做到分鍾級別的監控報警。不過為了運算簡單我們會拋棄 UV,只計算每一分鍾部分維度的 PV。

2. 接入層監控

客戶端監控的數據會比接入層更加豐富,因為有可能會出現部分數據還沒到達接入層就已經被打回,例如運營商劫持的情況。

network_3_4

但是接入層的數據監控還是非常有必要的,主要的原因是:

  • 實時性。客戶端如果使用秒級的實時上報,對用戶性能影響會比較大。服務端就不會存在這個問題,它很容易可以做到秒級的監控。
  • 可靠性。如果出現某些網絡問題,客戶端的數據上報通道可能也會受到影響,客戶端的數據不完全可靠。

那接入層應該關心哪些數據呢?一般來說,我們會比較關心服務的入口和出口流量、服務端的處理時延、錯誤率等。

3. 監控報警

無論是客戶端還是接入層的監控,它們都是分層的。

  • 實時監控。秒級或者分鍾級別的實時監控的信息會相比少一些,例如只有訪問量(PV)、錯誤率,沒有去拆分幾百個上千個維度,也沒有獨立訪問用戶數(UV),實時監控的目的是最快速度發現問題。
  • 離線監控。小時或者天級別的監控我們可以拓展出全部的維度來做監控,它的目的是在監控的同時,可以更好地圈出問題的范圍。

下面是一個簡單根據客戶端、國家以及運營商維度分析的示例。當然更多的時候是某一個服務出現問題,這個時候通過分域名或者錯誤碼就可以很容易的找到原因。

network_3_5

那在監控的同時如何實現准確的自動化報警呢?這同樣也是業界的一個難題,它的難度在於如果規則過於苛刻,可能會出現漏報;如果過於寬松,可能會出現太多的誤報。

業界一般存在兩種報警的算法,一套是基於規則,例如失敗率與歷史數據相比暴漲、流量暴跌等。另一種是基於時間序列算法或者神經網絡的智能化報警,使用者不需要錄入任何規則,只需有足夠長的歷史數據,就可以實現自動報警。智能化報警目前准確性也存在一些問題,在智能化基礎上面添加少量規則可能會是更好的選擇。

如果我們收到一個線上的網絡報警,通過接入層和客戶端的監控報表,也會有了一個大致的判斷。那怎么樣才能確定問題的最終原因?我們是否可以拿到用戶完整的網絡日志?甚至遠程地診斷用戶的網絡情況?關於“網絡日志和遠程診斷,如何快速定位網絡問題”,我會把它單獨成篇放在專欄第二模塊里,再來講講這個話題。

總結

監控、監控又是監控,很多性能優化工作其實都是“三分靠優化,七分靠監控”。

為什么監控這么重要呢?對於大公司來說,每一個項目參與人員可能成百上千人。並且大公司要的不是今天或者這個版本可以做好一些事情,而是希望保證每天每個版本都能持續保持應用的高質量。另一方面有了完善的分析和監控的平台,我們可以把復雜的事情簡單化,把一些看起來“高不可攀”的優化工作,變成人人都可以做。

最后多談兩句我的感受,我們在工作的時候,希望你可以看得更遠,從更高的角度去思考問題。多想想如果我能做好這件事情,怎么保證其他人不會犯錯,或者讓所有人都可以做得更好。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM