滴滴出行平臺業務架構演進
**桔妹導讀:**爲了滿足不同用戶在價格、體驗等方面的差異化訴求,滴滴提供了越來越豐富的品類,這些品類大體流程是類似的,在一些細節體驗上有差異,一套架構如何兼顧隔離和複用,同時支持這些品類,且看滴滴服務端技術的灣流平臺怎麼做。
**1. **
項目背景
在滴滴打車業務中,服務端 API 向上接收端上請求並組裝返回,向下串接訂單、計價、收銀等業務中臺的各個系統,完成整個打車流程,灣流平臺項目期望在服務端 API 層打造一個「出行中臺」。在已有業務中臺的情況下,爲什麼還要在業務「前臺」API 層打造一個「出行中臺」,這和出行業務的流量模式是分不開的。
****1.1 ****流量模式
1.1.1 傳統和業界常規的 “錐狀” 流量分配模式
傳統的典型中臺架構是 “大中臺小前臺”,造就這種架構的原因與流量分配模式相關。以電商領域爲例,中臺抽象了電商相關的業務實體如:訂單、收銀臺、商品等,而不同業務線之間的流量入口是分開的,不同 BU 間能夠以小前臺的方式閉環實現。這種“大中臺小前臺” 的架構,可以支持快速構建原型產品進行試錯探索,中臺提供電商標準的基礎能力,前臺各自閉合實現 B2C、C2C 等業務,而業務間互不影響。
流量分配模式表現爲:多業務(或品類)開放的前臺流量接入,轉化至統一有限的業務中臺,最終落至基礎平臺。
1.1.2 網約車獨特的 “菱形” 流量分配模式
滴滴網約車業務核心是打車,爲了滿足不同用戶的需求(定價、應答率、體驗等),通過品類區分提供差異化服務能力,從最初的出租車、專車、快車延展到了如今幾十個品類。而入口始終圍繞在司乘兩端、開放平臺。
流量分配模式表現爲:多品類由統一的端接入流量入口(API)並完成各品類的主要業務邏輯處理,再交由統一的業務中臺,最終落至基礎平臺。
網約車的「菱形」流量分配模式註定:服務端 API 一方面需要支持一些跨 BU 的平臺級需求,如:春節服務費、疫情停開服等;另一方面也要支持不同 BU 間的差異化需求,如出租車使用打表計價不用線上計價等。
隨着品類越來越豐富,這些差異邏輯也越來越多,導致系統越來越臃腫,複雜度越來越高,迭代效率下降。所以需要將服務端 API 通用的部分下沉,並且開放差異定製的機制,同時兼顧隔離和複用,灣流平臺項目應運而生。
1.2 服務端 API 職責定位
在開始前,需要先明確服務端 API 的核心職責。
****◎ 核心職責
-
流量染色:識別和定義接入流量中的品類、場景、功能,並轉義標識爲統一的業務特徵。
-
流程串接:根據不同事件 / 請求按照相應的邏輯和流程調用下游服務,以完成具體的功能。
-
數據渲染:將處理結果數據按不同的端或品類 / 場景要求渲染成對應的數據視圖。
****◎ 終極問題
-
複用:what(複用什麼,複用到什麼級別), how(怎麼實現複用)
-
隔離:what(隔離的是什麼),how(隔離機制是什麼)
1.3 灣流平臺演進
在過去幾年時間裏,基於上述背景我們一直在不斷探索,以下簡單介紹下灣流平臺項目前兩個版本迭代的情況。
1.3.1 灣流平臺 1.0(2017-2018)
1.0 階段主要解決的是快速增加新品類和不同 BU 間代碼隔離的問題,使用配置化和插件化解決。配置化主要是統一了上下游產品描述協議,形成產品描述 N 元組,並抽象一套通用的 N 元組到功能的映射規則;插件化利用插件包隔離不同 BU 間的代碼,運行時插件選擇器根據流量特徵分發到對應插件包。
****◎ 遺留問題
-
配置化依賴於功能抽象,需要一套統一的抽象方法
-
插件化依賴於穩固的流程,以及清晰的功能邊界。按差異開放插件點會導致插件定義不明確、粒度無法把控、插入點不穩定等問題,長期維護困難。
1.3.2 灣流平臺 2.0(2018)
2.0 一方面要解決 1.0 遺留的功能抽象、流程固化等問題,一方面還要面臨複雜度越來越高的服務端 api 系統。爲此我們借鑑了 DDD 的思路,開啓了灣流平臺 2.0 的改造。
- 宏觀上,根據核心數據 & 職能對服務進行了拆分,將一個大模塊拆分成多個垂直閉環的子模塊,即領域化。通過分治的方式,降低了整體的複雜度,同時也解決了所有團隊成員在一個模塊開發導致的上線衝突和排隊情況。
- 微觀上,按流程和功能對領域服務進一步分析,進行功能聚合與抽象,即組件化。提高複用性,解決業務擴展性和開發效率的問題。
****◎ 遺留問題
-
未做系統性的框架約束,迭代容易破壞原有結構
-
側重於分治和抽象,未同步考慮品類間隔離問題
**2. **
灣流平臺 3.0 詳細方案
2.1 總體思路
前面也提到,灣流平臺核心要解決隔離和複用的問題。3.0 整體思路是把服務端 API 的業務邏輯分爲兩層,一層是用於串接狀態流轉的流程層,一層是用於完成各個垂類功能的能力組件層。流程層既包含從預估、發單到完單的宏觀打車流程,也包含每個接口的執行流程。宏觀的打車流程基本品類間是統一的,與端的交互協議也是統一的;每個接口執行流程不同品類間由於業務形態差異,會有部分不一致。我們把接口的執行流程做環節抽象,形成一個個的 step,沉澱一套接口標準通用的執行 step,品類可以根據各自的差異,重載 step。
能力組件抽象聚合了一些垂直的功能,組件內部按照策略模式,根據不同的品類場景使用不同的策略完成組件行爲。如播單組件,提供了延遲播單、實時播單、輪次播單等模式,專車預約採用延遲播單,這種是在播單組件中通過配置實現。如果一些有特別大的差異,比如出租車要實現一個全新的播單模式並且不具備通用性,也可以由出租車實現這個新的模式,通過插件的形式掛載進來。
經過這樣改造之後,同時配套誕生了一些平臺產品,輔助提高開發效率。如流程編排中心,可以根據不同的品類場景,對接口流程環節進行編排;特徵管理平臺,統一管控業務特徵,保持業務描述統一;品類配置中心,從品類場景視角,配置不同能力的行爲模式,快速上線新品類場景。
****2.2 ****框架介紹
爲了實現總體思路,我們開發了一套代碼運行框架,命名爲 DuKang,何以解憂,唯有 DuKang!依託 DuKang 框架,解決我們隔離和複用的難題。
DuKang 框架,針對每個接口,按照下圖流程執行調度,涉及 InputSource、Transport、TransportFactor、StepRuntime、Step、Ability 等核心概念。
其中核心的要點是,Transport 作爲流程承載器,提供了一個 base 的基礎流程實現,不同品類可繼承 BaseTransport,然後可以針對差異的流程環節 step 進行重載,但整個流程是由流程驅動引擎調度,各品類保持一致。ability 是能力組件,組件內部提供了一組通用的 mode,不同品類場景通過配置化方式複用這些 mode,同時也向業務開放了定製 mode 的機制,業務可以通過使用 biz 定製自己獨有的 mode,掛載到 ability 下,實現差異化功能。
服務端 API 語言棧以 php 和 golang 爲主,其中老的服務主要是用 php 寫的,整體逐步在往 golang 上遷移,新服務都是直接採用的 golang。Dukang 框架同時支持了 php 和 golang 兩個版本,下面以 php 版本,成單接口爲例,展示 dukang 框架的運行過程:
//配置文件,管理流程環節,以及提供給不同品類註冊各自transport
{
"name": "ConfirmOrder,
"transports": {
"default": "\\DuKang\\Transport\\ConfirmOrderaseTransport",
"express": "\\DuKang\\Express\\Transport\\ConfirmOrderExpressTransport",
"luxury": "\\DuKang\\Luxury\\Transport\\ConfirmOrderLuxuryTransport",
"taxi": "\\DuKang\\Taxi\\Transport\\ConfirmOrderTaxiTransport",
},
"steps": [
{
"step_id": "fetchInfoStep",
"description": "獲取基本信息"
},
{
"step_id": "confirmTravelStep",
"description": "確認行程信息"
},
{
"step_id": "confirmBillStep",
"description": "確認計價信息"
},
{
"step_id": "checkStep",
"description": "成單檢查"
},
{
"step_id": "fillOrderDetailStep",
"description": "訂單維度填充"
},
{
"step_id": "sendOrderCommandStep",
"description": "訂單處理操作"
},
{
"step_id": "sendDriverCommandStep",
"description": "司機處理操作"
},
{
"step_id": "sendSchedulingCommandStep",
"description": "調度處理操作"
},
{
"step_id": "buildResponseStep",
"description": "構建響應"
},
{
"step_id": "asyncOperationStep",
"description": "異步操作"
},
{
"step_id": "writeLogStep",
"description": "日誌處理"
}
]
}
// dukang框架核心執行過程
try {
// 加載並解析接口配置,包括BizConfig、StepConfig
$oBizConf = BizConfig::load($sConfigStr);
// 獲取輸入源數據,包括Request和基礎數據獲取
$oInputSource = new ConfirmOrdernputSource();
// 構造StepRuntime
$oStepRuntime = new ConfirmOrdertepRuntime($oInputSource);
// 將接口配置對象、StepRuntime放入流程調度器
$oFlowScheduler = new FlowScheduler($oBizConf, $oStepRuntime);
// 初始化傳輸器路由因子
$oTransportSelectFactor = new ConfirmOrderTransportSelectFactor($oInputSource);
if($oFlowScheduler->selectTransport($oTransportSelectFactor))
{
// 執行流程調度
$oFlowScheduler->run();
}
// 異常處理原則:接口外層只處理DuKangException和Exception,Step或者Ability處理異常則先處理邏輯再拋異常
} catch (DuKangException $e) {
$aResp = [
'errno' => $e->getCode(),
'errmsg' => $e->getMessage(),
];
echo json_encode($aResp);
} catch (\Exception $e) {
$aResp = [
'errno' => $e->getCode(),
'errmsg' => $e->getMessage(),
];
echo json_encode($aResp);
}
接下來,再展開對 dukang 的一些核心概念進行講解
2.2.1 Transport - 業務傳輸器 / 承載器
****◎ 定義
-
針對單接口內不同運力 (或品類) 進行抽象得來的流程載體
-
任一業務必有其承載的流程和執行順序
****◎ 約束
-
BaseTransport 覆蓋當前接口業務的通用流程
-
任一接口內有且只有一個 BaseTransport
-
XxxTransport 覆蓋不同運力 (或品類) 的差異化實現
-
任一接口內的差異化 XxxTransport 必須繼承自 BaseTransport
-
任一 Transport 至少包含一個 Step
Transport <=> [N]Step (N >= 1)
2.2.2 Step - 流程環節
****◎ 定義
-
針對單接口內的業務進行抽象出來的環節載體
-
單一 Step 是某一段業務環節或功能的具體實現
****◎ 特性
-
單一 Step 是大粒度差異化 (如豪華車 / 出租車) 的有效手段, 可通過 Override Step 實現
-
Step 間的通信通過統一運行時數據總線 StepRuntime 串聯,理論上可實現熱拔插
****◎ 圖例
-
業務流程驅動型接口:以串聯各個處理流程爲主
-
數據驅動型接口:以構建業務特徵數據爲主
2.2.3 StepRuntime - 運行時數據總線
****◎ 定義
- 流程串聯運行時數據總線
****◎ 約束
- StepRuntime 只能作爲 Ability 的輸入,不能在 Ability 及下層邏輯中對 StepRuntime 的業務特徵和數據進行修改
****◎ 圖例
2.2.4 InputSource - 輸入源
****◎ 定義
-
外部輸入數據源,爲 TransportFactor 準備數據
-
用於消除外部執行環境差異化,隔離外部入參
****◎ 約束
- 進入 Step 之後具有隻讀屬性,被 StepRuntime 引用,後續業務邏輯不可修改其包含的所有特徵及數據內容
****◎ 圖例
2.2.5 Transport Factor - 傳輸器因子
****◎ 定義
-
業務傳輸器 Transport 決策因子
-
不同接口可能會採取不同的因子進行 Transport 決策
-
目前實現的有針對訂單維度 product_id,針對司機維度 car_level
-
可選擇端來源作爲決策因子,如滴滴出行 app、開放平臺、禮橙專車 app 等
****◎ 約束
-
Factor 必須是確定可選擇的幾類因子,不能由 RD 同學自由編寫
-
同類業務接口原則上 TransportFactor 要儘量保持一致
2.2.6 Ability - 能力組件
2.2.6.1 概念描述
◎ 定義
- 以特徵數據爲視角,對聚焦業務進行提煉和抽象,形成能力組件
◎ 設計原則
-
面向可複用設計
-
面向可擴展差異化設計
2.2.6.2 業務特徵
****◎ 業務特徵概念歸納
- 業務表達:播單計劃 = $sStartAddDuseTime + $bIsDelayBroadcast + $bIsRepeatAssign+ $iBroadcastAssignType + $iBroadcastExpire
****◎ 業務特徵解決的問題
-
規範業務屬性或字段語義,避免歧義和未知語義
-
定義:業務 = 有序流程 * 控制 (特徵 X, 特徵 Y, 特徵 Z, ...), 控制 = 染色 | 填充 | 複寫 | 合併 | 標記
2.2.6.3 能力組件抽取過程
針對一組業務特徵,將圍繞這些業務特徵的生產、修改操作聚合,形成能力組件。如圍繞播單特徵的播單組件、價格特徵的計價組件等等
****◎ Ability 擴展性
支持以 Addtional 的業務擴展 Ability Mode,即前面提到的 biz 形式定製化 mode,從而達到不同品類在 Ability 上的隔離。
****◎ Ability + 品類場景配置最終思路
複用:基於品類 + 場景(N 元組)配置化,mode selector 靈活決策能力組件的執行模式
差異化:業務通過 biz 實現 mode 的定製,配置化動態加載
2.3 應用框架目錄結構
php 模塊目錄結構示例,golang 模塊整體類似
// 模塊根目錄
├── Dukang // 新引入的內容,區隔老代碼,未來將替代hermes
│ ├── Ability // 能力目錄
│ │ ├── Common // 公用能力
│ │ │ ├── DispatchOrder
│ │ │ │ └── DispatchOrderComponent.php
│ │ │ └── VirtualPhone
│ │ │ └── VirtualPhoneComponent.php
│ │ └── Express // 品類特有能力擴展
│ │ └── DispatchOrder
│ │ └── DispatchOrderComponent.php
│ ├── Config // 接口配置
│ │ └── ConfirmOrder.json
│ ├── InputSource // 接口輸入
│ │ └── ConfirmOrderInputSource.php
│ ├── StepRuntime // 全局數據總線
│ │ └── ConfirmOrderStepRuntime.php
│ ├── Model // 公用model
│ │ ├── Dao
│ │ ├── Driver
│ │ │ └── DriverModel.php
│ │ └── Order
│ │ └── OrderModel.php
│ ├── Service // 公用service
│ │ └── Driver
│ │ └── DriverService.php
│ ├── TransportFactor // Transport因子
│ │ └── ConfirmOrderTransportFactor.php
│ └── Transport // 流程串接
│ ├── Base
│ │ └── ConfirmOrderBaseTransport.php
│ └── Taxi
│ └── ConfirmOrderTaxiTransport.php
└── vendor
└── dukang
└── framework
├── idl // 數據字典(Dimensions,標準dto)
└── src // 框架代碼
本文作者
滴滴服務端技術團隊是滴滴網約車核心後臺研發團隊,負責網約車核心出行、品類技術、開放平臺、業務架構與創新業務、業務中間件等公司級核心項目的研發,支持快車、專車、優步、優享、豪華車、拼車、出租車等出行業務。在這裏,你將面對高併發、大流量、複雜業務的極限挑戰;你將和頂級工程師一起打造分鐘級接入新業務、新功能的技術實現方案。
滴滴服務端技術團隊長期招高級後端研發工程師職位,歡迎有興趣的小夥伴加入,可投遞簡歷至 diditech@didiglobal.com,郵件請郵件主題請命名爲「姓名 - 投遞崗位 - 投遞團隊」。
內容編輯 | Hokka
聯繫我們 | DiDiTech@didiglobal.com
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/TeMTWJlplHkxxKfS6R87hw