跨全端 SDK 技術演進

阿里云云棲號 發佈 2022-08-04T12:02:31.385163+00:00

越來越多的業務需求都有統一的業務訴求,按照傳統的方式,在開發、測試、維護上的成本都是乘以N的,體驗也很難做到一致性,特別是複雜的業務,實現成本高,導致功能不能很快的上線,各端側對齊存在成本,綜合來看,這樣或者類似的業務基於研發效率等考慮,選擇用跨平台的實現方式是非常有必要的。

關於為什麼要選擇跨平台的實現方式

Write Once, Run AnyWhere.

越來越多的業務需求都有統一的業務訴求,按照傳統的方式,在開發、測試、維護上的成本都是乘以N的,體驗也很難做到一致性,特別是複雜的業務,實現成本高,導致功能不能很快的上線,各端側對齊存在成本,綜合來看,這樣或者類似的業務基於研發效率等考慮,選擇用跨平台的實現方式是非常有必要的。

多角色的運營,多場景的觸發,多重能力的支持,導致我們的消息系統十分的複雜,歷史發展下的多通道消息系統底層由於歷史發展衍生出3個子系統分別是:

  1. BC消息 - 最早的阿里旺旺就是基於此
  2. AMP消息 - 淘寶天貓的各類運營基於此,衍生出來的主要業務包括商家群、淘友、達人業務等,
  3. IMBA消息 - 主要是號方面相關的消息,包括BC通知類的消息、達人運營,品牌號運營等

分別在不同的BU維護,而這些在多平台運營下都是需要的,比如多角色的融合,多載體運行的訴求,每個載體又有多種平台。
基於以上背景,研發一個高可用、高復用、可定製的跨終端消息模塊是非常有必要的。

關於語言的選擇

選擇編譯型語言還是解釋型語言,我覺得是沒有絕對而言的,具體選擇哪個要根據面對業務形態,適配的終端類型等多方面抉擇而言,這裡說下我們選擇c++作為跨平台開發的首選語言一些背景。

  1. 我們團隊本身是客戶端的業務型團隊,當前需要跨平台的業務主要是消息以及消息衍生業務的開發、維護和創新。
  2. 主要的終端包括移動IOS、移動安卓、MAC、WINDOWS。
  3. 主要載體包括淘寶app、天貓app、千牛app、淘特app、1688app、ICBU app等等。

C++作為天然的跨平台語言,可以高效無縫的調用系統能力,有豐富的技術生態,綜合組內的技術棧,是比較契合我們的。

其實也跟集團里其他幾個有類似需求的團隊聊過,目前選擇比較多的也是C++技術棧,本文也將以C++作為跨平台選型語言為主要來講述。

即使是對於客戶端而言,可選擇跨平台語言也是眾多的,通常跟隨著跨平台技術方案一起來看會比較好一些,沒有絕對的對和錯,只有是否適合你。

關於基礎庫的選擇

既然選擇了C++作為我們跨平台開發的基礎語言,那麼面臨的第一個問題是需要有一個功能較為完備,且符合當前訴求的基礎庫。我們調研了市面上幾個主流的基礎庫

再結合集團內當時的現狀,特別考慮移動端的現狀:

  1. 包大小問題
  2. 集團中多種中間件(mtop、db、accs等)要不然已然跨終端,要不然在各端上都有較為優秀的獨立sdk提供,基於第一點考慮,這一部分不需要在基礎庫里二次建設。

綜合以上因素,集團內沒有合適的足夠小的且滿足需求的c++基礎庫,因此我們當時決定自研一個基於C++ STD的符合移動端現狀的輕量級跨終端基礎庫。

這裡是跨終端輕量級基礎庫(LITE)的一個功能集合圖,在對這部分進行設計的時候,主要考慮的幾個設計原則:

  1. 基礎能力要圈覆蓋,
  2. 綜合考慮包大小,性能 以及耗電量等
  3. 除了提供默認實現外,當系統層面有比較好的能力、或更優解時時,復用端上的能力,通過統一的方式嫁接。舉個例子,集團中間件mtop,accs等,亦或者諸如Crypt模塊,我們期望用比較簡單的方式去實現,調用了Openssl的加解密接口,熟悉Openssl的同學都知道他的庫大小在1M左右,同時IOS系統直接就提供Openssl能力,所以在做這一部分的時候我們提供了默認實現,同時開放統一接口接入系統能力,從而實現包大小最優。

總結一下設計原則9個字:最小夠用可擴展原則。

平台

包大小(kb, 基礎功能)

說明

ANDROID

43kb

動態庫,v8a

IOS

116

靜態庫,實際占app大小

Windows

47kb

x86

包大小情況如圖(未包含淘寶天貓基礎客戶端能力,包含後略增加60k左右)。

目前Lite庫已經作為淘系C++基礎庫集成在集團的近100個App里,除了包大小優勢外,穩定性也極佳,百萬分之1左右的崩潰率(淘寶雙端統計),同時與淘寶天貓架構組合作,無縫提供淘寶天貓客戶端基礎能力(log、埋點、鍵值存儲等)。

關於跨平台的架構選擇

通常技術還是為業務服務,還是貼近業務來講述,能更有體感一些,這樣也更直觀的把我們遇到的問題和背後思考透漏給大家,作為參考,還是以消息為例:

我們面臨的問題和訴求:

  1. 屏蔽通道,以統一的數據模型方式對外提供:底層需要對接3個服務端(多通道),同時每個server端提供的能力、功能又高度相似,需要抽象後統一屏蔽提供給上層,此外相關數據需要跨通道或者屏蔽通道存儲。
  2. 調用方線程不統一:業務部分需要處理來自不同線程的高並發(於客戶端而言)請求。
  3. 多語言:接入層需要適配不同平台,ANDROID-Java,IOS、MACOS-Object C,WIN-C++。
  4. 多維度支持定製:
    1. 業務定製:業務上需要面對不同的APP可以有不同的功能定製,這裡還包括支持通道的可配置,業務能力的可定製等,舉個例子:在淘特里無群聊服務,但是在淘寶里這一部分又是需要的,在千牛里需要有商家群,但不需要有淘友關係群,等等基於業務、以及包大小考慮的定製。
    2. 線程調度定製:基於耗電量、系統資源、平台特性等考慮,需要線程調度方案的可定製。在移動端對耗電量比較敏感且系統資源有限,但是PC端上對性能要求會更高一些,如何在統一的方案里支持線程調度的平台級、甚至是業務級別定製。
    3. 網絡流量管理定製:對網絡能力一方面要有統一的監控,方便判斷MTOP的各種API調用情況分析,另一方面需要有統一的切面能力,在MTOP的調用上做二次業務級別的網絡流量接口優化定製。
    4. DB管理定製:基於FD、平台特定等考慮,需要DB管理可以根據不同的平台有不同的定製策略,在移動端採用單用戶一庫多表的形式,在pc端性能最大化採用多庫多表甚至需要支持不同app的不同db管理方案可定製。
  5. 基礎能力復用:對於集團通用的基礎組件,需要有統一的能力從外部獲取從而方便c++層使用。
  6. 穩定性保障:是否方便做單元測試,設計上如何避免單元測試對內部代碼的入侵。

基於以上,我們設計了MessageSDK的架構大致如下:

來整體看下上面的設計是如何解決這些問題的。

總體分5部分,Service層,業務層,數據層,數據通道層以及基礎服務層。從上到下看整個架構設計

  1. Service層:也就是我們通常說的Wrapper層,其目的是為了翻譯業務層C++的接口,提供三種方式的直接接入,Java,Oc,C++,需要說明的是這一層不要做除語言轉換外的任何代碼,一來方便問題排查,wrapper不做額外的處理和問題排查,二來業界也有不少自動化實現語言翻譯的工具,有關這部分我們後面部分詳細聊。
  2. 業務層:業務邏輯的一個組合,以消息為例,按模塊分為三個子模塊第一個消息模塊,第二個profile模塊,第三個群模塊,模塊與模塊之間相對獨立,做強制隔離,代碼無耦合,功能可定製化
  3. 數據層:分為兩部分本地存儲以及網絡存儲,其目的是屏蔽數據來源以統一的方式提供給業務層,讓業務不感知通道來源,同時在這一層還吃掉所有的耗時操作,IO以及網絡請求,其中Adpter主要是屏蔽通道之間的差異,這一部分支持整體可替換,目的提高擴展性,可定製接入三方通道
  4. 數據通道層:這一部分就是數據來源的通道,主要有SYNC,WXNET,DB等
  5. 基礎服務層:是一個跨平台的c++基礎庫。

其實客戶端按層分割的設計理念,無論是在PC時代還是現在的移動互聯,都不是什麼顯而易見的事兒,主要關注下這裡面的差異點也可以說是這種框架設計的優點。

  1. 便捷:service我們只做語言層面的膠水,縮短後期排查問題的整個鏈路,方便後期排查和定位問題。同時自動化的實現也再次降低了維護成本。
  2. 開放:上圖右側是Openpoint能力,主要是通過一些標準化的開放點去開放一些能力,讓接入方做更多的業務屬性,簡單來說【每個業務有特定的開放點,用統一的形式開放出去】
  3. 定製:
    1. 業務定製:模塊與模塊之間相互完全獨立,代碼無耦合,強制隔離,實現功能的可定製化,讓業務方可以自由的進行業務形態組合的同時做到包大小最優,
    2. 線程調度定製:統一線程調度處理方案,抽象線程配置策略,多app可自由配置,上層可選擇平台級線程調度最優方案。
    3. 網絡流量管理定製:網絡調用處統一切面處理,一方面對瞬時相同請求做攔截合併,大幅度降低服務端壓力,另一方面針對業務級別做原子調用的合併定製,減少服務端查詢量,此外切面處進行統一打點處理,方便客戶端做調用流量監控。
    4. DB管理定製:同線程調度定製,統一DB管理分配方案,抽象庫分配策略,多app可自由配置,上層可選擇平台級線程調度的最優方案。
  4. 擴展:對外提供整體能力,又可以動態替換,目的是提供一種能力讓接入方可以放入自己的數據通道,從而實現一個業務的擴展,此方法也極大的方便了後期做統一的單元測試。

於CPP程式設計師而言,線程模型是非常重要的,好的設計可以從架構上就避免掉後期一系列隱秘的bug,這裡包括一系列問題,比如:

  1. 在移動端上,通常調用者不關心調用線程,但是在數據層又需要做統一的數據修改和聚合能力
  2. 業務層頻繁調用,過多的鎖除了可能會出現死鎖外,也不利於性能最大化。
  3. 部分接口如果IO、NET時間過長可能導致整個流程中斷,或異常。

來看下跨終端SDK架構設計的線程模型:

  1. 接入層:不限制調用線程,降低接入成本
  2. 業務層 : 按模塊做隔離,單獨運行在自己的業務線程里,在biz層做業務無鎖話的同時,做到單業務無鎖化,多業務並行化
  3. Model層 : 主要是做IO、網絡操作,目的是為了業務線程IO、網絡無阻塞

整個線程模型的特點:純異步,可定製,單業務無鎖化,多業務並行化,IO網絡無阻塞。

C++工程腳手架及C++工程標準化

C++作為天然的跨平台語言,可以高效無縫的調用系統能力,有豐富的技術生態。但是實際面向Android、iOS、Windows、Mac等多平台的開發過程並不順滑,主要存在以下問題:

  1. 平台特性差異、開發語言、開發工具鏈、編譯打包等
  2. 多平台下項目配置維護成本高
  3. 膠水(語言轉換層)代碼人工接入成本高、重複性工作大、門檻高(主要是對於C++開發者)
  4. 業務及技術框架選型困難
  5. 構建發布方案規範化較差、集團研發平台對跨平台構建發布的設計不足

我們在想,SDK開發流程是否可以是一個可被SOP的過程?如果有統一的跨平台SDK開發SOP流程,特別是對於新入此行的同學,當然是非常友好的。想想coder如果可以上來就可以寫業務代碼,不用關心框架,工程模型,打包方案等等,是不是美滋滋。

為了解決上述共性問題,結合之前我們在消息SDK上的相關經驗,我們啟動了Eyas(雛鷹)項目。

Eyas旨在進一步結合集團技術能力,降低跨終端開發成本,為其提供標準化的作業流程,包括跨平台基礎庫組件化、業務框架通用化、配置一體化、語言轉換層代碼工具化,以及發布能力統一化等一整套解決方案,讓開發者們可以在輕量級認識跨終端的同時,更專注於業務本身。

Eyas的中文是雛鷹,我們希望通過Eyas,可以一起在跨終端的藍海中探索無限可能,同時可以孵化出更多跨平台的模塊,讓開發者低成本開發跨平台SDK,保證多平台業務一致性,提升業務開發效率的同時降低測試成本。

為了讓開發者可以更快的上手跨平台的開發,我們擬定了一套跨平台SDK開發的作業流程,同時在每一步都提供了對應的解決方案,所有的解決方案均提供工具化的方式來高效、低成本的輔助開發者們進行跨平台SDK的開發。下圖是MTL4上C++跨平台研發工作流:

其中 IDL(接口描述)的編寫 以及 功能代碼編寫,需要開發者根據實際業務進行開發。我們從項目創建、代碼生成、編譯選項和構建四個維度分析。

創建項目

跨終端團隊在以往的桌面端開發中積累了很多SDK開發經驗,也有一套成熟的SDK框架代碼,但是我們在孵化新SDK時候,還是要花一定時間在SDK的通用代碼的拷貝上,無法快速的複製出一個新的SDK項目。

開發者都是懶惰的,為了儘可能的復用代碼,提升開發效率,開發者發明了宏,也發明了模板編程。為了支持復用SDK框架通用代碼和項目配置,我們提供了基於項目模板創建項目的工具,快速實現項目工程從0到1的過程。

其實對於剛涉足到跨終端開發的開發者,創建了可以編譯不同平台產物的項目就已經開啟了跨平台的大門。

代碼生成

代碼生成即語言膠水層代碼工具化,由於平台開發的語言差異,需要有一層膠水代碼來進行語言的翻譯,膠水代碼本身是沒有業務邏輯的,並且耗費開發成本,人工編寫代碼也會帶來出錯的風險。我們使用膠水代碼自動化來解決該問題。

膠水代碼自動化的原理:

根據接口的idl描述生成接口文件和膠水代碼

JNI: C++ <-> Java交互的膠水代碼
ObjCPP: C++ <-> ObjC交互的膠水代碼

我們基於djinni(地址:https://github.com/dropbox/djinni)進行了二次開發,提升安全性的同時增加了多項feature,更容易對複雜項目做模塊定製化能力。

編譯配置+依賴管理

支持以下能力:

  1. 使用GN管理工程和源碼文件,確保多端的C++項目配置一致。
  2. 統一多平台業務模塊定製化能力。
  3. 支持多平台下不同IDE編碼和調試。
  4. 結合集團技術能力,統一依賴管理解決方案,支持通過一份配置文件管理多平台下的依賴。

原文連結:http://click.aliyun.com/m/1000351094/

本文為阿里雲原創內容,未經允許不得轉載。

關鍵字: