iOS 網(wǎng)絡(luò)性能獲取及優(yōu)化

隨著產(chǎn)品的功能越來(lái)越多,App的性能問(wèn)題越來(lái)越凸顯,為提高產(chǎn)品的體驗(yàn),拿到App的性能數(shù)據(jù)尤其重要及時(shí)上報(bào),才能不管的優(yōu)化,給用戶最好的體驗(yàn)。

分析網(wǎng)絡(luò)請(qǐng)求流程及耗時(shí)

1. 網(wǎng)絡(luò)請(qǐng)求的過(guò)程

發(fā)起請(qǐng)求 > 域名解析 > tcp三次握手 > tls握手 > request > response > json解析 > 業(yè)務(wù)

2. 耗時(shí)統(tǒng)計(jì)

在了解了網(wǎng)絡(luò)請(qǐng)求的流程之后,我們可以先測(cè)試一下自己項(xiàng)目網(wǎng)絡(luò)請(qǐng)求耗時(shí)分布,對(duì)于iOS來(lái)說(shuō)。

App使用網(wǎng)絡(luò)的組件主要有兩個(gè):

2.1、NSURLSession

獲取耗時(shí)最簡(jiǎn)單的方式就是監(jiān)聽NSURLSession的didFinishCollectingMetrics回調(diào)方法

 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0))

{
 if (@available(iOS 10.0, *)) {
     for (NSURLSessionTaskTransactionMetrics *sessionMetric in metrics.transactionMetrics) {
         NSInteger dom = ([sessionMetric.domainLookupEndDate timeIntervalSince1970] - [sessionMetric.domainLookupStartDate timeIntervalSince1970]) * 1000 ;
         NSInteger sec = ([sessionMetric.secureConnectionEndDate timeIntervalSince1970] - [sessionMetric.secureConnectionStartDate timeIntervalSince1970]) * 1000;
         NSInteger con = ([sessionMetric.connectEndDate timeIntervalSince1970] - [sessionMetric.connectStartDate timeIntervalSince1970]) * 1000;
         NSInteger req = ([sessionMetric.requestEndDate timeIntervalSince1970] - [sessionMetric.requestStartDate timeIntervalSince1970]) * 1000;
         NSInteger res = ([sessionMetric.responseEndDate timeIntervalSince1970] - [sessionMetric.responseStartDate timeIntervalSince1970]) * 1000;
         NSInteger tot = ([sessionMetric.responseEndDate timeIntervalSince1970] - [sessionMetric.fetchStartDate timeIntervalSince1970]) * 1000;

         NSString *locip = @"";
         NSString *remip = @"";

         if (@available(iOS 13.0, *)) {
             locip = [NSString stringWithFormat:@"%@", sessionMetric.localAddress];
             remip = [NSString stringWithFormat:@"%@", sessionMetric.remoteAddress];
         }

         NSLog(@"metric path:%@ 總耗時(shí):%ldms, 域名解析:%ldms, 連接耗時(shí):%ldms(包括TLS:%ldms), 請(qǐng)求:%ldms, 回調(diào):%ldms l:%@ r:%@",sessionMetric.request.URL.lastPathComponent,tot,dom,con,sec,req,res, locip, remip);
     }
 }
}    

獲取的數(shù)據(jù)類似下圖:

圖片
2.2、WKWebView 和 UIWebView(已被蘋果拋棄)

JS公用方法

static NSString *KIOS10UpWebViewJSTiming = @"JSON.stringify(window.performance.timing.toJSON())";
static NSString *KIOS10DownWebViewJSFun = @"function flatten(obj) {"
                                             "var ret = {}; "
                                             "for (var i in obj) { "
                                                "ret[i] = obj[i];"
                                                "}"
                                            "return ret;}";

static NSString *KIOS10DownWebViewJSTiming = @"JSON.stringify(flatten(window.performance.timing))";

UIWebView獲取網(wǎng)絡(luò)性能方法

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSString *timingStr;
    if (@available(iOS 10.0, *)) {
        timingStr = [webView stringByEvaluatingJavaScriptFromString:KIOS10UpWebViewJSTiming];
    }else{
        [webView stringByEvaluatingJavaScriptFromString:KIOS10DownWebViewJSFun];
        timingStr = [webView stringByEvaluatingJavaScriptFromString:KIOS10DownWebViewJSTiming];
    }
    NSLog(@"webView總耗數(shù)據(jù):%@", timingStr);
}

WKWebView獲取網(wǎng)絡(luò)性能方法

-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
if (@available(iOS 10.0, *)) {
        [webView evaluateJavaScript:KIOS10UpWebViewJSTiming completionHandler:^(NSString * _Nullable timingStr, NSError * _Nullable error) {
            if (!error) {
                NSLog(@"webView總耗數(shù)據(jù):%@", timingStr);
            }
        }];
    }else{
        [webView evaluateJavaScript:KIOS10DownWebViewJSFun completionHandler:^(NSString *_Nullable result, NSError * _Nullable error) {
            if (!error) {
                [webView evaluateJavaScript:KIOS10DownWebViewJSTiming completionHandler:^(NSString * _Nullable timingStr, NSError * _Nullable error) {
                    if (!error) {
                       NSLog(@"webView總耗數(shù)據(jù):%@", timingStr);
                    }
                }];
            }
        }];
    }
}

二.優(yōu)化思路

1.域名解析耗時(shí)

我們常用的HTTP請(qǐng)求,底層基于TCP連接,需要有IP和端口信息。由于IP是可變的,且不好記憶,所以有了域名這樣一個(gè)字符串幫助人們進(jìn)行記憶。

字符串到IP需要經(jīng)過(guò)DNS服務(wù)器來(lái)進(jìn)行獲取,這一過(guò)程使用的UDP方式,這一過(guò)程的快慢取決于網(wǎng)絡(luò)質(zhì)量以及域名映射關(guān)系存放的DNS服務(wù)器跟你所在地跨越的層級(jí)。甚至有一些不可控的情況會(huì)導(dǎo)致域名解析出來(lái)一個(gè)錯(cuò)誤的IP地址。

為了提高域名解析速度,以及減低域名污染風(fēng)險(xiǎn),我們可以使用HTTPDNS的方式來(lái)進(jìn)行IP獲取。也可以設(shè)計(jì)一套IP直連方式獲取域名IP池機(jī)制。

在APP啟動(dòng)后檢查更新DNS服務(wù)IP的更新,域名IP池的更新,以及對(duì)IP池中的IP進(jìn)行測(cè)速,選擇網(wǎng)速較好的IP。在使用IP直連請(qǐng)求時(shí),需要修改request的host,以及證書校驗(yàn)的host。

2.連接耗時(shí)

建立連接需要的耗時(shí)較長(zhǎng),如果能夠利用長(zhǎng)連接可以保持的特性,那么這塊的耗時(shí)優(yōu)化非??捎^。

優(yōu)化這塊需要后端支持,我們可以設(shè)計(jì)一個(gè)通道API,將多個(gè)域名的請(qǐng)求放在同一個(gè)連接中進(jìn)行請(qǐng)求,這塊需要網(wǎng)絡(luò)庫(kù)層和后端進(jìn)行協(xié)議開發(fā),業(yè)務(wù)端可以普遍享受到增益。

當(dāng)然,我們也要考慮在通道不可用時(shí),及時(shí)采用常規(guī)請(qǐng)求方式的策略。以及HTTP1.1存在隊(duì)頭阻塞問(wèn)題,需要在HTTP2.0上才能真正起到優(yōu)化效果。

request及response(download)優(yōu)化

request耗時(shí)一般是在服務(wù)端,如果遇到耗時(shí)較長(zhǎng)的情況需要找一下服務(wù)端同學(xué)排查一下是否有耗時(shí)邏輯。response耗時(shí)較長(zhǎng)一般是網(wǎng)速慢或者數(shù)據(jù)體積大導(dǎo)致的,體積大這個(gè)問(wèn)題可以通過(guò)刪減冗余數(shù)據(jù),開啟gzip,數(shù)據(jù)分頁(yè)等方式進(jìn)行優(yōu)化。

3.json解析

json解析在數(shù)據(jù)量不大的情況下一般耗時(shí)不會(huì)太多,如果遇到數(shù)據(jù)量特別大的情況可能就需要考慮怎么降低數(shù)據(jù)量,采用解析效率更高的庫(kù),可以參考如下網(wǎng)圖。

YYModel原理和MJExtension類似,都是基于runtime進(jìn)行動(dòng)態(tài)獲取屬性進(jìn)行賦值。前者使用了CoreFoundation更底層的方法以及內(nèi)聯(lián)函數(shù)的應(yīng)用所以效率上得到了大幅提高。

圖片

4.業(yè)務(wù)側(cè)優(yōu)化

4.1 網(wǎng)絡(luò)請(qǐng)求優(yōu)先級(jí)排布,通過(guò)對(duì)業(yè)務(wù)代碼梳理,在啟動(dòng)階段進(jìn)行高優(yōu)先級(jí)接口請(qǐng)求,對(duì)可以合并的接口進(jìn)行合并,落地點(diǎn)銷毀的請(qǐng)求及時(shí)取消請(qǐng)求。

4.2 升級(jí)HTTP2.0,利用其二進(jìn)制分幀,頭部壓縮,head block優(yōu)化的特性,降低請(qǐng)求數(shù)據(jù)量,提高并發(fā)請(qǐng)求效率。

5.弱網(wǎng)優(yōu)化

弱網(wǎng)環(huán)境是移動(dòng)APP經(jīng)常需要面對(duì)的問(wèn)題,地鐵,人員密集區(qū)域,運(yùn)動(dòng),都有可能導(dǎo)致網(wǎng)絡(luò)請(qǐng)求不穩(wěn)定,失敗等問(wèn)題。

HTTP1.1,2.0都是采用的TCP連接方式,而TCP需要三次握手,斷開后需要重新握手,這些會(huì)導(dǎo)致在弱網(wǎng),運(yùn)動(dòng)環(huán)境持續(xù)請(qǐng)求失敗。

QUIC協(xié)議是谷歌提出的基于UDP的數(shù)據(jù)傳輸協(xié)議,HTTP3.0就是采用的QUIC實(shí)現(xiàn)機(jī)制。使用HTTP3.0進(jìn)行網(wǎng)絡(luò)請(qǐng)求可以享受到如下這些進(jìn)步,當(dāng)然這得服務(wù)端和客戶端共同配合才能完成。

更好的連接建立方式
更好的擁塞控制
沒(méi)有隊(duì)頭阻塞的多路復(fù)用
前向糾錯(cuò)
連接遷移

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容