UP主分銷系統平臺化升級與演進
1. 引言
近年來,達人分銷模式推動電商銷售額呈現爆發式增長,其核心在于連接品牌商家與具有影響力的達人,通過內容與商品撮合進行精準推廣觸達消費者,實現流量變現和GMV快速增長。B站達人帶貨業務發展快速,從系統平臺層面來看,面臨諸多挑戰:
- 歷史系統腐化影響:復雜業務場景支撐如(直播/視頻/圖文跨場景不同資源位的帶貨分銷玩法)及智能托管模式探索受到一定程度限制
- 協同成本相對較高:歷史模型分散且不穩定,業務變更會頻繁引發上下游協同,迭代效率往往不達預期
- 平臺擴展出現瓶頸:現有系統存儲耦合等技術債難以支撐億級數據量讀寫,無法匹配當前業務規模化發展
因此,在帶貨業務持續保持高速增長的階段,構建高效穩定且具有較高擴展性的達人分銷系統尤為重要。本文將詳細介紹UP主帶貨分銷平臺現狀,基于領域驅動設計(DDD)思想,如何對現有系統進行平臺化重構,實現分銷系統整體架構的升級。
2. 現狀&目標
2.1 現有系統痛點
B站帶貨分銷業務涉及直播、視頻、圖文等場域,當前已覆蓋大部分資源位場景。業務快速發展的同時也歷經團隊組織的調整,形成了如下圖所示的系統調用鏈現狀。以用戶最基礎的操作場景為例,UP主在分銷平臺選擇不同內容不同資源位進行帶貨,由原來單一視頻場景擴展到圖文、直播帶貨場景后,需要關聯多個應用多種數據模型,超過數十張數據表的讀寫,很小業務迭代和變更都會涉及不同應用間的聯動開發,同時會引發下游不同團隊(如引擎、數據等)協同變更支持,額外增加用戶端帶貨服務體驗治理難度。
圖片
實際上的情況遠比上圖所示鏈路復雜,從整體看,核心問題主要有以下幾個方面。
- 架構層面擴展性低,煙囪式系統導致3套獨立數據模型、2種開發語言并存,維護成本和迭代升級瓶頸大,復雜業務的支撐變的越來越困難。
- 服務耦合度高,系統歷史債重,多個業務深度耦合,邏輯分散在多個模塊,部分接口交織,改動會直接影響底層調用及下游依賴(引擎/算法),系統穩定性無法閉環。
- 存儲瓶頸嚴重,當前存儲單表模式,核心數據表量級和日均慢查數均已遠遠超出上限,面對開閉環分銷業務快速發展,難以承載接口性能和未來帶貨業務增量需求。
2.2 平臺化目標
為從根本上改變系統現狀,解決以上痛點,需要重構系統架構,推行帶貨分銷系統平臺化升級,首先確立了幾個重要目標。
- 提高系統擴展性和可維護性:建立穩定的帶貨分銷業務數據模型,抽象服務和流程,方案可覆蓋現有直播、視頻、圖文場域20多種帶貨場景。
- 獨立鏈路核心業務:通過DDD劃分領域,根據領域邊界拆分業務域,升級系統架構,實現核心業務內聚,關聯域解耦。
- 提升系統穩定性:通過存儲隔離及分庫分表,升級中間件,統一業務框架和研發約定,實時感知觀測系統異常。
面對系統多年歷史的積累,黑盒面大,如何整體推動是個比較大的問題。根據系統預演和目標優先級,制定了分階段的演進策略,如下所示,后文將會進一步介紹平臺化實踐的一些關鍵過程和關鍵問題。
第一步:建立統一模型和落地onebp方案,拆分解耦并遷移視頻帶貨業務
第二步:遷移圖文/直播帶貨應用至新模型,完成域內業務與服務統一
第三步:域外統一切至帶貨分銷新模型,一套模型N種業務場景,實現上下游一致表達
圖片
3. 業務建模與設計
分銷帶貨業務域相對不太聚焦,包括不僅限貨品管理、分銷關系、人貨場撮合、歸因跟蹤、分銷結算等。從全局視角看帶貨分銷業務,主要參與角色由商家、服務商、UP主、消費者等,活動在不同場域,當前業務階段UP主是最核心的分銷者,如下圖所標示區域。
本次平臺化升級一方面需要完全覆蓋重構的幾個核心目標,另一方面需要充分結合業務現狀和團隊開發人員的經驗情況,如何有效結合業務現狀和長期演進進行建模是比較關鍵的問題。領域驅動設計(DDD)作為微服務的主流設計方法,是通過領域模型捕捉領域知識,使用領域模型驅動設計,主要目標是解決復雜業務系統中的設計難題,提高系統的適應性、可維護性和開發效率,與當前業務階段匹配。基于整體考慮,采用該設計思想,去推進整體平臺化實踐,是比較有效的方式,但整個實施路徑不嚴格按照DDD方案論推進,優點在于:
- 能夠幫助團隊統一語言,捕捉復雜業務邏輯,可以很好解決當前分銷業務規則混亂以及場域多變依賴子域復雜的現狀。
- 通過領域劃分、聚合和限界上下文的設計,可以解耦系統,提高可維護性和擴展性
- 結合實際考慮聚合事務邊界,降低建模復雜度,兼顧團隊在經驗上的缺乏和ROI,避免貧血模型或過度設計的反模式,很容易地實現架構演進
3.1 邊界劃分
從業務視角出發,按照上文中提到的各角色的用例動線,通過分析分銷業務流程和所有用例(局部用例圖如下圖所示)去識別業務域,進而劃分子域,定義模型關系。
圖片
識別出系統中的主要業務域,并劃分出核心域(關鍵業務)、支撐域(輔助業務)和通用域(共享業務)如下圖。
圖片
3.2 領域模型
領域模型反映了業務領域中的實體、值對象、聚合、工廠和存儲庫等。在分銷業務中,通過構建抽象模型,相對穩定描述和解決業務問題。對于UP分銷任務,關聯了內容載體(視頻/直播/圖文等)和層級以及投放資源位信息,同時分銷者(UP主)多分銷任務可以被一個分銷計劃管理,內容層級、資源位和商品(含虛擬品)核心信息構成了投放的最小單元,該模型可以表達如下。
圖片
說明:
- 內容:內容載體(稿件、圖文、專欄、直播等)
- 資源位:每個內容載體有多個資源位,如視頻有浮層、彈幕、框下、評論、圖文等
- 商品:每個資源位可以掛多個商品(開環商品、閉環商品、虛擬品等)
- 分銷任務:帶貨分銷單元,可關聯內容資源位和多個商品
- 分銷計劃:由若干分銷任務構成,用于任務單元管理層級的擴展
任務單元對象關系如下,需要說明的是,樣式Object按靜態類處理,用于模型的映射,支撐組件樣式在消費者的展示。
圖片
3.2 事件驅動
領域事件通過將業務動作顯式化,促進系統的高內聚、低耦合,是構建復雜業務系統的有效模式。在分銷域中,當分銷任務聚合根狀態及核心信息發生變化時生成事件,保障任務事件與業務邏輯的緊密關聯性。在設計時,考慮了全局事件,比如創建分銷任務的事件定義如下
該事件通用消息報文定義如下:
{
"contentTask":{
private Long id
// UP主mid賬號
private Long mid;
// 商業賬號id
private Long merchantId;
// 內容id(評論/視頻/圖文等)
private Long contentId;
// 內容類型(評論/視頻/圖文等)
private Integer contentType;
// 任務狀態
private Integer taskStatus;
...
},
"goodsMappings":[{
// 分銷任務id
private Long taskId
// 全局唯一商品id
private Long itemId;
// 跳轉鏈接,含歸因
private String jumpUrl;
...
},{...}]
}
3.3 狀態機
帶貨場域分銷狀態主要在于分銷任務狀態的流轉,如下圖所示,分銷帶貨任務從草稿態開始到任務終態(包含刪除和審核不通過兩種狀態)。
圖片
業務階段狀態碼(終態、可更改態、未完成態)分別定義如下:
// 完成態
public static final List<Integer> END_STATUS = Collections.unmodifiableList(Lists.newArrayList(
DELETED.code, REJECT.code
));
// 未完成態
public static final List<Integer> NOT_FINISHED_STATUS = Collections.unmodifiableList(Lists.newArrayList(
DRAFT.code, AUDITING.code, REJECT.code, LAUNCHING.code
));
// 可更改態
public static final List<Integer> CAN_UPDATE_STATUS = Collections.unmodifiableList(Lists.newArrayList(
DRAFT.getCode(), LAUNCHING.getCode(), AUDITING.getCode()
));
3.4 存儲設計
3.4.1 分庫分表方案
在完成業務建模后,需要考慮存儲的整體設計。上文現狀部分已提到過單表數據量過億、數據模型不能完全表達分銷業務(和其他業務存儲耦合)、慢查詢數量日均十萬級的情況,系統基本已無擴展性。總結下來,可采用的方案主要包括分庫分表、分區表、冷熱分離和分布式數據庫等4種方案,從實際的業務發展來看,QPS/TPS在評論等場景中持續增長,為有效應對單表億級數據的存儲與性能挑戰,同時為后續業務擴展預留彈性空間,選擇分庫分表來做基礎存儲設計,那么問題來了,分庫分表策略是什么?
通常分庫分表可以分為水平分片和垂直分片,不過通常分庫分表更多指水平分片,也就是將數據按某種規則分布到不同的庫或表中,常見的分片規則比如哈希取模、范圍分片、一致性哈希、按日期或時間分片、枚舉分片、地理位置分片、復合分片等。回到業務用例場景,需要支持分銷者id(account_id)和分銷任務id(task_id)多維度的查詢,復合分片能很好解決,假如按4庫16張表為例,按照如下規則,那么根據task_id和account_id都能滿足大多數業務用例的查詢了。
3.4.2 中間件選型
確定分片策略后,進一步考慮分庫分表中間件方案,這里給出了業內常見中間件的對比分析,可參見下表。需要說明的是,這里沒有提及TDDL和DRDS等商業或非開源方案,涉及資源依賴等問題。表中Akso-Proxy是B站分庫分表中間件的一種解決方案,目前對于不規則的分片策略暫未支持。在選型的考慮上有兩個點相對比較重要:
- 能支持復合分片的方案訴求,也即支持指定庫/表查詢(HINT),支持多字段分庫分表
- 輕量級低成本接入,能匹配項目節奏及部門當前基建的應用維護現狀
Sharding-JDBC是相對輕量級java框架,使用客戶端直連數據庫,無需額外部署和依賴,可被視為增強版JDBC驅動。目前在部門交易域使用比較廣泛,也集成到腳手架,運維升級可以統一管理,成本基本可忽略,結合優勢和不足的權衡,Sharding-JDBC是當前階段最為合適的選擇。
圖片
在實踐過程中,定義了分庫分表策略,繼承AbstractShardingAlgorithm,依賴關系如下,實現中間中ComplexKeysShardingAlgorithm中的interface方法doSharding,在 doSharding 方法中,根據分片鍵計算目標數據庫和數據表,核心接口定義與基礎實現如下。
圖片
public class DatabaseShardingAlgorithm extends AbstractShardingAlgorithm {
@Override
public String doSharding(String shardingKey) {
// 根據分片鍵計算目標數據庫
int hash = shardingKey.hashCode();
int databaseIndex = Math.abs(hash % N); // 假設有N個數據庫
return "db_" + databaseIndex;
}
}
4. 架構模式
軟件架構從單機、集中式到分布式微服務架構經歷了三個階段的演進,每個階段都以提高系統響應為目標,通過分離復雜度來實現業務優化。傳統的軟件架構大多都是三層架構,如下圖所示,解決了程序內代碼間調用復雜、代碼職責不清的問題。但依然屬于邏輯分層概念,核心問題就是技術建模和業務需求存在視角差異,隨著項目的迭代演進,各層級與各模塊之間可能存在交叉引用。從分銷系統現狀來看,屬于典型的傳統三層架構模式。
圖片
DDD(領域驅動設計) 是一種處理高度復雜領域的設計思想,可以有效分離技術實現的復雜性,圍繞業務概念構建領域模型來控制業務復雜度,非常適合微服務架構的演進。其分層架構設計包括用戶接口層、應用層、領域層和基礎層,如下所示。每層都扮演著特定的角色,通過嚴格的分層原則實現松耦合,解決了三層架構中核心業務邏輯混亂、代碼改動相互影響大的問題,大大簡化持續性升級和維護。相比較六邊形架構、洋蔥架構、CQRS架構等,其實現復雜度相對較低,作為當前業務架構解決方案更有效。
圖片
4.1 分層架構
如何從三層架構向DDD四層架構演進,需要根據建模階段,重新歸類要素、重新劃分層次、重新確定交互規則和職責邊界,主要集中在業務邏輯層和數據訪問層的劃分,在實踐過程中分銷服務分層演進過程和常見演進方式類似,如下圖。
圖片
在設計視角上,根據業務功能歸屬,提前做好不同業務領域的拆分。在技術實現上,通過實現與接口分離,在 application 與 infrastructure 之間新增 domain 領域層,讓domain層盡量獨立,不耦合與任何模塊,下圖描述的是分銷平臺化從三層架構現狀升級到四層DDD架構的實踐情況。
圖片
四層架構在平臺化項目工程中具體描述如下表所示
圖片
具體的依賴調用關系以分銷任務單元的生命周期為例,如下圖所示。這里需要指出的是,domain領域層非必須依賴調用層,通常在業務服務表達中,簡單業務非核心領域的調用可以直接跨過,這樣簡化了非核心領域的調用層級,靈活性高。
圖片
4.2 規范約定
確定分層架構后,需要進行相關技術層面的代碼約定和規范,主要是異常處理方案和錯誤碼規范,這些作為系統平臺化可觀測能力的基礎條件。
對于異常處理方案,每一層(包括系統內/系統間)僅捕獲當前層處理的異常,跨系統間不拋出異常,默認使用通用返回對象+錯誤碼的形式管理。
對于錯誤碼規范,按照8位約定,基礎枚舉定義如下。在規范要求中,需要統一遵循i18n,命名規范統一約束成 {平臺域/業務域}-{領域}-{系統名縮寫}-{錯誤類型}-{錯誤碼數字編號}。舉個例子,在分銷任務業務用例中,敏感詞稿件評論分銷任務創建失敗,那么錯誤碼則設定為:81211001,這樣就對上下游和域內錯誤異常進行了全局的定義,可快速識別平臺異常根因。
圖片
5. 系統遷移策略
5.1 切流方案
切流和數據遷移這兩個部分是大項目中的關鍵,風險很高。切流指的是將流量從舊系統切換到新系統,而數據遷移則是將舊數據轉移到新系統中。這兩者都可能遇到很多問題,比如數據不一致、服務中斷、兼容性等問題,對于耦合依賴重的系統,更需要考慮切流與遷移成本以及整體項目節奏和進度,這里面涉及眾多關鍵問題。
5.1.1 雙向同步
在分銷系統中,涉及相當多的上下游,比如引擎、算法、數據以及其他業務方依賴,實際過程中不可能做到各方節奏上同步性,因此多套老模型老庫表的業務數據和新模型必須保持高度的一致,否則就會出現線上故障。當部分切流后,命中的用戶會請求到新系統,數據會直接寫新,同時異構寫老,要做到對依賴方無影響;同時存在較多場景需要讀取和變更該用戶的存量數據,需要將老模型數據同步異構至新模型庫表,當然后者在切流100%后就不再需要了。所以從整體看,數據流是雙向同步的,這也決定了平臺化升級切流和數據同步就具備較高的復雜性。
圖片
歷史業務數據區分度較低,改動也將會影響其他的業務方,如何進行雙向同步?同時還要保障成本最低、風險最小?這里面有幾個關鍵性切流設計:
- 分銷任務單元的變更僅在一端發生:一旦數據同步期間發生意外,以發生變更側的狀態為準,而變更側定義為數據產生端。這里在老庫表新增新模型的唯一id,在新庫表新增ref_id,如果unique_id或ref_id非0,則表示當前的記錄是從對側同步過來的。
- 變更操作按新系統是否存在數據記錄來判斷:灰度期請求入口均在老系統,就可能存在兩套id,變更操作請求的處理,若新系統能查到,則創建正常處理,否則拒絕。
- binlog循壞問題檢查:數據變更是不是因同步而改變,新增輔助flag字段,用于檢查同步流向。
圖片
5.1.2 實施方案
這里選擇視頻帶貨服務和平臺化升級后的新服務來說明整個切流方案的實施過程(圖文帶貨和直播帶貨獨立服務均采用類似方案),其中mas代表視頻分銷應用,cbp代表新應用。
第一步,視為初始狀態,業務依舊100%在老系統。這里會有有兩個前置處理:
- mas數據庫同步cbp,也即完成mas數據的存量快照搬遷和mas數據的增量同步
- cbp準備好,完成CbpToMas的部署和開關配置,不接入實際流量
圖片
圖片
第二步,開始切流階段,白名單的用戶走新系統,非白名單用戶走老系統。其中,編輯/讀取任務,取決于任務初始是在mas還是在cbp創建的 ,cbp里創建/編輯的任務會同步到mas。
圖片
第三步,切流100%階段,所有用戶創建任務都切到cbp,后續mas系統不再創建任務,因此mas的binlog里應該不再有INSERT,而編輯/讀任務切流還根據任務的初始創建系統走。觀察穩定后,會有個后置處理,翻轉refId,讓所有的請求均走新服務。
圖片
第四步,依賴方遷移,依賴老系統接口業務方,可以直接切換cbp域名完成低成本切換,完成新老服務平緩切換。
圖片
5.1.3 回滾預案
分銷平臺化升級切流環節涉及數十服務和不同模塊,外部系統依賴性強,需要制定應急回滾預案,確保系統的高可用性,盡可能降低對用戶對業務的負面影響,這里采用了系列前置動作。
- 異常感知:切流過程,出現異常,在數據層和服務層通過對賬和流量回放等方式自動觸發異常告警
- 動態配置:異常一旦發生,切流不符預期,可動態調整切流比例,配置化快速回滾,流量重新回到老系統,控制風險;
- 修復工具:當切流產生異常數據,提供快速修復工具,進行數據補償處理
- 回滾預演:上線前多次注入切流異常,進行預演驗證,實際上10min內即可完成數據與應用的回滾處理,結合小流量切流比例,影響相當小
5.2 數據一致性保障
切流和數據同步過程對數據一致性要求比較高,一旦新老模型數據不一致,就會產生系統故障,這對模型對賬也提出了更高的要求:
- 完整性:確保所有數據(全量或增量)均從舊系統遷移到新系統,無遺漏。
- 一致性:新老系統的數據內容(字段值、格式、關聯關系等)完全一致。
- 業務正確性:遷移后的數據能支持業務邏輯的正常運行
除了離線全量對賬作為基礎,同時也設計了增量全套實時對賬流程,如下所示。
圖片
過程中的數據同步問題及時監控和告警,在上線前不斷演練,不斷修正歷史數據和不一致性問題,約99%潛在問題都能被發現和處理,如下實時告警。
圖片
另外結合回滾預案提到的恢復工具和回滾機制,充分控制切流遷移風險,最終保障項目穩定切流,完成了億級數據全量遷移,期間域內和關聯上下游未出現故障問題,系統切流過程高可用質量超出預期。
6. 總結與展望
6.1 階段總結
本文全面介紹了UP主帶貨分銷系統平臺化升級的實踐過程,最終完成了業務技術重構和代碼結構優化。在整個推進過程中,充分考慮了業務發展及系統當前/未來的基礎平臺能力,如下圖所示,提出了平臺化升級的長期目標。
圖片
通過結合DDD基本設計思想,實現業務域劃分和分銷領域模型和事件的定義,然后在存儲和架構分層上,提出了符合業務和團隊現狀的實踐方案,最后設計了穩定且低成本的切流遷移方案,完成了平臺階段性升級目標,并達成如下預期成效:
- 系統擴展性和可維護性增強,核心模型可覆蓋視頻/圖文/直播現有20多種帶貨場景,新場景擴展接入從1-2周降至天級,維護成本大大降低
- 核心業務域完成部分拆分,實現了廣告/主站/結算/數據/引擎等眾多支撐域的解耦
- 系統穩定性提升,新服務異常感知提升至分鐘級,慢sql幾乎未出現,核心模型接口讀寫RT下降4倍左右(如任務單元創建從200ms+下降至40ms+)
這里需要提出的是,過程中引入DDD相對傳統架構而言,可以幫助從業務角度更合理地劃分系統和業務邊界,找到并改進現有架構中的問題。但在初期的架構實現上存在更高的實現成本和復雜度,因此需要做到在合適的場景中選擇合適的方案,結合實際情況會更為有效。另外平臺化升級不僅是技術層面的重構迭代,更是組織模式與業務思維向前走了一大步,整個項目團隊積累了一定的實踐經驗。在系統穩定性提升、復雜業務場景快速接入、資源利用率優化等方面也達成了階段性成果。另外,也還有未完成的部分在途持續推進,比如直播場域帶貨分銷模型遷移、商品等支撐域解耦等。
6.2 未來展望
從業務域應用架構現狀來看,如下圖所示,系統應用調用關系尚較為復雜,長期的治理和平臺化的目標是保持一致的。后續隨著系統平臺化各域的深入演進,將會進一步推動各子域和上下游,從分銷全局視角獲得最優解。
圖片
而分銷業務域的平臺應用架構將也會不斷升級,從現有的20多個應用整合縮簡至幾大核心應用,整體演進規劃如下圖所示,可以看到還有相當多的挑戰,需要在實踐中逐一去解。未來平臺化技術深入方向也將會以用戶價值為核心,持續提升用戶體驗,支撐產品化能力,深化生態合作,充分優化組織的迭代流程,不斷探索平臺智能化(AI托管等),確保在快速增長和變化的業務探索中保持分銷平臺的領先性。
圖片