跨全端SDK技術(shù)演進(jìn)
細(xì)想,團(tuán)隊(duì)進(jìn)行跨平臺(tái)開發(fā)已有三年有余,也是集團(tuán)里面C++方向里比較早涉及該領(lǐng)域的部門之一,伴隨業(yè)界跨平臺(tái)技術(shù)發(fā)展與演進(jìn),我們也沉淀了一整套基于C++的跨平臺(tái)技術(shù)體系。
關(guān)于為什么要選擇跨平臺(tái)的實(shí)現(xiàn)方式
Write Once, Run AnyWhere.
越來越多的業(yè)務(wù)需求都有統(tǒng)一的業(yè)務(wù)訴求,按照傳統(tǒng)的方式,在開發(fā)、測(cè)試、維護(hù)上的成本都是乘以N的,體驗(yàn)也很難做到一致性,特別是復(fù)雜的業(yè)務(wù),實(shí)現(xiàn)成本高,導(dǎo)致功能不能很快的上線,各端側(cè)對(duì)齊存在成本,綜合來看,這樣或者類似的業(yè)務(wù)基于研發(fā)效率等考慮,選擇用跨平臺(tái)的實(shí)現(xiàn)方式是非常有必要的。
這里以電商的消息為例,
多角色的運(yùn)營,多場(chǎng)景的觸發(fā),多重能力的支持,導(dǎo)致我們的消息系統(tǒng)十分的復(fù)雜,歷史發(fā)展下的多通道消息系統(tǒng)底層由于歷史發(fā)展衍生出3個(gè)子系統(tǒng)分別是:
- BC消息 - 最早的阿里旺旺就是基于此
- AMP消息 - 淘寶天貓的各類運(yùn)營基于此,衍生出來的主要業(yè)務(wù)包括商家群、淘友、達(dá)人業(yè)務(wù)等,
- IMBA消息 - 主要是號(hào)方面相關(guān)的消息,包括BC通知類的消息、達(dá)人運(yùn)營,品牌號(hào)運(yùn)營等
分別在不同的BU維護(hù),而這些在多平臺(tái)運(yùn)營下都是需要的,比如多角色的融合,多載體運(yùn)行的訴求,每個(gè)載體又有多種平臺(tái)?;谝陨媳尘?,研發(fā)一個(gè)高可用、高復(fù)用、可定制的跨終端消息模塊是非常有必要的。
關(guān)于語言的選擇
選擇編譯型語言還是解釋型語言,我覺得是沒有絕對(duì)而言的,具體選擇哪個(gè)要根據(jù)面對(duì)業(yè)務(wù)形態(tài),適配的終端類型等多方面抉擇而言,這里說下我們選擇C++作為跨平臺(tái)開?發(fā)的首選語言一些背景。
- 我們團(tuán)隊(duì)本身是客戶端的業(yè)務(wù)型團(tuán)隊(duì),當(dāng)前需要跨平臺(tái)的業(yè)務(wù)主要是消息以及消息衍生業(yè)務(wù)的開發(fā)、維護(hù)和創(chuàng)新。
- 主要的終端包括移動(dòng)IOS、移動(dòng)安卓、MAC、WINDOWS。
- 主要載體包括淘寶app、天貓app、千牛app、淘特app、1688app、ICBU app等等。
C++作為天然的跨平臺(tái)語言,可以高效無縫的調(diào)用系統(tǒng)能力,有豐富的技術(shù)生態(tài),綜合組內(nèi)的技術(shù)棧,是比較契合我們的。
其實(shí)也跟集團(tuán)里其他幾個(gè)有類似需求的團(tuán)隊(duì)聊過,目前選擇比較多的也是C++技術(shù)棧,本文也將以C++作為跨平臺(tái)選型語言為主要來講述。
即使是對(duì)于客戶端而言,可選擇跨平臺(tái)語言也是眾多的,通常跟隨著跨平臺(tái)技術(shù)方案一起來看會(huì)比較好一些,沒有絕對(duì)的對(duì)和錯(cuò),只有是否適合你。
關(guān)于基礎(chǔ)庫的選擇
既然選擇了C++作為我們跨平臺(tái)開發(fā)的基礎(chǔ)語言,那么面臨的第一個(gè)問題是需要有一個(gè)功能較為完備,且符合當(dāng)前訴求的基礎(chǔ)庫。我們調(diào)研了市面上幾個(gè)主流的基礎(chǔ)庫
再結(jié)合集團(tuán)內(nèi)當(dāng)時(shí)的現(xiàn)狀,特別考慮移動(dòng)端的現(xiàn)狀:
- 包大小問題
- 集團(tuán)中多種中間件(mtop、db、accs等)要不然已然跨終端,要不然在各端上都有較為優(yōu)秀的獨(dú)立sdk提供,基于第一點(diǎn)考慮,這一部分不需要在基礎(chǔ)庫里二次建設(shè)。
綜合以上因素,集團(tuán)內(nèi)沒有合適的足夠小的且滿足需求的c++基礎(chǔ)庫,因此我們當(dāng)時(shí)決定自研一個(gè)基于C++ STD的符合移動(dòng)端現(xiàn)狀的輕量級(jí)跨終端基礎(chǔ)庫。
這里是跨終端輕量級(jí)基礎(chǔ)庫(LITE)的一個(gè)功能集合圖,在對(duì)這部分進(jìn)行設(shè)計(jì)的時(shí)候,主要考慮的幾個(gè)設(shè)計(jì)原則:
- 基礎(chǔ)能力要圈覆蓋,
- 綜合考慮包大小,性能 以及耗電量等
- 除了提供默認(rèn)實(shí)現(xiàn)外,當(dāng)系統(tǒng)層面有比較好的能力、或更優(yōu)解時(shí)時(shí),復(fù)用端上的能力,通過統(tǒng)一的方式嫁接。舉個(gè)例子,集團(tuán)中間件mtop,accs等,亦或者諸如Crypt模塊,我們期望用比較簡(jiǎn)單的方式去實(shí)現(xiàn),調(diào)用了Openssl的加解密接口,熟悉Openssl的同學(xué)都知道他的庫大小在1M左右,同時(shí)IOS系統(tǒng)直接就提供Openssl能力,所以在做這一部分的時(shí)候我們提供了默認(rèn)實(shí)現(xiàn),同時(shí)開放統(tǒng)一接口接入系統(tǒng)能力,從而實(shí)現(xiàn)包大小最優(yōu)。
總結(jié)一下設(shè)計(jì)原則9個(gè)字:最小夠用可擴(kuò)展原則。
平臺(tái) | 包大小(kb, 基礎(chǔ)功能) | 說明 |
ANDROID | 43kb | 動(dòng)態(tài)庫,v8a |
IOS | 116 | 靜態(tài)庫,實(shí)際占app大小 |
Windows | 47kb | x86 |
包大小情況如圖(未包含淘寶天貓基礎(chǔ)客戶端能力,包含后略增加60k左右)。
目前Lite庫已經(jīng)作為淘系C++基礎(chǔ)庫集成在集團(tuán)的近100個(gè)App里,除了包大小優(yōu)勢(shì)外,穩(wěn)定性也極佳,百萬分之1左右的崩潰率(淘寶雙端統(tǒng)計(jì)),同時(shí)與淘寶天貓架構(gòu)組合作,無縫提供淘寶天貓客戶端基礎(chǔ)能力(log、埋點(diǎn)、鍵值存儲(chǔ)等)。
關(guān)于跨平臺(tái)的架構(gòu)選擇
通常技術(shù)還是為業(yè)務(wù)服務(wù),還是貼近業(yè)務(wù)來講述,能更有體感一些,這樣也更直觀的把我們遇到的問題和背后思考透漏給大家,作為參考,還是以消息為例:
我們面臨的問題和訴求:
- 屏蔽通道,以統(tǒng)一的數(shù)據(jù)模型方式對(duì)外提供:底層需要對(duì)接3個(gè)服務(wù)端(多通道),同時(shí)每個(gè)server端提供的能力、功能又高度相似,需要抽象后統(tǒng)一屏蔽提供給上層,此外相關(guān)數(shù)據(jù)需要跨通道或者屏蔽通道存儲(chǔ)。
- 調(diào)用方線程不統(tǒng)一:業(yè)務(wù)部分需要處理來自不同線程的高并發(fā)(于客戶端而言)請(qǐng)求。
- 多語言:接入層需要適配不同平臺(tái),ANDROID-JAVA,IOS、MACOS-Object C,WIN-C++。
- 多維度支持定制:
- 業(yè)務(wù)定制:業(yè)務(wù)上需要面對(duì)不同的APP可以有不同的功能定制,這里還包括支持通道的可配置,業(yè)務(wù)能力的可定制等,舉個(gè)例子:在淘特里無群聊服務(wù),但是在淘寶里這一部分又是需要的,在千牛里需要有商家群,但不需要有淘友關(guān)系群,等等基于業(yè)務(wù)、以及包大小考慮的定制。
- 線程調(diào)度定制:基于耗電量、系統(tǒng)資源、平臺(tái)特性等考慮,需要線程調(diào)度方案的可定制。在移動(dòng)端對(duì)耗電量比較敏感且系統(tǒng)資源有限,但是PC端上對(duì)性能要求會(huì)更高一些,如何在統(tǒng)一的方案里支持線程調(diào)度的平臺(tái)級(jí)、甚至是業(yè)務(wù)級(jí)別定制。
- 網(wǎng)絡(luò)流量管理定制:對(duì)網(wǎng)絡(luò)能力一方面要有統(tǒng)一的監(jiān)控,方便判斷MTOP的各種API調(diào)用情況分析,另一方面需要有統(tǒng)一的切面能力,在MTOP的調(diào)用上做二次業(yè)務(wù)級(jí)別的網(wǎng)絡(luò)流量接口優(yōu)化定制。
- DB管理定制:基于FD、平臺(tái)特定等考慮,需要DB管理可以根據(jù)不同的平臺(tái)有不同的定制策略,在移動(dòng)端采用單用戶一庫多表的形式,在pc端性能最大化采用多庫多表甚至需要支持不同app的不同db管理方案可定制。
- 基礎(chǔ)能力復(fù)用:對(duì)于集團(tuán)通用的基礎(chǔ)組件,需要有統(tǒng)一的能力從外部獲取從而方便c++層使用。
- 穩(wěn)定性保障:是否方便做單元測(cè)試,設(shè)計(jì)上如何避免單元測(cè)試對(duì)內(nèi)部代碼的入侵。
基于以上,我們?cè)O(shè)計(jì)了MessageSDK的架構(gòu)大致如下:
來整體看下上面的設(shè)計(jì)是如何解決這些問題的。
總體分5部分,Service層,業(yè)務(wù)層,數(shù)據(jù)層,數(shù)據(jù)通道層以及基礎(chǔ)服務(wù)層。從上到下看整個(gè)架構(gòu)設(shè)計(jì)
- Service層:也就是我們通常說的Wrapper層,其目的是為了翻譯業(yè)務(wù)層C++的接口,提供三種方式的直接接入,Java,Oc,C++,需要說明的是這一層不要做除語言轉(zhuǎn)換外的任何代碼,一來方便問題排查,wrapper不做額外的處理和問題排查,二來業(yè)界也有不少自動(dòng)化實(shí)現(xiàn)語言翻譯的工具,有關(guān)這部分我們后面部分詳細(xì)聊。
- 業(yè)務(wù)層:業(yè)務(wù)邏輯的一個(gè)組合,以消息為例,按模塊分為三個(gè)子模塊第一個(gè)消息模塊,第二個(gè)profile模塊,第三個(gè)群模塊,模塊與模塊之間相對(duì)獨(dú)立,做強(qiáng)制隔離,代碼無耦合,功能可定制化
- 數(shù)據(jù)層:分為兩部分本地存儲(chǔ)以及網(wǎng)絡(luò)存儲(chǔ),其目的是屏蔽數(shù)據(jù)來源以統(tǒng)一的方式提供給業(yè)務(wù)層,讓業(yè)務(wù)不感知通道來源,同時(shí)在這一層還吃掉所有的耗時(shí)操作,IO以及網(wǎng)絡(luò)請(qǐng)求,其中Adpter主要是屏蔽通道之間的差異,這一部分支持整體可替換,目的提高擴(kuò)展性,可定制接入三方通道
- 數(shù)據(jù)通道層:這一部分就是數(shù)據(jù)來源的通道,主要有SYNC,WXNet,DB等
- 基礎(chǔ)服務(wù)層:是一個(gè)跨平臺(tái)的c++基礎(chǔ)庫。
其實(shí)客戶端按層分割的設(shè)計(jì)理念,無論是在PC時(shí)代還是現(xiàn)在的移動(dòng)互聯(lián),都不是什么顯而易見的事兒,主要關(guān)注下這里面的差異點(diǎn)也可以說是這種框架設(shè)計(jì)的優(yōu)點(diǎn)。
- 便捷:service我們只做語言層面的膠水,縮短后期排查問題的整個(gè)鏈路,方便后期排查和定位問題。同時(shí)自動(dòng)化的實(shí)現(xiàn)也再次降低了維護(hù)成本。
- 開放:上圖右側(cè)是Openpoint能力,主要是通過一些標(biāo)準(zhǔn)化的開放點(diǎn)去開放一些能力,讓接入方做更多的業(yè)務(wù)屬性,簡(jiǎn)單來說【每個(gè)業(yè)務(wù)有特定的開放點(diǎn),用統(tǒng)一的形式開放出去】
- 定制:
- 業(yè)務(wù)定制:模塊與模塊之間相互完全獨(dú)立,代碼無耦合,強(qiáng)制隔離,實(shí)現(xiàn)功能的可定制化,讓業(yè)務(wù)方可以自由的進(jìn)行業(yè)務(wù)形態(tài)組合的同時(shí)做到包大小最優(yōu),
- 線程調(diào)度定制:統(tǒng)一線程調(diào)度處理方案,抽象線程配置策略,多app可自由配置,上層可選擇平臺(tái)級(jí)線程調(diào)度最優(yōu)方案。
- 網(wǎng)絡(luò)流量管理定制:網(wǎng)絡(luò)調(diào)用處統(tǒng)一切面處理,一方面對(duì)瞬時(shí)相同請(qǐng)求做攔截合并,大幅度降低服務(wù)端壓力,另一方面針對(duì)業(yè)務(wù)級(jí)別做原子調(diào)用的合并定制,減少服務(wù)端查詢量,此外切面處進(jìn)行統(tǒng)一打點(diǎn)處理,方便客戶端做調(diào)用流量監(jiān)控。
- DB管理定制:同線程調(diào)度定制,統(tǒng)一DB管理分配方案,抽象庫分配策略,多app可自由配置,上層可選擇平臺(tái)級(jí)線程調(diào)度的最優(yōu)方案。
- 擴(kuò)展:對(duì)外提供整體能力,又可以動(dòng)態(tài)替換,目的是提供一種能力讓接入方可以放入自己的數(shù)據(jù)通道,從而實(shí)現(xiàn)一個(gè)業(yè)務(wù)的擴(kuò)展,此方法也極大的方便了后期做統(tǒng)一的單元測(cè)試。
于CPP程序員而言,線程模型是非常重要的,好的設(shè)計(jì)可以從架構(gòu)上就避免掉后期一系列隱秘的bug,這里包括一系列問題,比如:
- 在移動(dòng)端上,通常調(diào)用者不關(guān)心調(diào)用線程,但是在數(shù)據(jù)層又需要做統(tǒng)一的數(shù)據(jù)修改和聚合能力
- 業(yè)務(wù)層頻繁調(diào)用,過多的鎖除了可能會(huì)出現(xiàn)死鎖外,也不利于性能最大化。
- 部分接口如果IO、NET時(shí)間過長可能導(dǎo)致整個(gè)流程中斷,或異常。
來看下跨終端SDK架構(gòu)設(shè)計(jì)的線程模型:
- 接入層:不限制調(diào)用線程,降低接入成本
- 業(yè)務(wù)層 : 按模塊做隔離,單獨(dú)運(yùn)行在自己的業(yè)務(wù)線程里,在biz層做業(yè)務(wù)無鎖話的同時(shí),做到單業(yè)務(wù)無鎖化,多業(yè)務(wù)并行化
- Model層 : 主要是做IO、網(wǎng)絡(luò)操作,目的是為了業(yè)務(wù)線程IO、網(wǎng)絡(luò)無阻塞
整個(gè)線程模型的特點(diǎn):純異步,可定制,單業(yè)務(wù)無鎖化,多業(yè)務(wù)并行化,IO網(wǎng)絡(luò)無阻塞。
C++工程腳手架及C++工程標(biāo)準(zhǔn)化
C++作為天然的跨平臺(tái)語言,可以高效無縫的調(diào)用系統(tǒng)能力,有豐富的技術(shù)生態(tài)。但是實(shí)際面向Android、iOS、Windows、Mac等多平臺(tái)的開發(fā)過程并不順滑,主要存在以下問題:
- 平臺(tái)特性差異、開發(fā)語言、開發(fā)工具鏈、編譯打包等
- 多平臺(tái)下項(xiàng)目配置維護(hù)成本高
- 膠水(語言轉(zhuǎn)換層)代碼人工接入成本高、重復(fù)性工作大、門檻高(主要是對(duì)于C++開發(fā)者)
- 業(yè)務(wù)及技術(shù)框架選型困難
- 構(gòu)建發(fā)布方案規(guī)范化較差、集團(tuán)研發(fā)平臺(tái)對(duì)跨平臺(tái)構(gòu)建發(fā)布的設(shè)計(jì)不足
我們?cè)谙耄琒DK開發(fā)流程是否可以是一個(gè)可被SOP的過程?如果有統(tǒng)一的跨平臺(tái)SDK開發(fā)SOP流程,特別是對(duì)于新入此行的同學(xué),當(dāng)然是非常友好的。想想coder如果可以上來就可以寫業(yè)務(wù)代碼,不用關(guān)心框架,工程模型,打包方案等等,是不是美滋滋。
為了解決上述共性問題,結(jié)合之前我們?cè)谙DK上的相關(guān)經(jīng)驗(yàn),我們啟動(dòng)了Eyas(雛鷹)項(xiàng)目。
Eyas旨在進(jìn)一步結(jié)合集團(tuán)技術(shù)能力,降低跨終端開發(fā)成本,為其提供標(biāo)準(zhǔn)化的作業(yè)流程,包括跨平臺(tái)基礎(chǔ)庫組件化、業(yè)務(wù)框架通用化、配置一體化、語言轉(zhuǎn)換層代碼工具化,以及發(fā)布能力統(tǒng)一化等一整套解決方案,讓開發(fā)者們可以在輕量級(jí)認(rèn)識(shí)跨終端的同時(shí),更專注于業(yè)務(wù)本身。
Eyas的中文是雛鷹,我們希望通過Eyas,可以一起在跨終端的藍(lán)海中探索無限可能,同時(shí)可以孵化出更多跨平臺(tái)的模塊,讓開發(fā)者低成本開發(fā)跨平臺(tái)SDK,保證多平臺(tái)業(yè)務(wù)一致性,提升業(yè)務(wù)開發(fā)效率的同時(shí)降低測(cè)試成本。
為了讓開發(fā)者可以更快的上手跨平臺(tái)的開發(fā),我們擬定了一套跨平臺(tái)SDK開發(fā)的作業(yè)流程,同時(shí)在每一步都提供了對(duì)應(yīng)的解決方案,所有的解決方案均提供工具化的方式來高效、低成本的輔助開發(fā)者們進(jìn)行跨平臺(tái)SDK的開發(fā)。下圖是MTL4上C++跨平臺(tái)研發(fā)工作流:
其中 IDL(接口描述)的編寫 以及 功能代碼編寫,需要開發(fā)者根據(jù)實(shí)際業(yè)務(wù)進(jìn)行開發(fā)。我們從項(xiàng)目創(chuàng)建、代碼生成、編譯選項(xiàng)和構(gòu)建四個(gè)維度分析。
? 創(chuàng)建項(xiàng)目
跨終端團(tuán)隊(duì)在以往的桌面端開發(fā)中積累了很多SDK開發(fā)經(jīng)驗(yàn),也有一套成熟的SDK框架代碼,但是我們?cè)诜趸耂DK時(shí)候,還是要花一定時(shí)間在SDK的通用代碼的拷貝上,無法快速的復(fù)制出一個(gè)新的SDK項(xiàng)目。
開發(fā)者都是懶惰的,為了盡可能的復(fù)用代碼,提升開發(fā)效率,開發(fā)者發(fā)明了宏,也發(fā)明了模板編程。為了支持復(fù)用SDK框架通用代碼和項(xiàng)目配置,我們提供了基于項(xiàng)目模板創(chuàng)建項(xiàng)目的工具,快速實(shí)現(xiàn)項(xiàng)目工程從0到1的過程。
其實(shí)對(duì)于剛涉足到跨終端開發(fā)的開發(fā)者,創(chuàng)建了可以編譯不同平臺(tái)產(chǎn)物的項(xiàng)目就已經(jīng)開啟了跨平臺(tái)的大門。
? 代碼生成
代碼生成即語言膠水層代碼工具化,由于平臺(tái)開發(fā)的語言差異,需要有一層膠水代碼來進(jìn)行語言的翻譯,膠水代碼本身是沒有業(yè)務(wù)邏輯的,并且耗費(fèi)開發(fā)成本,人工編寫代碼也會(huì)帶來出錯(cuò)的風(fēng)險(xiǎn)。我們使用膠水代碼自動(dòng)化來解決該問題。
膠水代碼自動(dòng)化的原理:
根據(jù)接口的idl描述生成接口文件和膠水代碼
JNI: C++ <-> Java交互的膠水代碼ObjCPP: C++ <-> ObjC交互的膠水代碼
我們基于djinni(地址:https://github.com/dropbox/djinni)進(jìn)行了二次開發(fā),提升安全性的同時(shí)增加了多項(xiàng)feature,更容易對(duì)復(fù)雜項(xiàng)目做模塊定制化能力。
? 編譯配置+依賴管理
項(xiàng)目結(jié)構(gòu):
支持以下能力:
- 使用GN管理工程和源碼文件,確保多端的C++項(xiàng)目配置一致。
- 統(tǒng)一多平臺(tái)業(yè)務(wù)模塊定制化能力。
- 支持多平臺(tái)下不同IDE編碼和調(diào)試。
- 結(jié)合集團(tuán)技術(shù)能力,統(tǒng)一依賴管理解決方案,支持通過一份配置文件管理多平臺(tái)下的依賴。
團(tuán)隊(duì)介紹
我們是大淘寶技術(shù)部行業(yè)與商家技術(shù)跨終端技術(shù)團(tuán)隊(duì),業(yè)務(wù)上負(fù)責(zé)為千萬級(jí)商家打造最高效的一站式工作臺(tái)千牛,為淘寶上億商家和消費(fèi)者提供穩(wěn)定高效的端到端消息IM服務(wù);技術(shù)上深耕C++跨終端及PC桌面端技術(shù)(Window?s&Mac),為商家,消費(fèi)者提供穩(wěn)定,可靠,高效的客戶端產(chǎn)品。