作者:閑魚技術-紫思

簡介

業務的不斷發展、商品類型的不斷增多、不斷添加的業務需求使得閑魚的代碼出現「bad smell」——平臺代碼和業務代碼耦合嚴重難以分離;業務和業務之間代碼交織缺少拆解。這也是行業中的老大難問題。為解決此類問題,閑魚自研了一套技術框架——SWAK。本文帶大家一起看看SWAK是怎麼解構閑魚代碼的。

SWAK是Swiss Army Knife的簡稱,眾所周知,瑞士軍刀是一款小巧靈活、適用於多種場景的工具。在閑魚服務端,SWAK框架也是這樣一種小巧靈活、適用於多種場景的技術框架, 它所要使用的場景都具有同一個特點——多實現間的規則化執行。本文將以一個例子開篇,來詳細介紹其中的概念。

多實現和規則化執行

熟悉閑魚的朋友們應該知道,在閑魚App裡面,商品有豐富的表現形式,不妨叫做類型A、類型B和類型C,各種類型也可以有各自的子類型。每種類型的業務邏輯存在一定的共性,但是也存在部分差異——如在分享頁面中,subtitle欄位的展示邏輯就不盡相同:

g.alicdn.com/idleFish-F (二維碼自動識別)

這種單一的實現通常會被寫成如下的代碼:

if(A類型) {
if(A1類型) {
doSomething1();
}else if(A2類型) {
doSomething2();
}
} else if(B類型) {
doSomething3();
} else if(C類型) {
if(C1類型) {
doSomething4();
}else if(C2類型) {
doSomething5();
}
}

類似的代碼大家應該都寫過不少。邏輯簡單的時候寫成這樣無可厚非,但當邏輯開始變複雜的時候這種寫法會具有較多的壞處:

  • 難以抽出公共的邏輯,代碼塊愈發臃腫。
  • 有較多相同點少量異同點的新類型的實現很難復用原先的代碼。
  • 各個類型的代碼實際上融合在一塊,更改代碼可能會影響到其他類型,提高上線風險和測試回歸成本。
  • 對於新接手的開發人員來說,理解成本高,上手難度大,無形中降低開發效率。

按照面向對象的思想,獲取title的方式對於所有類型都是一致的,應該沉澱成平臺邏輯,而獲取subtitle就可以抽象成一個介面方法,而類型A、類型B和類型C的寶貝都具有各自的實現而已。對於 獲取subtitle這個介面方法來說,它有著多種實現。

那麼什麼是規則化執行呢?在上面的例子中,我們按照了商品的類型(type)進行了邏輯的分離,但通常情況下並非能分隔地如此徹底。舉一個例子,運營團隊的劃分可能也按照商品類型(type)做劃分,也有可能按照類目(category,如手機、3C數碼、服飾、圖書等)體系來做劃分,甚至還有可能按照地域進行劃分。那麼一個商品可能既會受到商品類型體系的約束,又會受到類目體系的約束,還會受到地域的約束。如果幾種約束不一致的話,就會產生衝突。比如subtitle欄位,從類型A的視角上來看應該顯示價格,在圖書類目的視角下或許應該透出出版社——畢竟愛讀書的人大多更關注質量而出版社是衡量質量的一個重要標準。是展示價格,還是出版社?或者都展示?如果都展示的話先展示價格還是先展示出版社?如果一行不夠放下所有內容又怎麼辦?無論是上述的哪一種展示方式,背後都是「規則」(在設計模式裏,稱之為「策略」),代碼也無非是按照「規則」進行編寫而已。

以上的例子是多實現規則化執行的一個經典場景。類似地,如ABTest、雙寫等邏輯也是多實現規則化執行的應用場景。

基本思想

在上面的例子中,按照商品的類型或者按照商品的類目進行區分會產生衝突。其實無所謂類型或者類目,對於商品這個對象來說,無非是給其貼上了不同的標籤而已——如一個類型A的圖書類目寶貝被貼上「類型A」和「圖書」兩個標籤。「類型A」的獲取subtitle介面方法對應著一種實現,而「圖書」的獲取subtitle介面方法又對應著另一個實現。當一個對象被貼多個標籤的時候,多個標籤對應的實現就會產生衝突。

衝突的解決依賴於「規則」。「規則」最重要的兩個部分是——優先順序(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仍在不斷進化,特性和功能仍在不斷豐富。類似地,在閑魚還有很多有意思的、創造性的嘗試。如果對此感興趣,歡迎加入我們。

加入閑魚,一起玩些「酷」的

閑魚技術團隊是一隻短小精悍的工程技術團隊。我們不僅關注於業務問題的有效解決,同時我們在推動打破技術棧分工限制(android/iOS/Html5/Server 編程模型和語言的統一)、計算機視覺技術在移動終端上的前沿實踐工作。作為閑魚技術團隊的軟體工程師,您有機會去展示您所有的才能和勇氣,在整個產品的演進和用戶問題解決中證明技術發展是改變生活方式的動力。

簡歷投遞:[email protected]


推薦閱讀:
查看原文 >>
相關文章