网易云音乐网络库跨平台化实践
導讀:?2021年10月21日,「QCon 全球軟件開發大會」在上海舉辦,網易智企技術 VP 陳功作為出品人發起了「AI 時代下的融合通信技術」專場,邀請到網易云信、網易音視頻實驗室、網易云音樂的技術專家與大家一起分享融合通信技術趨勢和演進方向、視頻通信關鍵技術探索及實踐、音頻 AI 算法在 RTC 中的實踐、網易云音樂網絡庫跨平臺化實踐等話題。
我們會針對四個演講專題逐一進行介紹與分享,本期是我們的第四期,網易云音樂網絡庫跨平臺化實踐。
嘉賓介紹:陳松茂,2020年底加入網易云音樂,一站式網絡解決方案技術負責人,目前從事跨平臺網絡解決方案相關研究,旨在降低多端網絡工作的研發成本,以及多應用間的接入成本,用較低的成本獲得持續可觀的性能及能效提升。曾就職于阿里,長期從事于 Chromium 相關技術研究和應用,擁有豐富的瀏覽器開發和內核升級經驗。
前言
在做網絡優化的過程中,由于系統網絡庫的差異,你不得不把同一類型的優化,在各端進行適配,你感覺工作量在成倍增長。
正因為多種適配版本的存在,一致性無法完全保證,另外由于系統網絡庫提供的數據采集能力不同,你很難采集到完全對齊的數據,這意味著服務端需要做一些兼容。
隨著移動互聯網的崛起,PC 在變得小眾化,但這不代表 PC 不需要對應的網絡優化或監控服務,但你確實無暇顧及。大廠可以不惜代價,籌建龐大的團隊深入協議棧進行重新開發優化,但你希望只投入有限的資源,就可以換來可觀的網絡性能提升。
不知道這些場景是否有戳中你的痛點,或者你曾經也被困擾過?如果我告訴你,接下來的分享能幫你解決上述所有的問題,你是否有興趣了解下?如果我告訴你,你可能什么都不需要做,就能輕松讓網絡的響應時間縮短 30%~40%,你是否更有興趣了?
本次演講主題為:網易云音樂網絡庫跨平臺化實踐。本文聚焦跨平臺化,主要涉及工程化相關思路,分四個部分,分別是背景介紹、方案設計、落地實踐及后續規劃。
?
背景介紹
下圖是當前云音樂網絡庫在各端的架構,可以看到上層的網絡策略和監控服務等等在各端都有獨立的實現,這會帶來如下問題:
重復造輪子:所有的網絡策略、質量監控,每個端都有實現。當你需要加一個新的策略的時候,每個端都得實現一遍,這是嚴重的人員浪費。
缺乏一致性:網絡庫及人的差異,導致輪子的標準和完成度不一致。
資源不對等:移動端研發比較多,但 PC 端研發就很少,這導致 PC 端很多網絡策略和質量監控都是殘缺的。
深度優化難:對網絡庫來說,要深層次的優化,需要對網絡模塊有充分的理解。但是這當下,我們可以在 Android 上進入深度的挖掘,但是不可能四個端里面都有做網絡的同學來做專門的優化。這導致每個端只做一些淺層次的優化,不會做深層次的挖掘,因為沒有人。
通過前面的分析,大家可以清楚的看到,其實每個端都實現一套,是不靠譜的。結合當下面臨的一些痛點,自然而然產生了一個共同的訴求:讓所有的端都共享一套網絡解決方案。再進一步,是不是所有的 App 都能共享一套網絡解決方案。
解決上述問題最主要的做法是整體方案跨平臺化,但也面臨著不少挑戰。
-
首先是跨平臺性。功能層面上是不是只有策略需要下沉,策略下沉以后,三方 SDK 是否能下沉?另外跨平臺方案有很多,可以僅僅是移動端的跨平臺化,也可以是移動端加桌面端都跨平臺化。
-
其次是能力復用。使用 C++ 重寫的網絡策略能不能在不同場景下復用,除了重構業務,是不是可以提煉一些框架?這些框架是不是能復用?另外后續新的需求出來了,是不是能在現有的基礎上很輕易的擴展上去?
-
最后是后期推廣。一開始建立這個項目的時候就明確后期需要進行推廣,不僅僅提供給網易云音樂使用。所以,其它 App 在接入網絡庫時成本怎么樣?是不是能很簡單的接入?這決定著后期能否順利推廣。另外,每個 App 都有各自的特點,比如網易云音樂有免流功能,但其它 App 可能不需要免流,換句話說,不同的 App 可以根據實際情況進行定制,這也是我們面對的挑戰。
針對上述挑戰進行的方案設計,是整個分享的核心。
方案設計
?跨平臺設計思路?
一開始設計思路很簡單,下圖中右側綠色部分是用 C++ 重寫的包含核心邏輯的 SDK,在這個基礎上,由 Android、iOS 分別接入這個 SDK,底下的網絡庫不變。這個改造成本很低,但接入成本較高,移動端接入 SDK 的時候,需要對我們提供的 SDK 進行二次包裝。于是我們就在想,能不能接入的時候不要搞五六個 SDK,合并成一個 SDK?
?
接下來就有另外一個方案,我們想把上層的綠色部分全部合并到一個 SDK,這需要在現有的網絡庫之上抽象出網絡代理層,基于網絡代理,上面做各種策略和業務邏輯封裝,這樣才能把所有邏輯合并到一個 SDK 里面,這個做完了以后整體接入成本就會大大降低。但網絡代理層很厚,隨著時間的遷移,底下的網絡庫會變,上面的網絡代理層需要不斷適配,這樣適配工作就會沒完沒了。
?
更進一步思考,能不能讓網絡庫統一掉?我們把上面的網絡代理層抽掉,選用一個通用的跨平臺網絡庫進行替代,在這基礎之上,封裝我們的策略和服務,這樣整體鏈路就跨平臺化了,這是一個思路。
?
最后小結一下,跨平臺的方法有很多,在網易云音樂,我們選擇了最徹底的一種,即整個網絡解決方案包括網絡庫、上層策略以及一些服務全部跨平臺化。
?跨平臺網絡庫設計?
上面講了跨平臺最關鍵的點就是選擇一個合適的跨平臺網絡庫,我們選擇了 Cronet,它跨五端,完全能支持云音樂的場景;協議層面支持傳統的 HTTP、HTTP/2,QUIC;另外普及程度上,Google 系全部用了 Cronet,國內百度、微博、網易傳媒,已經接入了官方的 Cronet,他們在官方 Cronet 基礎上,各端做了自己的策略包括一些監控服務。頭條系、蘑菇街改造比較徹底,在 Cronet 基礎上進行二次定制開發,形成了自己的網絡庫;自從 Chromium 開源以后,國內所有瀏覽器都是基于這個項目做的,并在不斷的演進中,非?;钴S;最后一點是開源協議,Chromium 主要使用的是 BSD 協議,也就是說我們基于 Cronet 做的修改,不需要強制開源,這對商業公司來說,很關鍵,也是我們選擇 Cronet 的重要考量。
?
?
下圖是整個跨平臺網絡庫的整體架構。
?
最底下分別是 OS、Base 和 Net;我們在?Net 的基礎上封裝了一層 Common API,作為膠水層隔離 Net,并擴展一些自己的基礎能力,包括 BI、FunctionBridge、插拔服務等;在通用能力基礎上又做了一層組件,包括網絡策略、APM 監控、HTTPDNS 服務等等;最后接口層,我們對外暴露了組件 API,方便 App 調用。
?
我們可以通過 Cronet API 進行網絡收發,并在這個基礎上新增了部分擴展 API(比如原先的 Cronet API 不支持超時[包括建連和包間超時]設置;對于成功和失敗的請求,也不支持返回具體的 IP;另外,對于 HTTPDNS 支持也不是很方便)。
對于 App 來說,除了看到 Cronet,還能看到一個組件系統。App 通過統一配置,把內部的組件按需啟用(比如這里默認會啟用 HTTPDNS 和 APM,但是對于網絡策略就不一定需要);理論上組件之間是完全隔離的,但是有一些特殊場景,比如網絡策略需要跟 HTTPDNS 組件做一些通訊,我們內部封裝了一些可插拔的服務,HTTPDNS 組件將能力暴露到插拔服務中,網絡策略借助插拔服務對 HTTPDNS 組件進行調用;App 通過調用橋接(類似于 JSBridge)直接與組件進行通訊,這樣后續組件接入和擴展起來成本就很低了。
組件不直接和網絡內核通訊,我們在網絡內核基礎上封裝了基礎能力,包括一些必要的請求攔截,請求轉發及網絡監控能力。
?
我們希望通過這次網絡庫跨平臺化實踐,沉淀出一個可復用的網絡框架:這部分網絡框架可以在網易內部復用,不包含業務邏輯,僅僅是一個框架,里面會定期更新 Cronet 內核,包括一些安全補丁(我們也接過 Cronet 官方版本,運行一段時間后發現,線上有一些 Crash,這個需要我們通過打補丁的方式修復);我們在 Cronet 的基礎上提供一些基礎的組件管理和基礎能力封裝,旨在降低 C++ 級別定制成本。
另一方面,基于可復用網絡框架,我們擴展了一部分能力集:所有業務都放在能力集里面,變成三方庫,以組件的形式進行復用;接入方可以根據自身需求,靈活組合;能力集可以提供通用的能力(比如 APM 監控、HTTPDNS 服務),也可以提供個性化的能力(比如免流服務),旨在滿足接入方多變的業務場景和定制述求。
最后,簡單總結一下網絡庫設計,我們直接基于開源的 Cronet 方案,構建了云音樂自己的統一網絡庫方案,并采用了“可復用網絡框架+可擴展能力集”的模式進行業務下沉。
?Cronet 升級?
我們在選這個方案時候經過了大量的網上調研,我們發現大部分公司在方案選型時,選擇了直接使用 Cronet,而不是基于 Cronet 進行二次開發定制。他們都提到了一個共同點,如果對 Cronet 進行了定制,后續內核升級把控起來難度有點大或者成本會很高,這是他們不選這個方案的最主要理由。
那么面對升級我們該怎么辦呢?首先講一下為什么要升級,升級原因很簡單,一個是要修復問題(比如出現一個安全漏洞,希望通過升級或者修補丁的方式修復這個問題);另外一個是獲得特性(比如現在的 QUIC 在快速演進中,國內主流用的都是 gQUIC,但是 iQUIC 標準正在慢慢的統一,Google 也在逐漸向 iQUIC 靠攏,我們希望通過升級直接支持 iQUIC);最后,Google 對 Cronet 也在持續進行優化,我們希望通過升級,直接享受到對應的優化成果。
那升級具體有哪些痛點呢?簡單點講,可以把升級類比成一次代碼提交:如果你提交足夠及時,修改一部分就馬上提交,你幾乎不會遇到沖突,很順利;有些同學習慣不好,寫了兩三天的代碼,才一次性提交,這時候就比較容易遇到代碼沖突,可能需要花點時間才能解決;如果繼續放慢代碼提交的節奏,像我們對 Cronet 的二次開發,可能半年或一年甚至更久,也不會與官方的最新 Cronet 進行合并,等到需要進行 Cronet 升級時,你會發現新版 Cronet 框架可能都變了,這個時候合并代碼就非常痛苦了。
以前,我們做瀏覽器,每次進行內核升級,就有幾千上萬個文件沖突,光合并代碼這部分要花半個月的時間。另外代碼合并的時候,你其實不知道怎么合并它,變化太多了,合并很麻煩。最后一個,即便僥幸編譯鏈接通過了,你會發現很多功能衰退。
基于此,我們想了一些對應的解決思路,比如對于代碼沖突來說,我們基于源碼二次定制時盡量減少修改,盡量做好隔離讓修改的地方能夠很輕易的辨識出來。這樣合并者可以很清楚的知道這個地方要合,這個地方不要合。對功能衰退最有效的方法就是做單測,Chromium 項目本身單測覆蓋率很廣,你只要在新增的代碼上做一些單測覆蓋就夠了。
下面重點講下,如何減少侵入并做好隔離,這兩點其實是很簡單的技巧,沒有什么難度,大家可以很輕易的參考并實踐。
減少侵入我們是這樣考慮的,主要是三個詞:一個是提接口,第二個是基于接口搭框架,最后基于框架擴組件。
?
?
對 Cronet 的侵入,我們更多以接口的方式進行,而不是直接魔改。提接口包括新增一些代理、觀察者或者攔截器(運用了部分設計模式的思路),把所有的修改都匯聚到幾個小的點上,整個實踐下來,提接口這部分,我們改造了快一年了,接口部分只占修改里面的 5% 甚至更少;基于新增的接口,我們搭了一個網絡框架,網絡框架主要做一些能力封裝,包括組件機制和通道封裝,再包括一些插拔服務等等;最后,我們會把各種策略、監控、業務都往組件上堆,整個下來,對源碼的侵入程度控制得還是可以的。
下圖代碼是我們對 Cronet 源碼進行修改的一處示例。左邊是源文件關于 Socket 復用的代碼,右邊做了一些改造,我們做了一個回調,由業務側決定是否可以進行 Socket 復用。乍一眼看,無法察覺兩者的差異,這給內核升級帶來一個很嚴重的挑戰。我們代碼完全沒加任何隔離措施,一合并完全不知道自己做了哪些修改,完全沒法合并。
怎么辦呢?有同學可能會想到加注釋這一類的。以前,剛開始做瀏覽器的時候,我們團隊也是用加注釋這種方式,這種方式簡單“高效”,但總感覺有一點不舒服,雖然進行了隔離,但是有時候定位一個問題,你覺得這個 bug 不應該是 Cronet 的,應該是我們修改出來的,這時候,你可能需要編譯原始代碼進行驗證,但是加注釋的方式導致你無法快速編譯出一個源版本。
對于 C++ 來說其實很簡單,就是加一個宏開關,通過宏開關你可以很清楚的看到哪些地方改動了,一目了然,并且可以快速進行源切換,這是以前我所在的團隊長期實踐下來的經驗。
大家看到所有修改都把源代碼放在前面,把我們的修改放在后面,為什么要這樣做?有一個很大的好處是,上面的代碼因為都不是你修改的,合并的過程中不太會有沖突,后續如果有問題,你可以通過合并工具快速的識別差異。
對于 C++ 以外的代碼,我們也做了與宏開關類似的開關進行代碼隔離,如下圖所示。
?
最后我們還做了文件級別的隔離。紅框部分是新增的文件目錄,我們把修改全部放在 wow 下面,把所有修改都隔離在自己的文件里面。舉一個例子,net 目錄下有一個文件,我們對它進行了修改,我們會把新建的文件放在 wow 下的 net 目錄中,并對新增的文件加上 wow 前綴。
Cronet 升級無法避免,也沒有銀彈,我們在這里采用低成本、可推行的“技巧”,讓整個升級變得相對簡單一點。
?
?避坑指南?
解決了升級的顧慮,你是不是已經躍躍欲試了?別急,你還需要最后一步,先讓 Cronet 代碼跑起來。
下圖是 Cronet 官方的文檔,對源代碼的 check out、build and run 給出了詳盡的描述,你只需要對著文檔一步步操作就行了。由于 Cronet 未單獨開源,Cronet 與 Chromium 的代碼是混在一起的,共用同一個代碼倉庫和構建系統,所以代碼拉取和編譯環境準備與 Chromium 一致,對于 Cronet 的編譯,Google 給出了單獨的文檔描述,并且為 Android 和 iOS 提供了單獨的編譯腳本,簡單點說就是一條命令行的事情。
年初我們對 Cronet 進行了調研,那時 Chromium 的內核版本是 M88,整個拉代碼編譯調試都很順利,當然我們只測試了 Windows 端。我們接入 Cronet 的初衷只是為跨平臺化,但隨著調研的深入,我們看到了各大互聯網公司關于 QUIC 方面的實踐,他們給出了不同版本的性能提升數據,由于 Cronet 天然支持 QUIC,我們就想,等完成網絡庫跨平臺化改造后,我們也開啟 QUIC,看下效果,如果真有提升,那就更好了。
但當我們對 QUIC 進行一番調研后,我們發現國內云廠商主要支持的 QUIC 版本是 gQUIC 43,并且 Chromium M88 版本已經不默認支持 gQUIC43,也就是說我們不得不回退到某個默認支持 gQUIC 43 的低版本 Chromium,為了更好的做數據對比并降低 QUIC 接入的風險,我們選取了與傳媒一致的版本 M72。
由于 M72 與 M88 隔了近2年的時間,當我們將 Chromium 代碼切回 M72 時,我們發現無論是 Windows、Ubuntu、Mac 下,都編譯不過,有各種奇怪的問題。搗騰了很久,后來才猛然意識的,可能是文檔的問題,畢竟2年過去了,怎么找到2年前 M72 對應的官方文檔呢?
Chromium 的官方文檔和代碼是統一管理的,你只需在 src 目錄下找到對應的文檔即可,或者線上直接根據 tags 找到對應的文檔即可,有了匹配的文檔,各種編譯鏈接問題就少多了。
?
下圖是根據官方文檔和我們的使用習慣整理的 Cronet 在各端下的開發工具、編譯環境和構建系統。唯一需要強調的是,在編譯 iOS 版本時,需要將 XCode 中的 Command line tools 指向低版本、Win10 下記得安裝對應的 SDK。另外,整個 SDK 的構建,使用了 GN+Ninja 的方式,可能剛接觸的同學會有一點不適應,其實熟悉幾天就可以上手了。
另外一個是調試。做開發的都知道,學一個東西很簡單,就是把代碼拉下來編譯,然后 Debug。出乎我們意料的是,在 Android 下原生不支持調試 Cronet SDK,官方推薦一個是 VLOG,第二個是 NetLog。因為我們三端都有對應的開發同學,比如我習慣 Windows,直接基于 Cronet 建一個 Demo 進行調試,大部分場景都能覆蓋。如果覆蓋不了就用 VLOG 調試一下,這個問題是一個痛點,暫時還沒有找到很好的解決方法。
?
最后是?App 接入。使用 Cronet 發起請求,Google 提供兩種實現,一種是異步的 UrlRequest,另一種是符合移動端協議標準的,在 UrlRequest 基礎上封裝了流操作的 HttpURLConnection 和 NSURLProtocol 實現。
由于我們對網絡請求進行了一些接口擴展,左側的方案只要修改 URLRequest,右側的方案既需要修改 UrlRequest,又需要修改上層的協議封裝,出于減少對 Cronet 侵入的考慮,我們更傾向于所有的端都使用同一套網絡接口,即 UrlRequest,事實上在主站 API 請求接入的過程中,我們的確是這樣操作的。但是當接入 CDN 請求時,面對流操作,UrlRequest 使用起來并不太方便,業務側需要更多的適配和修改才能接入,最后出于讓 App 更低成本接入方面的考慮,我們還是整體切換到了使用 HttpURLConnection 和 NSURLProtocol 的方式。
?
這里面有一些試錯成本,分享給大家,大家可以少走點彎路。避坑指南主要是基于 Cronet 進行二次開發相關,如何讓代碼跑起來只是一個小坑,跳過了以后就是康莊大道。
落地實踐
我們一開始對 Cronet 不太了解,先接入了官方的 Cronet SDK,進行了試驗;再基于 Cronet 做一些網絡框架的搭建,然后將業務組件逐個下沉到網絡庫(用 C++ 重寫);下沉完以后在 Android 端進行了落地(放量中);然后逐步把 QUIC 開啟,代碼里面只要一行代碼開啟就行了;最后,iOS 端也在接入過程中。
線上數據方面,Cronet 在未開啟 QUIC 的情況下,響應時間較 OK 提升 16%~20%,開啟 QUIC 后,響應時間進一步提升到 37%~41%。這個 40% 的性能提升非??捎^,你可能優化一年也趕不上這個效果。我們為了把上層業務跨平臺化,最終選擇將底層網絡庫也一起跨平臺化,因為方案選型的原因,我們直接享受了 Cronet 帶來的網絡性能的優化成果。
后續規劃
整個跨平臺網絡庫,已經在網易云音樂的主 App 側部分落地了,接下來會把 Windows 和 Mac 覆蓋掉;網絡庫,在云音樂主 App 完全落地后,我們會在云音樂產品矩陣中逐個落地;未來,我們會在網易內部進行推廣。
Cronet 接入了,后面的調優之路才剛剛開始,包括預連接、參數調優、“競速”優化、連接復用率、連接遷移、QUIC 集群獨立部署等。
今年 QUIC 的標準化版本已經出來了。未來,等時機成熟,我們會通過升級 Cronet 內核版本直接支持 iQUIC。
我們實踐下來網絡庫是最值得和最應該進行跨平臺化改造的;推薦 Cronet 作為跨平臺網絡庫的首選,稍微有一點點門檻,但是這個門檻不高;我們線上數據 Cronet 在 HTTP/2 的基礎上已經有不錯的性能優勢,大概 20% 左右,開啟 QUIC 后優勢進一步放大;對于接入 Cronet,一開始我們也是觀望的,你可以跟百度、微博、網易傳媒一樣,先嘗試一下用官方的 Cronet 進行接入,再決定是否基于Cronet進行二次開發。
總結
以上是生活随笔為你收集整理的网易云音乐网络库跨平台化实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 元宇宙大潮来袭?业内首个虚拟形象实时互动
- 下一篇: 网易云信携手“瑶台”,打造元宇宙商业化实