當前位置:
首頁 > 最新 > 多階段長委託架構模式與應用場景

多階段長委託架構模式與應用場景

在大規模互聯網應用中,系統間相互調用非常普遍,常見的方式有RPC和RESTful API等。它們適合短時延場景,即一次請求和響應在毫秒數量級內完成。但現實應用中有沒有長時延的場景呢?即上游A調用下游B來完成一個任務,而這個任務需要耗時數十秒,甚至幾分鐘、幾小時。

的確有不少這樣的場景。比如在電商支付場景里,在線商城或委託第三方支付商向用戶收錢,只有收錢成功後,商城才給用戶發貨(虛擬商品也稱發貨)。但是收錢成功這個任務,涉及用戶輸入密碼確認,不太可能在毫秒級完成。再比如在視頻點播場景里,用戶或網站編輯上傳了一個電影,在線播放前需要轉碼成MP4或HLS等格式,另外還要通過內容審查。因為視頻轉碼是個既耗CPU、又耗內存、還耗磁碟的操作,及時分散式雲轉碼也不可能毫秒級完成。

我們把這類長時延的調用叫做「長委託」:「長」表示時延長;「委託」表示上游A(又稱「委託方」)把一個任務交給下游B(又稱「受託方」)去做,下游B做完了要給上游A一個反饋,上游A也會督促B的進度,這非常類似生活中的「委託」關係。用個時序圖表示這種計算模型:

圖-1:長委託計算模型

第1步:上游給下游發起長任務委託。通常可以是個RPC請求。

第2步:下游立即回復ACK,只表示任務收到,不表示任務完成。通常是個RCP響應。

第3步:下游開始執行長任務,可能會花費幾秒、幾分鐘、甚至幾小時。在這期間,上游或需定時查看任務進展情況,也可能會催辦,還有可能會取消任務。

第4步:任務完成後,下游給上游一個FIN notification通知或callback回調。

第5步:上游收到通知或回調後,立即更新任務的狀態,設置為完成或失敗。

上述5個步驟,通常需遵循4個原則:

1. 委託方超時重傳: 受託方雖然回復了ACK,表示收到了委託,但是還可能因為處理不當(比如受託方程序Crash等),把委託請求弄丟了。這要求委託方自己通過超時重傳來督辦&催辦。這裡的超時指的是在協定的時間內,任務依然處於既沒有明確成功、也沒有明確失敗的中間狀態。

2. 受託方冪等防重:因為委託方會超時重傳,所以受託方需要辨識出重傳的請求,並對重傳的請求做冪等處理,即先後兩次處理同一個請求,對自己的狀態變更是一樣的,對外界的響應也是一樣的。

3. 共享任務追蹤ID:FIN notification並不是一問一答式同步返回的,而是後期非同步通知的。為了把請求和響應關聯起來,委託方與受託方必須共享一個ID,來追蹤每個委託。同樣這個ID也是受託方冪等防重的依據。共享ID也意味著它需要侵入到雙方系統內。

4. 上下游Echo回傳:同樣是因為響應是非同步的,需要建立請求與響應之間的對應關係,為了方便通信,需要下游給上游增加一個拓展欄位,這個拓展欄位好比回聲(Echo),需要怎麼傳來的,怎麼回傳回去。讀者可能會問有了第3點的「追蹤ID」,為什麼還要「Echo回傳」呢?的確它是非必須的。這個道理就跟HTTP會同時存在Session和Cookie兩種追蹤機制一樣。追蹤ID好比是Session,Echo回傳好比是Cookie。

以網銀支付場景,來說明下「長委託」計算模型:

圖-2:網銀支付場景

第1步:用戶在在線商店方提交訂單,開始結算,選擇支付方式。

第2步:商店方生成支付單,並數字簽名。通常商店方需要提前在支付商平台註冊賬號,並分配商戶號、應用編號、數字秘鑰。生成支付單時,需要含支付單ID、創建時間、商戶號、應用編號和這些信息的簽名,以便支付商驗證商店的身份。

第3步:支付單和簽名,需要通過前端瀏覽器發送給支付商。為了不讓參數信息在瀏覽器地址欄顯示,需採用POST Redirect方式。

第4步:瀏覽器執行JS,以實現POST Redirect。

第5步:瀏覽器透傳商店方生成的支付單。值得提醒的是簽名必須由商店後台簽,以體現商店方的委託意向,瀏覽器只是委託經辦方,並不是委託方。

第6步:支付商依據商戶號和應用編號獲取商戶應用的數字秘鑰,並依預先約定的演算法來驗證簽名,防止請求偽造。同時依據支付單ID,來防止重複扣款。

第7和8步:支付商扣用戶的錢,需要徵求用戶的同意。用戶通過輸入賬號和密碼錶示同意。

第9步:如果用戶密碼正確,系統給用戶扣款。支付單本質上是一筆商店方、支付商和用戶的三方合同,體現了三方的意志。

第10和11步:支付商通過前台瀏覽器方式通知商店方發貨。這個前台通知報文上並不是HTTP請求,

竟然是一個HTTP響應。並通過Redirect方式將請求轉到了商店方後台。

第12步:為了防止瀏覽器Crash等造成前台通知失敗的情況,還需後台方式發送發貨通知。這個是支付商伺服器對商店方伺服器後端對後端的通知。而且必須保證通知可靠到達,必須明確收到商店方後台反饋的ACK或Rollback,否則必須反覆通知。

第13步:商店方收到發貨通知後,表示用戶支付成功,必須理解更新訂單狀態為支付成功。

第14步:商店方後台對後台發貨通知,明確回復ACK或Rollback。為什麼需要提供Rollback機制呢?比如支付商扣完錢後,因為系統臨時故障等原因,導致沒通知發貨。這時商店方存在個「盲區」:當訂單處於「支付中」的狀態時,商店方無法精確判斷到底是因為用戶中途放棄了支付,還是因為支付了但沒收到發貨通知。用戶等著急了,很可能取消訂單。恰好過一會兒系統故障恢復了,支付商給補發了發貨通知,此時商店方應該反饋Rollback,以讓支付商把用戶的錢加回去。

第15步: 用戶在頁面上見到支付成功。

在上述支付流程中,「委託」行為體現在商店方委託支付商向用戶代收費。這個委託通常不可能在毫秒級完成,是個長委託,因為它需要用戶輸入賬號和密碼,需要用戶交互。整個流程設計也體現了前面提到的幾個原則。其中「委託方超時重傳」一般不會通過定時器觸發,而是用戶觸發,對於尚未支付完成的訂單,用戶可以隨時重新觸發支付。「受託方冪等防重」體現在第6步。「共享任務追蹤ID」在第2步的支付單號,這個支付單號是雙方對賬的憑證。


上述的「長委託」比較簡單,因為它只有一個階段,只涉及到一個行為,就是支付:要麼成功,要麼失敗。有時候,我們的任務是分階段的,第一步、第二步、直到第N步。如下圖所示:

圖-3:中控式多階段長委託

上圖描述了一個「長委託」Job,需要S1、S2和S3三步驟完成,每一步叫一個Task。圖例的工作流程是:

第1步:用戶提交作業(長委託)到KeeperNode節點。KeeperNode會協同下游的不同的WorkerNode節點,以完成不同的Task。

第2步:KeeperNode將Task S1委託給了WorkerNode#S1。

第3步:WorkerNode#S1收到Task後,回復ACK表示收到。

第4步:WorkerNode#S1執行具體的Task作業,通常比較耗時。

第5步:WorkerNode#S1完成Task作業後,回調通知KeeperNode。

第6步:KeeperNode收到回調通知後,更新Job的進度條狀態。

第7步:KeeperNode更新完進度條後,立即觸發下一階段的作業Task S2。

後續步:Task S2和S3的處理過程,跟TaskS1是類似的。

上述S1、S2和S3的作業流轉的決策權是放在中心控制器KeeperNode上的。這讓我們想起了職責鏈設計模式,比如Servlet的Filter機制,它通過FilterChain來組織各個環節。但不同的是,作業流轉的決策權是分散在各個Filter節點的。它的工作方式更像下圖:

圖-4:分散式多階段長委託

可以看出分散式與中控式最大的區別開始體現在第7步,分散式的作業流轉是分散在各個WorkerNode上決策的,而集中式的作業流轉是集中在KeeperNode上的。

對比多階段與單階段長委託,沒有什麼本質的區別,只是狀態追蹤上多了幾個中間狀態,非常類似大家安裝軟體時見到的「進度條」。當然因為增加了中間環節,異常的概率會增加,但依然可以採取端到端對賬的方式(Job呆在中間環節的時間總是短暫的)來督辦和催辦,每個環節都支持冪等即可。


我們已視頻點播系統為例,說明多階段長委託的應用場景:

圖-5:視頻點播場景

視頻點播系統的主要功能是:用戶或網站編輯上傳視頻,然後系統開始讀取meta信息、提取縮略圖、轉碼成MP4和內容審查等。所有環節都成功後,才能在網站上播放視頻。這個長委託不再只是一個狀態,而是有很多個階段。

整個系統設計主要有3大功能:(1)視頻上傳:上傳支持大文件,要求急速和斷點續傳。(2)視頻轉碼:用戶上傳後,要求最短的時間內通過各個環節,快速發布播放。轉碼操作非常消耗資源,要求做到彈性伸縮。(3)分散式一致性:整個任務分多個階段,無論哪個階段出異常,最後都需要端到端的一致性,任務要麼失敗,要麼成功。

由於本文關注點是「多階段長委託」話題,這裡只介紹上圖的第7步到底14步:

第7步:UTCC將videoID與視頻播放地址URL的關係存儲在metadata DB 中。

第8步:UTCC緊接著給消息系統發送轉碼事件通知。UTCC扮演了架構模型中的KeeperNode的角色,消息系統扮演了KeeperNode與WorkerNode之間的RPC通信。之所以這裡用消息系統,是為了消費端能彈性伸縮容量。關於消息系統採用kafka,還是採用RabbitMQ的優劣對比,不在本文範圍內。

第9步:保存完meta信息,並觸髮長委託任務後,就可以告知用戶視頻上傳成功了。

第10步:以Pull的形式拉取消息,沒用Push的方式,完全是為了自適應機器在轉碼等操作時的差異。

第11步:從S3下載原始視頻文件,以便提取meta信息和轉碼等。

第12步:提取meta、縮略圖和轉碼。三個階段是分開做的,階段之間的流轉是在消息消費的節點上,也就是分散式的。當然也可以採用集中式的,這樣可以在中控節點上隨時調整作業的執行順序,而且WorkerNode節點不會牽扯修改任何代碼。

第13步:去除消息系統中的消息。表示消息處理完畢。

第14步:回傳任務的處理狀態。注意提取meta、縮略圖和轉碼各個環節都要分別回傳任務進度給UTCC控制器。

最後需要提及右上角的「Consistency Timer」,它就是為了防止任何一個階段的消息丟失,導致任務處於中間狀態無法推進用的。

小結下「多階段長委託」在這個視頻點播場景實現,最大的特點就是藉助了消息系統來各階段的流轉,並藉助Timer來督辦和催辦。嚴格來說,這個多階段長委託是分散式的。


最後再看一個在2B電商(企業採購)的場景。京東的2B電商說的是向企業等提供高效和陽光的一站式採購解決方案。企業客戶內部多部門可以提內部請購PO訂單,並以批次生成2B訂單,為了減少企業收貨部門的收貨次數,我們需要給客戶提供一個跨部門、跨一定時間間隔的集中收貨服務。提供這個服務的系統叫FPK,它協同了多個其他子系統:風控系統攔截風險訂單、履約日曆管理客戶收貨地址和時間、拆分系統負責按倉庫和品類等拆分成子訂單、子訂單暫存後積累成履約單、履約單按倉庫分拆成履約分片、依據配送調度和TCS時效計算在DTS的提醒下下傳到倉庫、不同倉庫生產履約分片並最終在分配的FPN節點裝配完成送達客戶。協同過程如下圖所示:

圖-6:2B電商場景

上述過程也是多階段長委託,跟視頻點播的設計不同的是,它採用了集中式管控,Worker的編排由FPK系統完成。這樣設計的好處是什麼? 它更符合職責分離的原則,它把Worker內的功能邏輯與Worker間的編排邏輯分離。另外從組織管理的角度看,這種職責分離會迫使每個Worker一開始研發時就對外描述API,被全部門共享(因為都要匯聚到FPK那裡),而分散模式的API只會在上下游分享,當組織結構大了的時候,會發現組織內很難有個人或小組能對全流程很清楚的,導致編排流程優化和再造時,溝通成本巨高,阻礙企業的高速發展。最後我們還可以在FPK上拓展編排以外的工作,比如負載均衡、灰度發布、降級限流、集中鑒權等。


本文主要闡述了「多階段長委託」的架構模式。按循序漸進的方式依次介紹了單階段長委託、分散式多階段長委託和集中式多階段長委託計算模型,並分別用互聯網的網銀支付場景、視頻點播場景和2B電商場景舉例說明。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 京東企業購 的精彩文章:

TAG:京東企業購 |