OkHttp 精講:ConnectInterceptor

  • 本文章所使用的 OkHttp 源碼版本:3.12.10

上一篇:OkHttp 精講:CacheInterceptor

開胃菜

  • 在講這個攔截器之前,我們先補充一點網(wǎng)絡(luò)基礎(chǔ),大部分人應(yīng)該都知道 Http 和 Https 之間最大的區(qū)別,就是使用了加密傳輸讓請求更加安全,但是大多數(shù)人卻不知道 Http 不同版本之間的區(qū)別。

  • Http 1.0 和 Http 1.1 變化最大的地方在于,Http 1.1 擁有長連接的特性,那么問題來了,什么是長連接?

    • 我們都知道 Http 是基于 TCP 衍生出來的一種應(yīng)用層協(xié)議,我們大可認為 Http 是 TCP 的子類,而一提及 TCP,大家可能第一想到的就是三次握手,每次建立連接之前都會發(fā)三次網(wǎng)絡(luò)信號來確定是否建立連接,這種模式在 Http 上面也有,只不過在 Http 1.0 和 Http 1.1 有一些區(qū)別,Http 1.0 每次請求網(wǎng)絡(luò)都需要進行三次握手,請求完畢之后連接就自動斷開了,而 Http 1.1 對這塊的內(nèi)容進行了優(yōu)化,在每次請求完畢之后并沒有立即把連接斷開,而是將這個連接復(fù)用起來,下次請求網(wǎng)絡(luò)的時候不經(jīng)過三次握手,而是直接建立連接,這個就是 Http 1.1 的長連接特性。

    • 這樣的好處在于,因為三次握手是需要消耗時間和資源的,如果每次請求網(wǎng)絡(luò)都需要三次握手才能建立連接,那么在這種條件下,無疑對服務(wù)器形成了一定的壓力負擔,同樣的客戶端也會受到一定的影響,所以復(fù)用三次握手可以極大減輕服務(wù)器的壓力和提升客戶端的響應(yīng)速度。

  • Http 1.1 和 Http 2.0 變大最大的地方在于,Http 2.0 擁有多路復(fù)用的特性,那么問題又來了,什么是多路復(fù)用?

    • 由于 Http 1.1 的長連接特性只能處理單個請求, 這樣的話請求一多就會導(dǎo)致排隊的情況出現(xiàn),若干個請求排隊串行化單線程處理,后面的請求等待前面請求的返回才能獲得執(zhí)行機會,一旦有某請求超時等,后續(xù)請求只能被阻塞,并且毫無辦法,也就是人們常說的線頭阻塞。而 Http 2.0 能在多個請求上同時在一個連接上并行執(zhí)行,如果某個請求任務(wù)耗時嚴重,則不會影響到其它請求的正常執(zhí)行。

源碼解析

  • 大家看到這個攔截器的第一感覺是不是:代碼那么少,感覺 so easy

  • 沒錯,我第一次看的時候也是那么想的,只是看完之后我的想法就變了

  • 這個一看就知道不是重點代碼,這里略過
  • 這個也是,直接跳過
  • 這個看起來有點意思,今天的主菜應(yīng)該就是它不會有錯了,讓我們接著看
  • 從翻譯上我們隱約能猜到這個方法是在尋找健康的網(wǎng)絡(luò)連接,讓我們接著往下看
  • 是不是感覺有點懵?讓我們先看看方法上面的注釋
  • 結(jié)合源碼和注釋,我們可以判斷出,它會從連接池中找連接,如果取出來的連接是不健康的,那么會繼續(xù)找,直到找到一個健康的連接為止。

  • 那么問題來了,什么樣的連接是不健康的?

  • 讓我們看一下這句代碼是怎么判斷的
  • 從這里我們可以得到一個額外的信息,OkHttp 是基于 Socket 實現(xiàn)的,而不是基于 HttpURLConnection,這個就是看源碼的好處,原來 OkHttp 的實現(xiàn)方式是直接基于 Socket。

  • 這個只是題外話,接下來讓我們看看這三個判斷是什么?

  • 一個不健康的流表現(xiàn)為:Socket 連接被關(guān)閉了又或者輸入輸出連接被關(guān)閉了
  • 讓我們再回去看看它是如何尋找連接的?
  • 注釋是看懂了,這個方法會優(yōu)先去復(fù)用已有的連接,如果復(fù)用不了或者沒有連接,最后才去創(chuàng)建一個新連接
  • 我們可以看到,這個方法會從連接池中獲取一個連接,接著看源碼
  • 它會遍歷整個連接池,判斷連接是否符合要求,如果連接符合要求就直接賦值給 StreamAllocation 對象的 connection 字段并返回,如果不符合就繼續(xù)遍歷,如果都沒有則返回空

  • 那么問題來了,在哪些情況下符合要求呢?

  • 如果這個連接對象不支持傳輸,那么就是不符合的
  • 如果這個連接的配置和要求的不一致(除了主機地址不比對),那么也是不符合要求的
  • 剛剛沒有比對主機地址,如果比對之后是一致的,那么證明所有的參數(shù)都匹配,這個連接是可以復(fù)用的

  • 如果主機地址不一樣,還需要判斷什么呢?讓我們接著往下看

  • 如果這個連接不支持 Http 2.0 協(xié)議,那么也不支持復(fù)用連接
  • 接下來就判斷兩個路由的信息是否匹配,那么問題來了,什么是路由?有什么用?

其實包含兩部分,一個是 HTTP 代理服務(wù)器地址,另一個是由 DNS 解析出來的 IP 地址,因為 HTTP 代理服務(wù)器可以有多個,而 DNS 解析出來的 IP 地址也會有多個,那么這個時候需要一個路由將這兩者進行拼合,例如代理服務(wù)器有 A 和 B,DNS 地址有 1 和 2,那么生成的路由有四個,分別是 A1、A2、B1、B2。代理服務(wù)器的作用是,例如我們平時訪問不到 Google,但是使用 VPN 就可以,這個 VPN 就是代理服務(wù)器,海外的代理服務(wù)器會替我們訪問這個網(wǎng)址并把數(shù)據(jù)返回給我們。而 DNS 解析出來的 IP 地址堆,則用于當一個 IP 地址的主機無法訪問的時候,則會切換到下一個 IP 地址再進行請求。

  • 這里判斷了是否開啟了代理,如果沒有則不能復(fù)用連接
  • 如果開啟了代理,但是代理服務(wù)器的地址不一樣,同樣也不能復(fù)用連接
  • 接著就是對網(wǎng)站的證書進行校驗了,如果證書上的 Host 地址和要請求的 Host 地址不一致,這個連接也是不能復(fù)用的

  • 通過這些信息,我們可以得出,從連接池中取出來的連接對象,并不能直接復(fù)用,而是需要滿足一定的條件才可以復(fù)用,被復(fù)用的連接必須和要當前用戶要請求的連接某些信息相匹配才可以。

  • 看完了這部分源碼,讓我們接著回去看之前的源碼

  • 如果從連接池找到合適的連接對象,那么會直接返回給上層,如果沒有呢?
  • 它會獲取路由列表,然后把路由分發(fā)給連接池去尋找是否有匹配的連接

  • 如果還是沒有找到符合要求的連接對象呢?讓我們接著往下面看

  • 如果有找到符合要求的連接對象,那么就直接返回回去,否則就創(chuàng)建一個新的連接對象,并進行 TCP 三次握手和 Https 身份校驗
  • 最后再將這個新的連接對象添加到連接池中備用

源碼總結(jié)

  • 這個攔截器主要的作用就是負責從連接池獲取健康的連接(判斷連接的 Socket 傳輸通道是否可用),如果在連接池中沒有找到符合復(fù)用的連接(除了主機地址之外的信息必須要全部匹配,例如 DNS、代理、協(xié)議、證書),那么就會使用路由再找一遍(路由的結(jié)構(gòu)是一個代理服務(wù)器的地址 + DNS 中的一個 IP 地址),如果還是沒有,那么會直接創(chuàng)建一個新的連接對象并進行三次握手,再將這個新的連接對象添加到連接池中,最后將可復(fù)用的連接返回回去。

下一篇:OkHttp 精講:CallServerInterceptor

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

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

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