vivo 短視頻用户訪問體驗優化實踐

語言: CN / TW / HK

作者:vivo 互聯網運維團隊- Hu Tao

本文介紹了vivo短視頻用户訪問體驗優化的實踐思路,並簡單講解了實踐背後的幾點原理。

一、背景

我們平時在看抖音快手視頻的時候,如果滑動到某個視頻畫面一直幾s不動的時候,大概率就會划走了,所以在短視頻項目中,畫面卡頓是非常影響用户體驗的,啟播速度越快,就越能留住用户。

啟播速度簡單來説就是從調用開始播放到首幀上屏的時間,大致可分為兩部分:

  • 視頻文件下載耗時

  • 視頻解碼耗時

本文主要從運維排查問題的角度,從網絡這部分的各個環節入手,結合vivo短視頻的具體案例,給大家分享下優化過程。

二、用户訪問鏈路

我們先梳理下一次完整的網絡請求過程,以客户端視角為例,如下圖所示:

在接入CDN的情況下,可分為幾個階段:

  1. DNS域名解析:獲取服務器的IP地址。

  2. TCP連接建立:與服務器IP建立連接即tcp三次握手。

  3. TLS握手:客户端向服務器索要並驗證服務器的公鑰,雙方協商生產「會話祕鑰」並進行加密通信。

  4. CDN響應:將內容資源分發到位於多個地理位置機房中的服務器上並返回給客户端。

針對以上階段,分別講下vivo短視頻是如何進行優化的。

三、DNS域名解析

我們在上網的時候,通常使用的方式域名,而不是 IP 地址,因為域名方便人類記憶。那麼實現這一技術的就是 DNS 域名解析,DNS 可以將域名網址自動轉換為具體的 IP 地址。

3.1 域名的層級關係

DNS 中的域名都是用句點來分隔的,比如 www.server.com,這裏的句點代表了不同層次之間的界限。在域名中,越靠右的位置表示其層級越高。根域是在最頂層,它的下一層就是 com 頂級域,再下面是 server.com

所以域名的層級關係類似一個樹狀結構:

  • 根DNS服務器

  • 頂級域 DNS 服務器(com)

  • 權威 DNS 服務器(server.com

根域的 DNS 服務器信息保存在互聯網中所有的 DNS 服務器中。這樣一來,任何 DNS 服務器就都可以找到並訪問根域 DNS 服務器了。

因此,客户端只要能夠找到任意一台 DNS 服務器,就可以通過它找到根域 DNS 服務器,然後再一路順藤摸瓜找到位於下層的某台目標 DNS 服務器。

3.2 域名解析的工作流程

瀏覽器首先看一下自己的緩存裏有沒有,如果沒有就向操作系統的緩存要,還沒有就檢查本機域名解析文件 hosts,如果還是沒有,就會 DNS 服務器進行查詢,查詢的過程如下:

  1. 客户端首先會發出一個 DNS 請求,問 www.server.com 的 IP 是啥,併發給本地 DNS 服務器(也就是客户端的 TCP/IP 設置中填寫的 DNS 服務器地址)。

  2. 本地域名服務器收到客户端的請求後,如果緩存裏的表格能找到 www.server.com,則它直接返回 IP 地址。如果沒有,本地 DNS 會去問它的根域名服務器:“老大, 能告訴我 www.server.com 的 IP 地址嗎?” 根域名服務器是最高層次的,它不直接用於域名解析,但能指明一條道路。

  3. 根 DNS 收到來自本地 DNS 的請求後,發現後置是 .com,説:“www.server.com 這個域名歸 .com 區域管理”,我給你 .com 頂級域名服務器地址給你,你去問問它吧。”

  4. 本地 DNS 收到頂級域名服務器的地址後,發起請求問“老二, 你能告訴我 www.server.com  的 IP 地址嗎?”

  5. 頂級域名服務器説:“我給你負責 www.server.com 區域的權威 DNS 服務器的地址,你去問它應該能問到”。

  6. 本地 DNS 於是轉向問權威 DNS 服務器:“老三,www.server.com對應的IP是啥呀?” server.com 的權威 DNS 服務器,它是域名解析結果的原出處。為啥叫權威呢?就是我的域名我做主。

  7. 權威 DNS 服務器查詢後將對應的 IP 地址 X.X.X.X 告訴本地 DNS。

  8. 本地 DNS 再將 IP 地址返回客户端,客户端和目標建立連接,同時本地 DNS 緩存該 IP 地址,這樣下一次的解析同一個域名就不需要做 DNS 的迭代查詢了。

至此,我們完成了 DNS 的解析過程。現在總結一下,整個過程畫成了一個圖。

圖片

DNS 域名解析的過程蠻有意思的,整個過程就和我們日常生活中找人問路的過程類似,只指路不帶路

3.3 vivo短視頻所做的優化

弄清了域名解析的工作流程,將vivo域名和頭部廠商域名進行對比分析,發現vivo短視頻域名解析耗時不穩定,波動範圍很大,懷疑是某些地區用户訪問量少,本地DNS服務器緩存命中率低導致的,因此我們的優化思路就是 提高本地DNS緩存命中率

如上圖所示,提高DNS緩存命中率一個簡單的辦法就是添加全國範圍內的撥測任務,來進行DNS加熱

通過對調整前後的DNS解析時間進行比較,可以看到耗時降低了30ms左右。

四、HTTP性能

這裏簡單對比下 HTTP/1、HTTP/2、HTTP/3 的性能。

HTTP 協議是基於TCP/IP,並且使用了「請求 - 應答」的通信模式,所以性能的關鍵就在這兩點裏。

1. 長連接

早期 HTTP/1.0 性能上的一個很大的問題,那就是每發起一個請求,都要新建一次 TCP 連接(三次握手),而且是串行請求,做了無謂的 TCP 連接建立和斷開,增加了通信開銷。

為了解決上述 TCP 連接問題,HTTP/1.1 提出了長連接的通信方式,也叫持久連接。這種方式的好處在於減少了 TCP 連接的重複建立和斷開所造成的額外開銷,減輕了服務器端的負載。

持久連接的特點是,只要任意一端沒有明確提出斷開連接,則保持 TCP 連接狀態。

2. 管道網絡傳輸

HTTP/1.1 採用了長連接的方式,這使得管道(pipeline)網絡傳輸成為了可能。

即可在同一個 TCP 連接裏面,客户端可以發起多個請求,只要第一個請求發出去了,不必等其回來,就可以發第二個請求出去,可以減少整體的響應時間

舉例來説,客户端需要請求兩個資源。以前的做法是,在同一個TCP連接裏面,先發送 A 請求,然後等待服務器做出迴應,收到後再發出 B 請求。管道機制則是允許瀏覽器同時發出 A 請求和 B 請求。

但是服務器還是按照順序,先回應 A 請求,完成後再回應 B 請求。要是 前面的迴應特別慢,後面就會有許多請求排隊等着。這稱為「隊頭堵塞」。

3. 隊頭阻塞

「請求 - 應答」的模式加劇了 HTTP 的性能問題。

因為當順序發送的請求序列中的一個請求因為某種原因被阻塞時,在後面排隊的所有請求也一同被阻塞了,會招致客户端一直請求不到數據,這也就是「隊頭阻塞」。好比上班的路上塞車。

4.1 HTTP/1.1 相比 HTTP/1.0 性能上的改進:

  • 使用 TCP 長連接的方式改善了 HTTP/1.0 短連接造成的性能開銷。

  • 支持 管道(pipeline)網絡傳輸,只要第一個請求發出去了,不必等其回來,就可以發第二個請求出去,可以減少整體的響應時間。

但 HTTP/1.1 還是有性能瓶頸:

  • 請求 / 響應頭部(Header)未經壓縮就發送,首部信息越多延遲越大。只能壓縮 Body 的部分;

  • 發送宂長的首部。每次互相發送相同的首部造成的浪費較多;

  • 服務器是按請求的順序響應的,如果服務器響應慢,會招致客户端一直請求不到數據,也就是隊頭阻塞;

  • 沒有請求優先級控制;

  • 請求只能從客户端開始,服務器只能被動響應。

針對上面的 HTTP/1.1 的性能瓶頸,HTTP/2 做了一些優化。而且因為 HTTP/2 協議是基於 HTTPS 的,所以 HTTP/2 的安全性也是有保障的。

4.2 HTTP/2 相比 HTTP/1.1 性能上的改進:

1. 頭部壓縮

HTTP/2 會壓縮頭(Header)如果你同時發出多個請求,他們的頭是一樣的或是相似的,那麼,協議會幫你消除重複的部分

這就是所謂的 HPACK 算法:在客户端和服務器同時維護一張頭信息表,所有字段都會存入這個表,生成一個索引號,以後就不發送同樣字段了,只發送索引號,這樣就提高速度了。

2. 二進制格式

HTTP/2 不再像 HTTP/1.1 裏的純文本形式的報文,而是全面採用了二進制格式

頭信息和數據體都是二進制,並且統稱為幀(frame):頭信息幀和數據幀

這樣雖然對人不友好,但是對計算機非常友好,因為計算機只懂二進制,那麼收到報文後,無需再將明文的報文轉成二進制,而是直接解析二進制報文,這增加了數據傳輸的效率

3. 數據流

HTTP/2 的數據包不是按順序發送的,同一個連接裏面連續的數據包,可能屬於不同的迴應。因此,必須要對數據包做標記,指出它屬於哪個迴應。

每個請求或迴應的所有數據包,稱為一個數據流(Stream)。

每個數據流都標記着一個獨一無二的編號,其中規定客户端發出的數據流編號為奇數, 服務器發出的數據流編號為偶數。

客户端還可以指定數據流的優先級。優先級高的請求,服務器就先響應該請求。

4. 多路複用

HTTP/2 是可以在一個連接中併發多個請求或迴應,而不用按照順序一一對應

移除了 HTTP/1.1 中的串行請求,不需要排隊等待,也就不會再出現「隊頭阻塞」問題,降低了延遲,大幅度提高了連接的利用率

舉例來説,在一個 TCP 連接裏,服務器收到了客户端 A 和 B 的兩個請求,如果發現 A 處理過程非常耗時,於是就迴應 A 請求已經處理好的部分,接着迴應 B 請求,完成後,再回應 A 請求剩下的部分。

5. 服務器推送

HTTP/2 還在一定程度上改善了傳統的「請求 - 應答」工作模式,服務不再是被動地響應,也可以主動向客户端發送消息。

舉例來説,在瀏覽器剛請求 HTML 的時候,就提前把可能會用到的 JS、CSS 文件等靜態資源主動發給客户端,減少延時的等待,也就是服務器推送(Server Push,也叫 Cache Push)。

4.3 那麼HTTP/2 有哪些缺陷?HTTP/3 做了哪些優化?

HTTP/2 通過頭部壓縮、二進制編碼、多路複用、服務器推送等新特性大幅度提升了 HTTP/1.1 的性能,而美中不足的是 HTTP/2 協議是基於 TCP 實現的,於是存在的缺陷有三個。

  • TCP 與 TLS 的握手時延遲;

  • 隊頭阻塞;

  • 網絡遷移需要重新連接。

1. TCP 與 TLS 的握手時延遲

對於 HTTP/1 和 HTTP/2 協議,TCP 和 TLS 是分層的,分別屬於內核實現的傳輸層、openssl 庫實現的表示層,因此它們難以合併在一起,需要分批次來握手,先 TCP 握手,再 TLS 握手。

發起 HTTP 請求時,需要經過 TCP 三次握手和 TLS 四次握手(TLS 1.2)的過程,因此共需要 3 個 RTT 的時延才能發出請求數據。

圖片

另外, TCP 由於具有「擁塞控制」的特性,所以剛建立連接的 TCP 會有個「慢啟動」的過程,它會對 TCP 連接產生"減速"效果。

2. 隊頭阻塞

HTTP/2 實現了 Stream 併發,多個 Stream 只需複用 1 個 TCP 連接,節約了 TCP 和 TLS 握手時間,以及減少了 TCP 慢啟動階段對流量的影響。不同的 Stream ID 才可以併發,即使亂序發送幀也沒問題,但是同一個 Stream 裏的幀必須嚴格有序。另外,可以根據資源的渲染順序來設置 Stream 的優先級,從而提高用户體驗。

HTTP/2 通過 Stream 的併發能力,解決了 HTTP/1 隊頭阻塞的問題,看似很完美了,但是 HTTP/2 還是存在“隊頭阻塞”的問題,只不過問題不是在 HTTP 這一層面,而是在 TCP 這一層。

HTTP/2 多個請求是跑在一個 TCP 連接中的,那麼當 TCP 丟包時,整個 TCP 都要等待重傳,那麼就會阻塞該 TCP 連接中的所有請求。

因為 TCP 是字節流協議,TCP 層必須保證收到的字節數據是完整且有序的,如果序列號較低的 TCP 段在網絡傳輸中丟失了,即使序列號較高的 TCP 段已經被接收了,應用層也無法從內核中讀取到這部分數據,從 HTTP 視角看,就是請求被阻塞了。

舉個例子,如下圖:

圖片

圖中發送方發送了很多個 packet,每個 packet 都有自己的序號,可以認為是 TCP 的序列號,其中 packet 3 在網絡中丟失了,即使 packet 4-6 被接收方收到後,由於內核中的 TCP 數據不是連續的,於是接收方的應用層就無法從內核中讀取到,只有等到 packet 3 重傳後,接收方的應用層才可以從內核中讀取到數據,這就是 HTTP/2 的隊頭阻塞問題,是在 TCP 層面發生的。

3. 網絡遷移需要重新連接

一個 TCP 連接是由四元組(源 IP 地址,源端口,目標 IP 地址,目標端口)確定的,這意味着如果 IP 地址或者端口變動了,就會導致需要 TCP 與 TLS 重新握手,這不利於移動設備切換網絡的場景,比如 4G 網絡環境切換成 WIFI。

這些問題都是 TCP 協議固有的問題,無論應用層的 HTTP/2 在怎麼設計都無法逃脱。

要解決這個問題,HTTP/3 就將傳輸層協議從 TCP 替換成了 UDP,並在 UDP 協議上開發了 QUIC 協議,來保證數據的可靠傳輸。

圖片

4.4 QUIC 協議的特點

  • 無隊頭阻塞,QUIC 連接上的多個 Stream 之間並沒有依賴,都是獨立的,也不會有底層協議限制,某個流發生丟包了,只會影響該流,其他流不受影響;

  • 建立連接速度快,因為 QUIC 內部包含 TLS1.3,因此僅需 1 個 RTT 就可以「同時」完成建立連接與 TLS 密鑰協商,甚至在第二次連接的時候,應用數據包可以和 QUIC 握手信息(連接信息 + TLS 信息)一起發送,達到 0-RTT 的效果。

  • 連接遷移,QUIC 協議沒有用四元組的方式來“綁定”連接,而是通過連接 ID 來標記通信的兩個端點,客户端和服務器可以各自選擇一組 ID 來標記自己,因此即使移動設備的網絡變化後,導致 IP 地址變化了,只要仍保有上下文信息(比如連接 ID、TLS 密鑰等),就可以“無縫”地複用原連接,消除重連的成本。

另外 HTTP/3 的 QPACK 通過兩個特殊的單向流來同步雙方的動態表,解決了 HTTP/2 的 HPACK 隊頭阻塞問題。

不過,由於 QUIC 使用的是 UDP 傳輸協議,UDP 屬於“二等公民”,大部分路由器在網絡繁忙的時候,會丟掉 UDP包,把“空間”讓給 TCP 包,所以 QUIC 的推廣之路應該沒那麼簡單。期待,HTTP/3 正式推出的那一天

4.5 vivo短視頻所做的優化

隨着vivo短視頻發展的不同時期,我們做了不同的優化:

1. 使用HTTP/1.1:客户端將首幀圖片域名和評論頭像域名進行合併,TCP鏈接複用率提高4%,平均圖片加載耗時下降40ms左右;

圖片

2.使用HTTP/2:客户端在部分域名上灰度使用H2,卡頓率下降0.5%

3. 使用QUIC:客户端針對弱網場景,優先使用QUIC協議;同時針對短視頻業務特點,專項優化QUIC性能。

圖片

五、CDN 加速

CDN 的全稱叫 Content Delivery Network,中文名叫「內容分發網絡」,它是解決由於長距離而網絡訪問速度慢的問題。

簡單來説,CDN 將內容資源分發到位於多個地理位置機房中的服務器上,這樣我們在訪問內容資源的時候,不用訪問源服務器。而是直接訪問離我們最近的 CDN 節點 ,這樣一來就省去了長途跋涉的時間成本,從而實現了網絡加速。

CDN 加速的是內容資源是靜態資源。

所謂的「靜態資源」是指數據內容靜態不變,任何時候來訪問都是一樣的,比如圖片、音頻。與之相反的「動態資源」,是指數據內容是動態變化的,每次訪問都不一樣,比如用户信息等。不過,動態資源如果也想被緩存加速,就要使用動態 CDN,其中一種方式就是將數據的邏輯計算放在 CDN 節點來做,這種方式就被稱為邊緣計算。

CDN 加速策略有兩種方式,分別是「推模式」和「拉模式」。

大部分 CDN 加速策略採用的是「拉模式」,當用户就近訪問的 CDN 節點沒有緩存請求的數據時,CDN 會主動從源服務器下載數據,並更新到這個 CDN 節點的緩存中。可以看出,拉模式屬於被動緩存的方式,與之相反的 「推模式」就屬於主動緩存的方式。如果想要把資源在還沒有用户訪問前緩存到 CDN 節點,則可以採用「推模式」,這種方式也叫 CDN 預熱。通過 CDN 服務提供的 API 接口,把需要預熱的資源地址和需要預熱的區域等信息提交上去,CDN 收到後,就會觸發這些區域的 CDN 節點進行回源來實現資源預熱。

5.1 如何找到離用户最近的 CDN 節點 ?

找到離用户最近的 CDN 節點是由 CDN 的全局負載均衡器(Global Sever Load Balance,GSLB)負責的。那 GSLB 是在什麼時候起作用的呢?在回答這個問題前,我們先來看看在沒有 CDN 的情況下,訪問域名時發生的事情。在沒有 CDN 的情況下,當我們訪問域名時,DNS 服務器最終會返回源服務器的地址。比如,當我們在瀏覽器輸入 www.server.com 域名後,在本地 host 文件找不到域名時,客户端就會訪問本地 DNS 服務器。

這時候:

  • 如果本地 DNS 服務器有緩存該網站的地址,則直接返回網站的地址;

  • 如果沒有就通過遞歸查詢的方式,先請求根 DNS,根 DNS 返回頂級 DNS(.com)的地址;再請求 .com 頂級 DNS 得到 server.com 的域名服務器地址,再從 server.com 的域名服務器中查詢到 www.server.com 對應的 IP 地址,然後返回這個 IP 地址,同時本地 DNS 緩存該 IP 地址,這樣下一次的解析同一個域名就不需要做 DNS 的迭代查詢了。

但加入 CDN 後就不一樣了。

圖片

會在 server.com 這個 DNS 服務器上,設置一個 CNAME 別名,指向另外一個域名 www.server.cdn.com,返回給本地 DNS 服務器。接着繼續解析該域名,這個時候訪問的就是 server.cdn.com 這台 CDN 專用的 DNS 服務器,在這個服務器上,又會設置一個 CNAME,指向另外一個域名,這次指向的就是 CDN 的 GSLB。接着,本地 DNS 服務器去請求 CDN 的 GSLB 的域名,GSLB 就會為用户選擇一台合適的 CDN 節點提供服務,選擇的依據主要有以下幾點:

  • 看用户的 IP 地址,查表得知地理位置,找相對最近的 CDN 節點;

  • 看用户所在的運營商網絡,找相同網絡的 CDN 節點;

  • 看用户請求 URL,判斷哪一台服務器上有用户所請求的資源;

  • 查詢 CDN 節點的負載情況,找負載較輕的節點。

GSLB 會基於以上的條件進行綜合分析後,找出一台最合適的 CDN 節點,並返回該 CDN 節點的 IP 地址給本地 DNS 服務器,然後本地 DNS 服務器緩存該 IP 地址,並將 IP 返回給客户端,客户端去訪問這個 CDN 節點,下載資源。

5.2 vivo短視頻所做的優化

通過我們分析後發現,部分接入CDN的域名在全國一些省份沒有就近訪問, CDN邊緣節點跨地區覆蓋問題比較嚴重。於是我們找CDN廠商針對性的做了調整,調整後平均請求耗時降到300ms左右,首包耗時也降到100多。

圖片

圖片

六、總結與展望

隨着業務的發展,提升用户體驗也會變得越來越重要,用户訪問體驗優化將是一個永無止境的過程,除了上面説的這些方法之外,還有一些優化也是我們嘗試過的,比如:

  • 連接優化:視頻播放上下滑動會頻繁的斷開連接,無法複用連接。

  • 預緩存文件:減少啟動耗時。

  • 內容優化:減少文件傳輸大小。

希望本文能為大家在日常工作中優化用户訪問體驗問題帶來幫助。