APP網(wǎng)絡監(jiān)控-技術分享

1.背景

隨著業(yè)務的發(fā)展轮听,用戶對于網(wǎng)絡的依賴場景會越來越多卷谈,隨之而來遇到的各種異常網(wǎng)絡場景也越來越多杯拐。

1、為了保證網(wǎng)絡的接口的持續(xù)健康世蔗、及時發(fā)現(xiàn)問題端逼,為網(wǎng)絡性能優(yōu)化提供數(shù)據(jù)基礎。

2污淋、同時以報表的形式直觀的去展現(xiàn)現(xiàn)有網(wǎng)絡質量顶滩。

3、也為了更好的支撐后續(xù)業(yè)務的發(fā)展寸爆,就需要我們?nèi)ゴ罱ㄆ鹣鄳木W(wǎng)絡監(jiān)控體系礁鲁。

二盐欺、目標

提供一套完整的網(wǎng)絡采集、監(jiān)控和預警的可視化機制仅醇,用于線上接口可用性和健康度觀察冗美,并提供一系列排查問題的輔助信息。

通過儀表盤可視化呈現(xiàn)網(wǎng)絡質量析二,包括但不限于以下能力:

  • 總體請求成功率
  • 過濾單個請求成功率墩衙、失敗錯誤碼
  • 查看接口請求詳情
  • 接口訪問平均耗時、時長分布
  • 通過 traceId 實現(xiàn)從客戶端請求的發(fā)起到最終具體服務器的處理返回全鏈路追蹤甲抖。

三漆改、總體方案

網(wǎng)絡接口監(jiān)控架構.png

不管是 iOS 還是 Android ,兩者最終要做的目標方案如上圖准谚。這里從下到上分別闡述每個部分的功能:

網(wǎng)絡基礎庫:針對平臺特性挫剑,這里有 iOS 的 AFnetworking、Alamofire 和 Android 的 okhttp3柱衔、okhttp4 樊破,其實現(xiàn)原理應該都差不多,都是針對底層的網(wǎng)絡api進行進一步的封裝唆铐,提高接口的易用性哲戚。

攔截器:主要是針對各個基礎網(wǎng)絡庫進行接口攔截。根據(jù)平臺不同艾岂,iOS 主要使用 NSProtocol + Hook顺少,Android 使用 Aspect 。

網(wǎng)絡封裝庫:一般開發(fā)過程都會針對基礎的網(wǎng)絡庫再做二次封裝王浴,加入一些策略脆炎、緩存、安全校驗等管理氓辣,使其更加貼合業(yè)務和快速接入使用秒裕。

插件/功能模塊:以插件化的形式提供額外的網(wǎng)絡功能

統(tǒng)計模塊:將從業(yè)務開始調用到回調給業(yè)務方的各個環(huán)節(jié)的耗時及狀態(tài)值,變成統(tǒng)計數(shù)據(jù)匯報到APM钞啸。
網(wǎng)絡診斷模塊:對關鍵業(yè)務進行診斷几蜻,包括dns解析、ping体斩、弱網(wǎng)檢測等梭稚,輸出診斷報告并上報到APM。
重試模塊:根據(jù)策略進行重試硕勿,包括 ip 重試哨毁、https 降級重試、原 url 重試等源武。
httpdns模塊:提供 httpdns 能力扼褪,解決域名劫持問題想幻。
上傳模塊:提供上傳能力,包括斷點續(xù)傳话浇、分片上傳以及包體大小脏毯、上傳耗時等信息監(jiān)控。
下載模塊:提供下載能力幔崖,包括大文件下載食店、斷點續(xù)傳以及包體大小、下載耗時等信息監(jiān)控赏寇。
mock 模塊:提供 mock 能力吉嫩,主要用于測試和后臺接口還沒有準備好的情況下使用。
對外接口層:這一層直接對接上層業(yè)務嗅定。

四自娩、具體實現(xiàn)

1)請求方式

iOS 常用的第三方網(wǎng)絡 AFNetworking、Alamofire 基本都是基于 NSURLConnection 或者是 NSURLSession 的封裝渠退,其中 NSURLConnection 是比較舊的使用方式了忙迁,而 NSURLSession 則是比較新的也是比較被推薦的使用方式。

2)底層原理

在使用 NSURLConnection 和 NSURLSession 進行網(wǎng)絡請求的時候碎乃,實際上走的都是更底層的 URL Loading System姊扔,URL Loading System 使用標準協(xié)議 https 或者自定義協(xié)議訪問標識資源,本身支持 http梅誓,https恰梢,文件,ftp 和數(shù)據(jù)協(xié)議证九。

可以通過繼承 NSURLProtocol 實現(xiàn)一個自定義的 Protocol删豺,然后調用 registerClass:方法注冊到 URL Loading System 中去,這樣 NSURLConnection愧怜、NSURLSession 或者是 NSURLDownload 在使用 NSURLRequest 初始化一個連接的時候,URL Loading System 就會

將按照注冊時的相反順序詢問每個注冊的類妈拌,詢問到第一個 +canInitWithRequest: 方法返回 YES 的時候則使用該類去處理請求拥坛。

  • NSURConnection 中,直接調用 registerClass:方法注冊我們自己的協(xié)議即可尘分。
  • NSURLSession 中猜惋,如果是通過 [NSURLSession sharedSession] 初始化創(chuàng)建網(wǎng)絡請求,調用 registerClass:即可培愁,如果是通過 configurantion 來初始化著摔,則通過修改 configuration 的 protocolClasses 屬性,把自定義類插入到該數(shù)組的前面定续,確保我們的自定義的協(xié)議能夠優(yōu)先處理到網(wǎng)絡請求谍咆。

可以看到 OHHTTPStubs 開源庫在注冊子類的時候也是這樣處理的

+ (void)setEnabled:(BOOL)enable forSessionConfiguration:(NSURLSessionConfiguration*)sessionConfig
{
    // Runtime check to make sure the API is available on this version
    if ([sessionConfig respondsToSelector:@selector(protocolClasses)]
        && [sessionConfig respondsToSelector:@selector(setProtocolClasses:)])
    {
        NSMutableArray * urlProtocolClasses = [NSMutableArray arrayWithArray:sessionConfig.protocolClasses];
        Class protoCls = HTTPStubsProtocol.class;
        if (enable && ![urlProtocolClasses containsObject:protoCls])
        {
            // 將自己的 NSURLProtocol 插入到 protocolClasses 的第一個禾锤,進行攔截
            [urlProtocolClasses insertObject:protoCls atIndex:0];
        }
        else if (!enable && [urlProtocolClasses containsObject:protoCls])
        {
            // 攔截完成后移除
            [urlProtocolClasses removeObject:protoCls];
        }
        sessionConfig.protocolClasses = urlProtocolClasses;
    }
    else
    {
        NSLog(@"[OHHTTPStubs] %@ is only available when running on iOS7+/OSX9+. "
              @"Use conditions like 'if ([NSURLSessionConfiguration class])' to only call "
              @"this method if the user is running iOS7+/OSX9+.", NSStringFromSelector(_cmd));
    }
}
3)實現(xiàn)步驟

利用 Objc 運行時 hook 掉 NSURLSessionConfiguration 的 defaultSessionConfiguration 屬性和 ephemeralSessionConfiguration 屬性設置,然后修改 configuration 的 protocolClassess 屬性摹察,插入我們自定義的 Protocol
在自定義的 NSURLProtocol 之類中實現(xiàn)如下方法:
+ canInitWithRequest: 在這里判斷當前網(wǎng)絡請求是否需要監(jiān)控恩掷,如果不需要直接 return NO 即可。

      + canonicalRequestForRequest:   生成一個新的 request 請求供嚎,同時標識該請求已經(jīng)處理過黄娘,防止死循環(huán)。

      - startLoading  將新的請求發(fā)送出去克滴,設置對應的回調代理逼争。

      - stopLoading   停止網(wǎng)絡請求。

 3. 處理請求回調劝赔,實現(xiàn)需要進行處理的回調方法誓焦,處理完成后通過 self.client.urlProtocol 將回調方法傳回至原來的 delegate。

 4. 至此望忆,我們就完成了發(fā)送罩阵、接收等一系列操作,并且完美的將回調轉發(fā)回了原來的代理方启摄,剩下的就是我們在回調中收集完了請求的各種信息就好稿壁。

流程圖如下:


網(wǎng)絡攔截前后對比圖.png
4)可能存在的問題及優(yōu)化

流程并不復雜,從上圖可以看到歉备,使用了網(wǎng)絡攔截之后的流程圖比原本的多了一個 custom protocol(DLURLProtocol)和 custom session傅是。custom potocol 用于攔截網(wǎng)絡請求,custom session 用于發(fā)起新的請求蕾羊。

這里可能會存在兩個問題:

每個請求都會新創(chuàng)建一個 NSURLSession喧笔,對于網(wǎng)絡請求這種很頻繁的操作來說不是很友好;
新創(chuàng)建的 NSURLSession 如何確保超時龟再、緩存书闸、認證、cookies 等策略跟原始的 NSURLSession 保持一致利凑,如果不一致是否會影響到既有的網(wǎng)絡請求浆劲?

五、風險評估

針對可能存在的問題做相關梳理和驗證~

關于第一點:每個請求都會創(chuàng)建一個 NSURLSession 這個很好解決哀澈,使用一個單例即可牌借,從蘋果的官方Demo CustomHTTPProtocol 中可以看到 Demux 這個類,通過閱讀源碼知道割按,該類的存在除了最大化復用 Session 之外膨报,還將請求的發(fā)起和回調都放到了這個類進行處理,確保請求發(fā)起和回調都是在同一個線程和 Runloop Mode,至于為什么要這么做现柠,文檔中沒有找到明確說明院领,不過后面踩坑的時候才發(fā)現(xiàn),如果不這么做的話晒旅,在回調里面很容易就會遇到崩潰的情況栅盲,盡管你什么都沒有做。

至于第二點:新創(chuàng)建的 NSURLSession 是否會影響到原來的網(wǎng)絡請求策略废恋?

思考:

根據(jù)蘋果提供的Demo CustomHTTPProtocol 中可以看到谈秫,同樣也是通過新創(chuàng)建一個 NSURLSession 發(fā)起請求的,那么它難道不會出現(xiàn)超時鱼鼓、緩存拟烫、認證等參數(shù)和原始請求不一致的情況么?

從邏輯上來說迄本,要么就是要獲取原始請求的 session硕淑,拿到對應的超時、緩存嘉赎、認證等配置信息再發(fā)起請求置媳;要么就是 Demux 中新創(chuàng)建的 session 對于請求發(fā)起方來說是透明的,這種透明包括不影響任何原始請求的參數(shù)配置公条!

針對以上猜想做相關驗證:

5.1拇囊、超時驗證:

驗證1:原始請求設置超時為 5s,Demux 設置超時時間為 60s靶橱,手機網(wǎng)絡設置為100% lost

驗證結果:5s 觸發(fā)超時

`2021``-``03``-``28` `18``:``44``:``11.307007``+``0800` `NSURLProtocolTest[``36460``:``8443172``] start a request...`

`2021``-``03``-``28` `18``:``44``:``16.381879``+``0800` `NSURLProtocolTest[``36460``:``8443172``] Task <CDCBC81D-E0C5-4A52-BDBF-5912AC0BCA32>.<``1``> finished with error [-``1001``] Error Domain=NSURLErrorDomain Code=-``1001` `"The request timed out."` `UserInfo={_kCFStreamErrorCodeKey=-``2102``, NSUnderlyingError=``0x2836d19e0` `{Error Domain=kCFErrorDomainCFNetwork Code=-``1001` `"(null)"` `UserInfo={_kCFStreamErrorCodeKey=-``2102``, _kCFStreamErrorDomainKey=``4``}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <CDCBC81D-E0C5-4A52-BDBF-5912AC0BCA32>.<``1``>, _NSURLErrorRelatedURLSessionTaskErrorKey=(`

`"LocalDataTask <CDCBC81D-E0C5-4A52-BDBF-5912AC0BCA32>.<1>"`

`), NSLocalizedDescription=The request timed out., NSErrorFailingURLStringKey=https:``//www.baidu.com, NSErrorFailingURLKey=[https://www.baidu.com](https://www.baidu.com/), _kCFStreamErrorDomainKey=4}`

  • 驗證2: 調用發(fā)設置超時時間為 60s寥袭,Demux 設置超時時間為 5s,手機網(wǎng)絡設置為 100% lost

驗證結果:60s 觸發(fā)超時

| 

`2021``-``03``-``28` `19``:``02``:``29.918506``+``0800` `NSURLProtocolTest[``36473``:``8448954``] start a request...`

`2021``-``03``-``28` `19``:``03``:``29.869946``+``0800` `NSURLProtocolTest[``36473``:``8448954``] Task <A533832C-C1E2-48B4-9C73-50447B930141>.<``1``> finished with error [-``1001``] Error Domain=NSURLErrorDomain Code=-``1001` `"The request timed out."` `UserInfo={_kCFStreamErrorCodeKey=-``2102``, NSUnderlyingError=``0x283d144b0` `{Error Domain=kCFErrorDomainCFNetwork Code=-``1001` `"(null)"` `UserInfo={_kCFStreamErrorCodeKey=-``2102``, _kCFStreamErrorDomainKey=``4``}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <A533832C-C1E2-48B4-9C73-50447B930141>.<``1``>, _NSURLErrorRelatedURLSessionTaskErrorKey=(`

`"LocalDataTask <A533832C-C1E2-48B4-9C73-50447B930141>.<1>"`

`), NSLocalizedDescription=The request timed out., NSErrorFailingURLStringKey=https:``//www.baidu.com, NSErrorFailingURLKey=[https://www.baidu.com](https://www.baidu.com/), _kCFStreamErrorDomainKey=4}`

結論:Demux 中新創(chuàng)建的 NSURLSession 超時時間設置不影響到請求發(fā)起方关霸。

5.2传黄、緩存驗證:

驗證1:原始請求設置為不使用緩存 NSURLRequestReloadIgnoringLocalCacheData,Demux 中設置為使用緩存 NSURLRequestReturnCacheDataElseLoad 队寇;通過 charles 抓包確認在收到 completed 的時候是否有真正發(fā)起請求膘掰。

驗證結果:每次點擊開始請求按鈕的時候, charles 都能抓到請求數(shù)據(jù)包佳遣,且 response code 為 200炭序。

驗證2:原始請求設置為使用緩存 NSURLRequestReturnCacheDataElseLoad,Demux 中設置為不使用緩存 NSURLRequestReturnCacheDataElseLoad苍日;通過 charles 抓包確認在收到 completed 的時候是否有真正發(fā)起請求。

驗證結果:卸載App窗声,首次點擊請求按鈕的時候可以在 charles 中抓到請求數(shù)據(jù)包相恃;后面再次點擊的時候就沒有抓到相關請求數(shù)據(jù)包了,但卻返回到了 completed 回調笨觅,且 response code 為 200

結論:Demux 中新創(chuàng)建的 NSURLSession 緩存設置不影響到請求發(fā)起方拦耐。

Snip20210712_6.png
5.3耕腾、認證策略驗證:

驗證1:由于條件限制,我們這里只做單向驗證杀糯,即驗證服務器證書扫俺。在請求方的回調 URLSession: didReceiveChallenge: completionHandler: 回調里面對服務器證書與本地正式的校驗,校驗通過則返回 completionHandler(NSURLSessionAuthChallengeUseCredential,credential);固翰;然后在 DLURLProtocol 的 URLSession: didReceiveChallenge: completionHandler: 回調中直接設置為校驗不通過

completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL);

驗證結果:觸發(fā)請求方的超時設置狼纬!

結論:會影響到請求方,盡管在請求方的 URLSession: didReceiveChallenge: completionHandler:回調里面調用了認證通過的completionHandler骂际,一樣會觸發(fā)超時操作疗琉!

規(guī)避方法:網(wǎng)絡監(jiān)控所需要的信息采集不涉及到認證這塊,可以直接將回調拋給請求方歉铝,由請求發(fā)起方進行處理盈简。

5.4、耗時驗證:

驗證1:相隔10ms太示,異步輪流發(fā)起請求分別請求 www.baidu.com 柠贤、www.sina.com.cnwww.taobao.com 這幾個域名,加起來總共請求 100 次类缤,然后計算使用 NSProtocol 和不使用 NSProtocol 的平均耗時臼勉。

驗證結果:

//有接入:
2021-05-11 00:36:39.112549+0800 NSURLProtocolTest[90129:29124516] baidu, count:26 , avgDuration:324.019181
 
2021-05-11 00:36:39.112666+0800 NSURLProtocolTest[90129:29124516] sina, count:46  avgDuration:553.305805
 
2021-05-11 00:36:39.115587+0800 NSURLProtocolTest[90129:29124516] taobao, count:28  avgDuration:300.874954

//無接入:
 
2021-05-11 00:29:52.958785+0800 NSURLProtocolTest[90066:29117542] baidu, count:35 , avgDuration:306.175676
 
2021-05-11 00:29:52.958941+0800 NSURLProtocolTest[90066:29117542] sina, count:29  avgDuration:321.528200
 
2021-05-11 00:29:52.959113+0800 NSURLProtocolTest[90066:29117542] taobao, count:36  avgDuration:297.670796

結論:除去網(wǎng)絡波動影響,耗時基本相近呀非。
詳細日志:(存放在百度網(wǎng)盤上面的網(wǎng)絡監(jiān)控文件夾)

六坚俗、參考鏈接

iOS 中的網(wǎng)絡調試

愛奇藝全鏈路自動化監(jiān)控平臺的探索與實踐

深度理解 NSURLProtocol

移動端APM網(wǎng)絡監(jiān)控與優(yōu)化實踐

URL Session Programming Guide

CustomHTTPProtocol

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市岸裙,隨后出現(xiàn)的幾起案子猖败,更是在濱河造成了極大的恐慌,老刑警劉巖降允,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恩闻,死亡現(xiàn)場離奇詭異,居然都是意外死亡剧董,警方通過查閱死者的電腦和手機幢尚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翅楼,“玉大人尉剩,你說我怎么就攤上這事∫汶” “怎么了理茎?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我皂林,道長朗鸠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任础倍,我火速辦了婚禮烛占,結果婚禮上,老公的妹妹穿的比我還像新娘沟启。我一直安慰自己忆家,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布美浦。 她就那樣靜靜地躺著弦赖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浦辨。 梳的紋絲不亂的頭發(fā)上蹬竖,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音流酬,去河邊找鬼币厕。 笑死,一個胖子當著我的面吹牛芽腾,可吹牛的內(nèi)容都是我干的旦装。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼摊滔,長吁一口氣:“原來是場噩夢啊……” “哼阴绢!你這毒婦竟也來了?” 一聲冷哼從身側響起艰躺,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呻袭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后腺兴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體左电,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年页响,在試婚紗的時候發(fā)現(xiàn)自己被綠了篓足。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡闰蚕,死狀恐怖栈拖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情没陡,我是刑警寧澤辱魁,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布烟瞧,位于F島的核電站,受9級特大地震影響染簇,放射性物質發(fā)生泄漏。R本人自食惡果不足惜强岸,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一锻弓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蝌箍,春花似錦青灼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至悯衬,卻和暖如春弹沽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筋粗。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工策橘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人娜亿。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓丽已,卻偏偏與公主長得像,于是被迫代替她去往敵國和親买决。 傳聞我的和親對象是個殘疾皇子沛婴,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容

  • .前言 1.什么是APM? APM(Application Performance Management & Mo...
    Jackxu_q閱讀 1,567評論 0 2
  • iOS網(wǎng)絡相關類介紹 網(wǎng)絡請求地址對象——NSURL url 介紹url,統(tǒng)一資源定位符督赤,也被稱為網(wǎng)址嘁灯,因特網(wǎng)上標...
    小芳姑娘2012閱讀 416評論 3 0
  • 目標 TCP建立連接時間 DNS時間 SSL/TLS時間 響應總時間 請求頭、請求body够挂、響應頭旁仿、響應body大...
    mtry閱讀 3,210評論 1 5
  • iOS網(wǎng)絡架構討論梳理整理中。孽糖。枯冈。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,193評論 1 23
  • 在開發(fā)中办悟,有時候我們需要獲取這些信息: 手機是否聯(lián)網(wǎng)當前網(wǎng)絡是WiFi還是蜂窩那么我總結一下具體的使用場景有哪些尘奏?...
    水落斜陽閱讀 986評論 0 2