記一次flutter應(yīng)用卡頓解決過(guò)程
前一段時(shí)間準(zhǔn)備接手維護(hù)公司一個(gè)flutter的k線組件乡小,目前該組件在Android上穩(wěn)定運(yùn)行。iOS還沒(méi)接入奢啥,于是自己嘗試做了demo體驗(yàn)一下flutter在iOS上的表現(xiàn)如何秸仙。剛開(kāi)始一切順利,然后就出現(xiàn)了卡頓的問(wèn)題桩盲。
問(wèn)題表現(xiàn):
1.當(dāng)出現(xiàn)網(wǎng)絡(luò)請(qǐng)求的時(shí)候寂纪,整個(gè)flutter的界面會(huì)卡死,得等到網(wǎng)絡(luò)請(qǐng)求有響應(yīng)界面才恢復(fù)。正常情況下是有一個(gè)flutter的加載進(jìn)度框捞蛋,一直在轉(zhuǎn)圈顯示加載中孝冒,如果出現(xiàn)問(wèn)題,整個(gè)頁(yè)面卡死拟杉。
2.出現(xiàn)概率:頻率很低庄涡,不是每次出現(xiàn),某些網(wǎng)絡(luò)條件下出現(xiàn)搬设,并且在有問(wèn)題的網(wǎng)絡(luò)條件下出現(xiàn)之后穴店,可能過(guò)一會(huì)就消失。也可能一直存在拿穴,導(dǎo)致每次網(wǎng)絡(luò)加載數(shù)據(jù)都卡住泣洞。
3.僅在iOS出現(xiàn),Android工作正常默色。
解決過(guò)程:
1.懷疑弱網(wǎng)環(huán)境下出現(xiàn)球凰,或者我的網(wǎng)絡(luò)請(qǐng)求響應(yīng)數(shù)據(jù)過(guò)多(大量的k線數(shù)據(jù)),導(dǎo)致json解析太慢该窗,引起主線程阻塞了。這里看了一下flutter的線程模型
我首先把服務(wù)器返回?cái)?shù)據(jù)減少,問(wèn)題依然存在蚤霞。(服務(wù)器是我自己部署的酗失,https用的let encrypt證書,圖方便用acme.sh腳本自動(dòng)更新證書昧绣,用來(lái)給demo響應(yīng)數(shù)據(jù)的)
使用charles模擬弱網(wǎng)環(huán)境降低網(wǎng)速规肴,問(wèn)題依然存在
-
采用isolate來(lái)封裝網(wǎng)絡(luò)請(qǐng)求,問(wèn)題解決夜畴,但是偶爾出現(xiàn)第一次請(qǐng)求很慢情況拖刃,整體頁(yè)面不卡住了,加載框能正常顯示動(dòng)畫贪绘。
這里說(shuō)下isolate兑牡,雖然用isolate解決了問(wèn)題,但是如果我們真的去使用isolate會(huì)發(fā)現(xiàn)很多問(wèn)題税灌,首先f(wàn)lutter中的isolate雖然概念上是線程均函,但是他和每個(gè)isolate之間的內(nèi)存空間是獨(dú)立的。假設(shè)我創(chuàng)建個(gè)isolate叫做requestIsolate菱涤,那么我要用requestIsolate去發(fā)送一個(gè)http請(qǐng)求苞也,必須在新創(chuàng)建的requestIsolate里面初始化一個(gè)httpclient,還有把一系列參數(shù)從主線程傳到requestIsolate中粘秆,而isolate的通信只能通過(guò)message如迟,message只能是基礎(chǔ)類型[string,int,float]這些個(gè)數(shù)據(jù)類型殷勘。這導(dǎo)致了isolate的使用場(chǎng)景大大減少此再。
比如你覺(jué)得json對(duì)象反序列化很耗時(shí),你想放到單獨(dú)的isolate中解析劳吠。對(duì)不起引润,不行。因?yàn)槟阋诺絠solate里面你需要把json string傳給isolate痒玩, isolate反序列化好之后變成一個(gè)dart對(duì)象淳附。但是這個(gè)dart對(duì)象又不能傳回主isolate,因?yàn)閙essage只能傳基礎(chǔ)數(shù)據(jù)類型蠢古。所以GG
2.雖然上面使用isolate讓flutter不會(huì)卡死了奴曙,但是還沒(méi)有結(jié)束。
用dart devtool分析卡頓情況:
可以看到基本上每一幀都在卡頓草讶,而且中間有一段時(shí)間啥也沒(méi)做洽糟。看看具體方法
Performance顯示在發(fā)起網(wǎng)絡(luò)請(qǐng)求ssl_handshake的時(shí)候堕战,卡在了CertifaicateVerificationCallback里面坤溃。大概率是網(wǎng)絡(luò)問(wèn)題。用Wireshark簡(jiǎn)單抓包看了下嘱丢,然后跑去溫習(xí)一下tls握手相關(guān)的知識(shí):
在tls在建立鏈接的時(shí)候是上面這個(gè)過(guò)程薪介,簡(jiǎn)單來(lái)說(shuō)由客戶端發(fā)起 clienthello,然后服務(wù)端響應(yīng),然后下發(fā)證書越驻。下發(fā)證書之后客戶端要驗(yàn)證證書是否有效汁政,比如是否過(guò)期之類,在客戶端驗(yàn)證證書的過(guò)程中出現(xiàn)了阻塞主線程的情況缀旁,導(dǎo)致整個(gè)應(yīng)用卡住记劈。
找到問(wèn)題大概放心后,去github issue搜索也在dart issue里找到了相似的問(wèn)題并巍,原來(lái)也有人遇到了相同的問(wèn)題目木。
https://github.com/dart-lang/sdk/issues/41519
整個(gè)原因就是let encrypt的ocsp服務(wù)被墻掉了,導(dǎo)致證書校驗(yàn)過(guò)程非常長(zhǎng)懊渡,或者失敗嘶窄,按道理這個(gè)校驗(yàn)不應(yīng)該卡住應(yīng)用程序,不過(guò)從上面的issue可以看到距贷,應(yīng)該是沒(méi)用使用異步處理柄冲,導(dǎo)致整個(gè)主線程被卡住,屬于http庫(kù)里的一個(gè)bug忠蝗。
關(guān)于客戶端證書認(rèn)證的文章可以看看:
https://www.anquanke.com/post/id/183339
解決方案:
對(duì)于采用了https的服務(wù)來(lái)說(shuō)现横,tls是會(huì)對(duì)客戶端造成額外的開(kāi)銷的,如果通過(guò)ocsp協(xié)議進(jìn)行校驗(yàn)是handshake階段在線校驗(yàn)的,比如let encrypt的校驗(yàn)地址是:https://ocsp.int-x3.letsencrypt.org 戒祠,這個(gè)地址被墻掉骇两,就會(huì)導(dǎo)致校驗(yàn)失敗,或者很慢的情況姜盈。 如果服務(wù)器開(kāi)啟ocsp stampling服務(wù)的話低千,這個(gè)校驗(yàn)過(guò)程可以由服務(wù)端進(jìn)行校驗(yàn),然后直接把結(jié)果和證書一起下發(fā)給客戶端馏颂∈狙客戶端就省去了這個(gè)校驗(yàn)工作,服務(wù)端校驗(yàn)有很多好處救拉,比如nginx可以把證書校驗(yàn)結(jié)果在一定有效期內(nèi)緩存下來(lái)难审,這樣能大大減少證書校驗(yàn)時(shí)間。
參考下列兩個(gè)issue的解決辦法:
https://github.com/flutterchina/dio/issues/786
https://github.com/dart-lang/sdk/issues/41519
1.換證書 2.開(kāi)啟ocsp proxy
收獲:
這個(gè)問(wèn)題困擾了我好多天亿絮,有空的時(shí)候我就去嘗試解決告喊,不過(guò)一開(kāi)始思路錯(cuò)了,都以為是是flutter異步任務(wù)太重或者代碼有問(wèn)題派昧,在往優(yōu)化的方向努力黔姜,原來(lái)還是網(wǎng)絡(luò)請(qǐng)求有問(wèn)題。作為客戶端開(kāi)發(fā)一般發(fā)現(xiàn)客戶端主線程卡死蒂萎,很少會(huì)考慮到是后端問(wèn)題秆吵,如果服務(wù)響應(yīng)過(guò)慢應(yīng)該是只會(huì)讓加載時(shí)間變長(zhǎng)而不應(yīng)該造成整個(gè)應(yīng)用卡死主線稱阻塞才對(duì)。造成了整個(gè)解決過(guò)程都沒(méi)想到是網(wǎng)絡(luò)服務(wù)的問(wèn)題岖是。整個(gè)過(guò)程還是學(xué)習(xí)到了很多知識(shí)帮毁。
1.掌握f(shuō)lutter性能監(jiān)控工具的使用
2.掌握f(shuō)lutter線程模型和isolate的原理和工作方式
3.學(xué)習(xí)到了tls握手的整個(gè)過(guò)程实苞,和ocsp的工作方式豺撑,nginx配置ocsp的方式
4.為網(wǎng)絡(luò)優(yōu)化學(xué)到了1個(gè)新技能,服務(wù)器端開(kāi)啟ocsp代理,可以大大減少tls證書認(rèn)證時(shí)間黔牵。
參考:
瀏覽器如何驗(yàn)證證書是否正確聪轿?
https://www.zhihu.com/question/37370216
https://zhuanlan.zhihu.com/p/25587986
OCSP stapling:
https://zh.wikipedia.org/wiki/OCSP%E8%A3%85%E8%AE%A2
TLS 握手優(yōu)化詳解