手機天貓解耦之路

guojin08發表於2018-03-12

做同樣的事情,感覺還是阿里系總結的好:http://www.infoq.com/cn/articles/the-road-of-mobile-tmall-decoupling

先要說一句:慚愧,慚愧。這本來是今年夏天在北京GMTC上的分享,其實早就應該整理成文。哎,拖延癌晚期的症狀始終沒有緩解,又恰好找到了一個要做雙11的藉口,至此。

在GMTC官網可以下載這次分享的資料:http://ppt.geekbang.org/slide/show/194

現場視訊已上線:《手機天貓解耦之路》現場

本文標題是解耦,聊解耦可以有很多方法,本文以架構進化為線索給各位分享手機天貓的解耦之路。我想,在手機天貓的成長過程中,一些形而上的思考和沉澱固然是對大家有參考價值的,而工具和方案則借鑑價值更大。所以本文會較少篇幅放在講過程和原因,比較多篇幅放在講工具和方案。 

什麼在推動進化

作為技術團隊,我們升級技術架構有各種原因,而什麼什麼因素是最關鍵的,什麼可以成為進化理由? 

  • 業務升級 

    最重要的因素一定是業務升級。一個不能產生業務價值的技術是不值得關注的,更不值得去實施。 

    好技術永遠要比業務先走一步,在前邊不遠的地方等著業務追上來——技術驅動業務。所以業務不斷升級,就要求我們的技術架構要跑的更快。

  • 團隊規模 & 合作模式 

    另一個推動架構進化的重要因素就是團隊規模。技術架構最重要的作用之一是保障生產效率和生產質量。那麼人的因素就非常關鍵。人多了,合作複雜了,技術架構就必須升級,去保障這麼多人在一起工作的時候,效率高,不出問題。

  • 程式碼規模 & 工程規模 

    程式碼和工程的規模是一個自然發展的結果,業務複雜了,團隊大了,人多了,程式碼規模和工程規模必然上升。一個數十萬DAU的App和一個千萬DAU的App,規模上的差距雖不是必然,卻也是極大概率了。 

    一個人,幾十個檔案,完成一個簡單功能的產品,和十幾個人,數千個檔案,功能複雜的產品,進一步到十幾個團隊,數百個模組,一個平臺及產品的技術架構必然千差萬別。

  • 新技術 

    新技術,就是工程師自己的事了。業界總會有新的技術出來,這個技術可能是別人家工程師,為了適應他們的業務發展和架構演化而研發出來的。但是技術沒有邊界,新技術好,能給我們帶來價值,提升我們的效率,那我們就拿來用。 

    舉個例子:Facebook的RN出來,我們都覺得不錯,應該也有很多公司確實大規模的使用了。大規模的使用RN做開發,也就會對你原本的架構提出升級的要求。

當然更重要的是如何去平衡快速升級技術架構的好奇心和恰好滿足業務與團隊要求這兩件事。過度追求技術架構革新,過度追求新技術,不但不能給業務和團隊帶來推動作用,反而會造成災難。所以,作為一個優秀的技術團隊永遠要權衡做或不做,多做或少做。 

架構怎麼進化

架構進化體現在哪些方面,作為一個技術團隊我們要如何把架構進化落地?這個問題因專案而異,因團隊而異,因方向而異。本文只介紹手機天貓在發展過程中,與解耦相關的進化歷程。 

  • 升級開發模式 

    開發模式的概念有點大,本文就只討論和解耦這件事相關的:團隊合作方式和工程組織形式。下文單獨一節聊這個事,此處不贅述。

  • 各維度解耦 

    工程大了以後,要分拆,不管是元件化還是外掛化,還是什麼,解耦是第一步,而且是各個維度的解耦。

  • 完善工具集 

    模式演進的過程中,解耦的過程中,就會衍生出很多的工具。在進化過程裡我們也會去思考,哪些工作是需要工具化的,主動去開發工具。一個完善的工具集,會極大提升團隊的生產力,可以說是最有價值的部分。

開發模式升級

手機天貓團隊從一個三端不到十個人的小團隊,成長到現在一個接近兩百人的大團隊,後文詳細描述開發模式經歷了怎麼樣的變更? 

  • 一個工程 

    三年前,手機天貓團隊剛剛組建,十個左右工程師,開發第一版只具備基礎功能的天貓App。整個團隊就這麼幾號人,包括iOS,Android和Server三端,一個平臺上也就三四個人;App的功能也非常簡單,能完成基本的導購和交易流程。 

    天貓App就使用了最簡單的架構,獨立工程,MVC架構。而且我們判斷在這種情況下這樣的架構是完全夠用的,事實如此。

  • 模組化 

    隨著無線業務的發展,手機天貓的團隊開始爆炸式的擴張。很快一個團隊變兩個,兩個變四個。隨著團隊增加,出現團隊分工,工程也越來越大,我們開始發現原始的架構已經開始不夠用,拆分模組勢在必行。 

    在這個階段,手機天貓的模組拆分也做得非常簡陋。先按功能把工程做橫向分層,在業務層再做縱向梳理。把不同的模組程式碼簡單的放在一個資料夾裡,而工程的組織形式並沒有發生變化。 

    如此拆分,我們做到程式碼獨立,跨團隊基本不會在同一個模組程式碼上產生衝突。

  • 外掛化 

    進一步發展,業務越來越複雜,團隊工作越發細分,人也越來越多,程式碼量越來越大。簡單的使用資料夾來組織模組的方式顯得力不從心。多業務跨團隊,不同的開發節奏,複雜的依賴關係,導致我們會花掉大量的時間解決編譯不過的問題。等待其他模組整合這件事居然成了我們開發效率最大的瓶頸。 

    如何解決這個問題,我們的方案是外掛化。那麼外掛和模組有什麼區別?我認為二者最大的區別在於獨立性。外掛是可以獨立開發,獨立釋出,獨立執行的,而模組則必須依賴主工程的環境。具備獨立性的外掛可以很好的隔離跨團隊之間的依賴,彼此獨立開發,按照各自的節奏釋出版本。 

    基於這樣的思考,我們引入依賴管理設施(iOS引入了Cocoa Pods,Android使用Maven);把此前的模組進一步剝離成獨立工程,單獨做版本管理;每個獨立的外掛對釋出的版本號負責,不論是其他外掛還是主工程都依賴外掛釋出的穩定版本。 

    然而,但是,But,外掛化這件事並沒有我們想象的那麼美好。程式碼出來了,但是不能獨立編譯,依賴管理設施有了,但是管不好。由於我們此前從未梳理過依賴關係,所以不管是模組還是外掛,只是一種程式碼管理和釋出流程的工作法,解決不了獨立開發和獨立執行的問題。在這個階段,我們選擇了容忍這個問題,因為獨立開發和獨立執行這兩件事對我們來說似乎並不是那麼的有價值,而無法實現這兩件事也並不成為我們的瓶頸。所以大家還是在一個工程裡,只是程式碼提交到不同的倉庫,然後通過依賴管理設施,通過版本號拼裝成主工程,原始碼最終執行還是揉在一起。

  • 獨立釋出 

    無法獨立釋出會帶來什麼問題?非常明顯,!外掛化一段時間後,我們發現慢的問題嚴重影響著我們的效率。在這個階段,我們已經有超過十個團隊,iOS工程的原始碼檔案超過一萬個。由於主工程是通過各外掛的原始碼組合起來的,每一次重新索引和編譯,都要消耗超過半個小時的時間。 

    要解決這個問題,就是要把外掛化進行到底,實現外掛的另外兩個獨立——獨立開發和獨立執行。最重要的工作就是我們今天的主題解耦,梳理各個外掛之間的依賴關係。讓每一個獨立外掛儘可能少的依賴其他外掛,在最小範圍內正常編譯執行。每次釋出不再是一個穩定版本號,而是一個穩定的二進位制包。 

    如此依賴,我們把超過半小時的編譯過程拆分到數十個模組中,而主工程依賴數十個二進位制包,編譯也就快了。

整個模式升級基本上經歷了這樣幾個階段: 

  • 程式碼獨立,先從形式上解耦 
  • 獨立程式碼工程化,為獨立執行打下基礎 
  • 梳理依賴關係,獨立工程可編譯 
  • 放棄原始碼依賴,提速整合編譯

一路走來,一步一個腳印,最終實現完整的解耦。在這個過程中我們沉澱了不少的方法論和最佳實踐,我想有兩個工具是值得介紹的,下文詳述。 

解耦工具箱

工欲善其事,必先利其器。這句話每個人都在說,卻不是每個人都能做到。一個具有工具文化的團隊會在質量,效率各個方面都會有很大優勢。 

一個工程,從原始狀態迅速膨脹到天貓現在的體量的,依賴關係之複雜,超乎想象。 

在這個膨脹過程裡,我把耦合分成三類: 

  1. 介面耦合,就是使用者操作流程裡,從首頁-到搜尋-到詳情-再進店,這些介面的跳轉是硬編碼的 
  2. 依賴耦合,顧名思義,兩個模組之間的有依賴,就是耦合 
  3. 工程耦合,每個模組有自己的生命週期和執行時,每個模組在生產環境裡又需要依賴主工程的執行時

Beehive是一個執行時框架,主要解決依賴耦合和工程耦合。 

說到耦合,體量如手機天貓這樣的一個App,各種依賴關係必然非常複雜,模組與模組的耦合也必然千絲萬縷。我們要做的並不是把這些依賴和耦合一一處理掉,而是進行梳理,把不合理的找出來,解決掉,讓整個工程處在一個健康合理的依賴和耦合範圍內。有問題的依賴基本有這樣幾種: 

  1. 模組迴圈依賴 
  2. 層間反向依賴 
  3. 非強功能依賴

下圖是一張依賴的示意圖。 

幾條虛線的依賴關係是我認為有問題的依賴,而抽象出有問題的幾個模組 

引入Beehive後,依賴關係會把幾條紅線全部引向Beehive模組,而Beehive模組則是獨立於各層之外的。 

Beehive的原理是,每一個對外提供服務的模組,需要註冊一個抽象介面到Beehive提供的Interfaces(介面池)。注意,在這個池子裡只有抽象介面。 

開發階段,呼叫方依賴介面池中響應的介面,並以介面為引數,通過Beehive提供的工廠方法獲取一個服務例項,這個例項可以正常進行服務。 

執行時階段,Beehive工廠方法根據服務的註冊配置,構造服務例項。若:當前的執行環境沒有依賴提供服務的模組,則返回空;若:當前執行環境依賴關係完整,則開始構造服務,並返回。 

通過這樣的方案,就可以實現模組間解耦。 

統跳協議 & Rewrite引擎

統調協議是一個基於URL的跳轉方案,配合Rewrite引擎實現全App呼叫解耦。此前蘋果核一篇文章詳細介紹,這裡我就不詳述細節。 

Beehive和統跳&Rewrite的區別

Beehive和統跳協議的目的都是解耦,然後二者所關注的重心不同。統跳主要為介面解耦服務,業務要求介面鏈路的強動態性;Beehive則為模組解耦,解決模組強依賴帶來的開發階段痛苦。 

以上,就是我們在過去的幾年裡,整個手機天貓所經歷的解耦過程。在這個過程裡,我們有過很多思考,也踩了很多坑,當然也沉澱了很多好用的工具。希望接下來能有更多機會跟各位分享,也歡迎各位跟我們交流,互相學習。 


感謝徐川對本文的審校。

給InfoQ中文站投稿或者參與內容翻譯工作,請郵件至editors@cn.infoq.com。也歡迎大家通過新浪微博(@InfoQ@丁曉昀),微信(微訊號:InfoQChina)關注我們。


相關文章