業務的不斷髮展、商品類型的不斷增多、不斷添加的業務需求使得閒魚的代碼出現「bad smell」——平臺代碼和業務代碼耦合嚴重難以分離;業務和業務之間代碼交織缺乏拆解。這也是行業中的通病。爲解決此類問題,閒魚自研了一套技術框架——SWAK。本文帶你們一塊兒看看SWAK是怎麼解構閒魚代碼的。設計模式
SWAK是Swiss Army Knife的簡稱,衆所周知,瑞士軍刀是一款小巧靈活、適用於多種場景的工具。在閒魚服務端,SWAK框架也是這樣一種小巧靈活、適用於多種場景的技術框架, 它所要使用的場景都具備同一個特色——多實現間的規則化執行。本文將以一個例子開篇,來詳細介紹其中的概念。緩存
熟悉閒魚的朋友們應該知道,在閒魚App裏面,商品有豐富的表現形式,不妨叫作類型A、類型B和類型C,各類類型也能夠有各自的子類型。每種類型的業務邏輯存在必定的共性,可是也存在部分差別——如在分享頁面中,subtitle字段的展現邏輯就不盡相同:
架構
這種單一的實現一般會被寫成以下的代碼:框架
if(A類型) { if(A1類型) { doSomething1(); }else if(A2類型) { doSomething2(); } } else if(B類型) { doSomething3(); } else if(C類型) { if(C1類型) { doSomething4(); }else if(C2類型) { doSomething5(); } }
相似的代碼你們應該都寫過很多。邏輯簡單的時候寫成這樣無可厚非,但當邏輯開始變複雜的時候這種寫法會具備較多的壞處:ide
按照面向對象的思想,獲取title的方式對於全部類型都是一致的,應該沉澱成平臺邏輯,而獲取subtitle就能夠抽象成一個接口方法,而類型A、類型B和類型C的寶貝都具備各自的實現而已。對於 獲取subtitle這個接口方法來講,它有着多種實現。工具
那麼什麼是規則化執行呢?在上面的例子中,咱們按照了商品的類型(type)進行了邏輯的分離,但一般狀況下並不是能分隔地如此完全。舉一個例子,運營團隊的劃分可能也按照商品類型(type)作劃分,也有可能按照類目(category,如手機、3C數碼、服飾、圖書等)體系來作劃分,甚至還有可能按照地域進行劃分。那麼一個商品可能既會受到商品類型體系的約束,又會受到類目體系的約束,還會受到地域的約束。若是幾種約束不一致的話,就會產生衝突。好比subtitle字段,從類型A的視角上來看應該顯示價格,在圖書類目的視角下或許應該透出出版社——畢竟愛讀書的人大多更關注質量而出版社是衡量質量的一個重要標準。是展現價格,仍是出版社?或者都展現?若是都展現的話先展現價格仍是先展現出版社?若是一行不夠放下全部內容又怎麼辦?不管是上述的哪種展現方式,背後都是「規則」(在設計模式裏,稱之爲「策略」),代碼也無非是按照「規則」進行編寫而已。測試
以上的例子是多實現規則化執行的一個經典場景。相似地,如ABTest、雙寫等邏輯也是多實現規則化執行的應用場景。fetch
在上面的例子中,按照商品的類型或者按照商品的類目進行區分會產生衝突。其實無所謂類型或者類目,對於商品這個對象來講,無非是給其貼上了不一樣的標籤而已——如一個類型A的圖書類目寶貝被貼上「類型A」和「圖書」兩個標籤。「類型A」的獲取subtitle接口方法對應着一種實現,而「圖書」的獲取subtitle接口方法又對應着另外一個實現。當一個對象被貼多個標籤的時候,多個標籤對應的實現就會產生衝突。spa
衝突的解決依賴於「規則」。「規則」最重要的兩個部分是——優先級(Priority)和歸約(Reduce)策略;執行的前後順序由優先級決定,而顯示第一個實現的結果、顯示第二個實現的結果仍是兩個實現結果的拼接等都是歸約策略。「規則」還能夠包含如「並行執行方式」和「異常處理方式」等其餘組成部分。設計
如上,能夠得出SWAK的基本思想:
值得一提的是,SWAK的基本思想借鑑自阿里巴巴中臺的TMF架構,關於TMF的細節能夠參考《盡在雙11--阿里巴巴技術演進與超越》一書的《基於TMF框架的交易平臺架構》章節。
相應地,使用SWAK框架將帶來以下的好處:
相較於運行期才進行根據標籤去掃描並加載實現類的方式,SWAK框架更傾向於在靜態期就能分析出具備某幾個標籤的對象在不一樣的實現方法下會有着怎樣的執行邏輯。一方面經過緩存能夠明顯下降響應時間,另外一方面也便於在開發期間發現和排查問題。總體的實現原理能夠分紅兩個部分:註冊和 執行。基本流程以下:
在註冊過程當中,SWAK框架將會掃描文件(多實現接口、歸約策略、衝突優先級採用了Java註解或者XML文件進行了配置,下面的代碼示例中介紹多實現接口和其實現類是如何配置的),掃描出的結果都註冊到了本地緩存中,而在執行過程當中SWAK框架會從本地緩存中直接查找其所需的衝突優先級配置和歸約策略等,這樣有助於減小響應時間。另外,使用統一的本地緩存有助於進行「可視化的展示」——開發人員能夠直觀地看到並分析出程序的執行流程;產品經理也能夠直觀地看到哪些功能點能夠方便擴展,哪些地方的優先級須要更新等等,甚至有助於需求的估時和排期。使用統一的本地緩存也爲「可視化的配置」提供了可能性,結合阿里內部的Diamond或者Switch框架(輕量級的開關和動態配置項管理框架),能夠無需更新代碼,僅需推送配置就能夠更新衝突優先級,爲開發和測試提供了極大的便利。
/** * 此處用一個簡單的demo演示下基本的配置,實際的業務要遠比demo複雜 */ @SwakInterface(desc="獲取subtitle") // 使用註解聲明這是一個多實現接口 public interface SubtitleFetcher { @SwakMethod String fetchSubtitle(); } @SwakTag(tags = {"tagA"}) // 使用SwakTag綁定tagA的實現 @Component public class TagASubtitleFetcher implements SubtitleFetcher { @Override public String fetchSubtitle() { return "我是TagA"; } } @Component @SwakTag(tags = {"tagB"}) // 使用SwakTag綁定tagB的實現 public class TagBSubtitleFetcher implements SubtitleFetcher { @Override public String fetchSubtitle() { return "我是TagB"; } }
閒魚服務端應用基本都基於Spring框架。爲了便於在服務端應用上使用SWAK框架,在設計之初,咱們就要求SWAK須要100%地兼容Spring框架。最終的實現版本作到了這一點,不管是業務的bean仍是SWAK框架自身引入的bean,都徹底由Spring容器託管。框架還使用了cglib代理了上圖裏執行過程當中的一系列流程,徹底由框架執行,對開發同窗是徹底透明、無感知的,使用起來如普通的單實現的接口通常,以下代碼塊所示。
@Autowired private SubtitleFetcher subtitleFetcher; //省略大段代碼....... String subtitle = subtitleFetcher.fetchSubtile(); //省略大段代碼.......
目前,SWAK框架在閒魚已經在商品發佈和編輯的部分流程上得以應用,咱們正在積極將SWAK框架擴展到到更多的流程上。下圖是基於SWAK框架的商品域核心功能的改造計劃。通過基於SWAK的升級改造,閒魚商品域核心功能按照業務隔離,各業務開發同窗僅需關係其對應業務的開發便可,其通用邏輯和業務隔離由基於SWAK框架的一層和二層充分保證。代碼質量和開發效率將得到顯著提高。
閒魚自研的SWAK這一多實現規則化執行框架,能夠很好地解決平臺代碼和業務代碼耦合嚴重難以分離、業務和業務之間代碼交織缺乏拆解的問題。而且SWAK 100%兼容Spring,使用方便,快速上手。名副其實地,SWAK框架就像瑞士軍刀同樣能夠適用於多種場景,小巧方便。固然,SWAK仍在不斷進化,特性和功能仍在不斷豐富。相似地,在閒魚還有不少有意思的、創造性的嘗試。
原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。