百度App组件化之路
原創:GuoJin 百度APP技術團隊-資深技術專家 文章來源:百度APP技術微信公眾號
??????????組件化是一個老生常談的涉及面很廣的話題,即不是做好一件事而是做好一系列的事情才能達成;其中包含組件化框架在內的各架構層級、構建系統、依賴管理系統、以及配套的防劣化機制與規則規范。
??????????本文主要基于百度App背景、目標和組件化歷程來講述保障并行開發和組件復用的手段,盡量避免過多發散到構建系統、依賴管理系統,以及組件化框架這樣的具體子方向。組件化的重要性取決于應用規模、團隊規模、產品技術目標;所述內容雖然是從iOS平臺出發,但方法論與實現路徑適用于大部分平臺。
背景與目標
百度App(大型App)復雜度來源
- 業務規模大:百度App技術方向及子方向70+,單端代碼量180w+;
目標:隔離各組件間影響避免故障蔓延,并控制整體App的復雜度; - 團隊規模大:有代碼權限的數百人;
目標:保障高效并行開發; - 公司內部接入業務多:30+,非單純基礎庫,與百度App關系復雜;
目標:處理接入業務與百度App架構及架構中各組件關系,保障快速高效接入與基礎能力復用。 - 迭代速度快:3周一個版本,2周開發1周測試;
目標:避免高速迭代情況下組件化程度劣化。 - 技術形態多:H5、NA、Hybrid、Talos、Flutter并存;
目標:保障基礎能力復用,構建系統支撐。
另外啟動速度、體積等準入流程的約束;以及目標的多樣性也是大型App復雜度來源因素;由背景產生的目標是天生的技術需求,除此之外,百度App在不同階段有不同的產品技術目標。
百度App不同階段的不同目標
- 合作業務三方庫復用;單個技術組件輸出(最早的需求,2014年),對單個組件輸出來講,如何避免輸出時,拔出蘿卜帶出"泥";
- 矩陣產品孵化(2017年~2019);
- 小程序開源復用(2018年):輸出組件兼容不同宿主,保持部分依賴組件可替代性;
目標多樣性要求在開發時考慮到各個目標的訴求,在方案設計時盡量避免和這些目標沖突。
重要架構迭代
初始態-2013(鉆木取火):
這一時期,百度App瀏覽器角色較重,大家都在一個工程里開發,各業務和基礎邏輯交錯,沒有邊界,你中有我、我中有你;UI架構比較復雜,每個RD都要從App主入口開始看懂主流程代碼,小心翼翼的開發。
這一時期的架構是這樣:
這一時期的主要問題有:
- 一些基礎庫、甚至開源三方庫都會有業務侵入;沒有明確分層和防修改機制,入侵成本極低;
- 首屏各業務間沒有容器隔離,牽一發而動全身,極容易互相影響;
- 對各業務共用的服務(遠程配置、端能力)沒有服務組件化,if else/switch case式邏輯無限蔓延;
- 邏輯、資源沒有合理歸屬,數據沒有拆分,基礎組件對外輸出困難;
- 插件接口層沒有體系化建設,穩定性欠佳(fragile);接入業務成為百度App里的超級模塊,依賴關系難以控制。
2014-2015(蒸汽機時代):
雖然當時團隊規模只有幾十人,但已經意識到了組件化的重要性;接入業務逐步變多,同時也有部分技術組件對外輸出的需求;這一階段:
- 首先拆出三方庫,粗粒度拆出基礎庫,歸到業務組件下層;百度App和接入業務復用這部分基礎庫;
- 引入框架容器,對首屏各業務及棧式導航容器中的業務進行隔離;
- 對新興的業務組件或需要重構的業務,首先采用組件化模式開發,邏輯、資源、數據各有歸屬,同時明確外部依賴;
- 初步制定了依賴規范,禁止層級反向依賴,這一階段只是規范,沒有工具鏈的強制支撐;
- 組件除基礎庫外的依賴通過Adapter注入來實現。
這一時期的架構是這樣:
這一時期的主要問題有:
- 組件歸屬的模糊性,部分組件游離在基礎庫和業務組件之間,同層組件間的依賴與調用關系不夠清晰;
- 組件間通過Adapter進行一對一解耦,雖然有比較明確的外部依賴關系,但解耦效率不高;
- 主App中還遺留端能力接口,與通過插件系統接入的一些SDK。
2016-2017(電力時代)
這一時期重點建設了組件化框架(Pyramid、SchemeRouter)與分發框架(RemoteConfig、PMS、預取分發),及數據拆分框架(CocoaSetting);進一步保障了各組件能做到邏輯、數據各有歸屬;
這一時期的架構是這樣:
2018-2019(理想態-核能時代)
這一時期,組件化框架相對完善,各組件已能做到邏輯、資源、數據各有歸屬。主工程進一步被弱化;
- 層級更加明確清晰,游離與基礎庫層和業務組件層間的通用服務有了歸屬;組件可以自下而上的對外輸出;
- 整個App通過中央倉庫的組件列表(Central Repo Specs),經過EasyBox組裝整個工程;
- 框架容器加載及系統事件分發統一到輕量級的AppLauncher;
- 對接入SDK,按架構層級屬性歸屬;如僅被某一個業務組件引用,則有這個業務組件負責管理,降低對外的復雜度;
- 服務層可共享服務相對完善。
組件化的進階-中臺化(星際遠航)
中臺化的大潮滾滾而來,除云端一體化復用外,對組件化也提出了其他的更高要求。共享組件庫+構建系統(EasyBox)合力,已能達到矩陣產品組合輸出能力。
組件化的實現路徑
自下而上的組件化建設
1、編譯隔離、架構分層及層級訪問限制機制建立
- 編譯隔離:通過構建系統(EasyBox)提供的編譯規則文件明確每個組件的對外接口,明確組件的外部依賴(這方面IDE也經常好心辦壞事,讓組件間可以低成本的隨意訪問,逐步模糊了組件的邏輯邊界,加深了組件間的依賴);
- 層間限制:通過構建系統(EasyBox)建立反向訪問限制,即下層組件不可以訪問上層組件;
- 同層訪問:同層組件間也不能無限制調用,通信及訪問限制通過組件化Pyramid框架來完成;各組件間維持較清晰的接口邊界和邏輯邊界。
2、三方庫規范化與基礎庫體系化
1)基礎庫主要存在以下問題:
- 沒有防修改機制,業務侵入成本低;
- 交叉依賴問題:同一基礎依賴的邏輯歸屬到同一組件里;
基礎庫要在無業務侵入的情況下經過一定程度的抽象到架構底層,二進制化實行組件負責人制度,并進行體系化建設避免上述問題。
2)三方庫主要存在以下問題:
- 沒有防修改機制,業務侵入成本低;
- 三方庫在一定用戶規模或業務規模下,確實存在bug,而github的push request不及時或無響應;
所有三方庫更新到最新發布版并二進制化避免業務侵入;差異部分明確修改點,通過運行時單獨打補丁;對外輸出時,明確這一點。
3、建立運行時分發與隔離服務
為避免各組件對共有邏輯、共有數據集中式處理,建立容器及分發機制來分發事件、數據、以及邏輯調用。
Pyramid組件化框架:
- 這里主要講Pyramid框架的分發作用,Pyramid將系統事件分發給各子組件;
- 除此之外,組件化框架還有另外兩個作用:
1)Pyramid框架組件間通信:adapter一對一方式解耦升級為一對多解耦;
2)它將組件間的強依賴轉變為弱依賴,這讓技術組件對外輸出時,被依賴的組件具有某種程度的可替代性;
端能力:
- 分離SchemeRouter與SchemeHandler邏輯,SchemeRouter歸屬服務層組件化框架,SchemeHandler歸屬各組件;
- 由于Scheme參數不夠明確清晰,在百度App中Scheme主要用于和H5的通信,較少用于頁面路由;
配置分發服務:將集中解析并調用各業務處理邏輯改造為分發機制,并最終升級為云控服務;
數據拆分服務:配合配置分發服務,將數據拆分到各組件內部管理;
資源/預取分發服務:建立資源/預取分發服務;
框架容器:通過Tab導航容器、棧式導航容器將各控制器UI數據拆分到各子控制器、事件分發傳遞給各子控制器;
分發與隔離機制、容器機制是并行開發的重要保障。
4、服務層建立
多業務調用的低依賴組件去業務化抽象成通用服務:賬號、分享、云控、統計、性能、AI等
5、建立組件模型
建立組件模型,各業務模塊快速組件化。
- 通俗的講,就是指導各業務模塊明確功能范圍,做到邏輯、資源、數據、私有SDK各有歸屬;
- 最終每個組件是一個獨立的功能單元、邏輯單元、數據及資源管理單元,H5通信單元,性能量化單元,編譯輸出單元(1個或多個)。
- 為了更靈活的組合輸出,組件的接口封裝層和服務對接層可以進行不同粒度編譯單元拆分;主要目的是分離依賴,滿足輸出靈活性;
6、業務組件化
按照組件模型,確定業務的功能范圍、邏輯與接口邊界,快速組件化。
7、劣化控制
組件接口變更、依賴變更、Warning數變化都會記錄通知相關負責人,這些在Tekes平臺管理;敬請繼續關注后續公眾號文章;沒有防劣化機制,填坑速度永遠比不上挖坑速度;一人填坑的速度也永遠比不上多人挖坑速度。
收益總結
1.研發效率的提升,主要體現在以下幾個方面:
- 復雜度控制:復雜度控制在組件內部,對外"簡單可依賴";
- 并行開發:組件化框架及各分發服務具備設計時的隔離性,保障大規模并行開發效率;以遠程配置為例,原來新增一項開發時間為4+小時,現在僅需0.5小時;效率提升8倍+;
- 復用:為矩陣產品輸出輪子;參考百度App矩陣產品,復用率都在50%以上;
- 編譯速度提升:因為組件具有獨立編譯單元的屬性,在編譯過程中組件源碼和編譯產物可以等價替換,所以組件化也為后續組件二進制化打下基礎,百度App編譯速度也平均值從15分鐘/次優化到2分鐘/次;
2.質量上,組件化具備設計時的隔離性,確保單個組件的故障影響范圍內斂到自己內部,不會引發整體crash;
3.為啟動速度、體積等方向提供量化單位;
4.建立健全架構體系,分組件深度優化。
吳軍博士在《文明之光》一書中評價希臘人對世界文明的貢獻這樣寫到:近代自然科學的很多體系都是在古希臘時代奠定的,希臘人在學術研究上有別于東方文明之處不在于一兩項科學發明和發現,而在于他們將自然科學各學科分門別類,對每一個學科都建立起一整套系統的體系,在此基礎上,演繹或歸納出普遍規律性,即定理或定律,繼而成為自然科學各個學科的基石和支柱。
雖然沒有這樣的高度,但對軟件開發來講,也有異曲同工的作用。
結語
"自己"得來終覺淺,引用一段話作為結語,節選自《每個架構師都應該研究下康威定律》
架構的目標是用于管理復雜性、易變性和不確定性,以確保在長期的系統演化過程中,一部分架構的變化不會對架構的其它部分產生不必要的負面影響。這樣做可以確保業務和研發效率的敏捷,讓應用的易變部分能夠頻繁地變化,對應用的其它部分的影響盡可能的小。
參考
- 每個架構師都應該研究下康威定律:
https://www.infoq.cn/article/every-architect-should-study-conway-law - 3 Key Software Principles You Must Understand:
https://code.tutsplus.com/tutorials/3-key-software-principles-you-must-understand–net-25161
總結
以上是生活随笔為你收集整理的百度App组件化之路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Eclipse打印Java的char默认
- 下一篇: unity之动画编辑器