B 站流程引擎設計與實踐

本期作者

潘俊傑

嗶哩嗶哩資深開發工程師

劉昊

嗶哩嗶哩基礎架構部

SRE 體系負責人

背景

隨着企業的發展,軟件開發的技術、架構以及理念也在快速更新迭代,如何搭建一套兼容當前 IT 架構且能適應未來變化的通用業務流程引擎是提升企業運營效率的關鍵。

Comet 流程引擎自 2019 年啓動研發併發布第一個版本,至今已陸續發佈多個正式版本。該系統以其足夠的靈活性、通用性和易用性,支撐了 B 站業務中大量需要藉助流程引擎來實現邏輯流轉和自動化的場景,跨度從底層基礎設施、基礎架構到上層各類業務,如各類資源申請、應用發佈、推送審覈、電子簽約、版權評級等等。

在此背景下,本文以 Comet 爲例,圍繞流程引擎的作用、使用場景、技術實現和業務價值進行詳細介紹。

概述

本文在企業級信息系統架構下設計了一套通用流程平臺技術,通過先進的工作流(Workflow)技術,按照集中化、標準化、集成化的原則,建設了一個面向日常管理與事務處理的通用流程平臺。一方面,從企業級信息系統架構的角度,建立一個與應用無關的通用流程模版,利用這種可配置的組件化模型來整合或改造現有應用的審批集成模式。通過集中或分佈的方式建立一套或多套系統平臺,降低流程系統的複雜度,使流程應用在能夠快速開發部署的同時,簡化管理維護工作、降低後期技術支持成本,另一方面可以實現各類管理與業務流程的統一管理和集中監控。

作爲 B 站面向運維及業務的統一流程引擎,Comet 支持移動端 H5 及 Web 訪問,具備加簽、轉籤、並籤、或籤等多種審批能力。通過開放式的表單和插件最大程度地提供了頁面渲染和外部系統集成的靈活性並利用圖算法來檢測配置流程的正確性,幫助用戶減少錯誤配置並提升接入效率。

場景實踐

項目的建設模式以致力於爲用戶提供更低擁有成本、更高響應效率的完整解決方案。

Comet 流程引擎是以業務爲中心,以流程爲導向,它既具備傳統 BPM 產品所必須的流程服務能力,可以滿足各種規模應用系統的流程業務需求;同時具備流程服務化所需要的動態伸縮、多租戶等特性,適應於大型集團公司的集中化、標準化的流程服務平臺建設。Comet 流程引擎強調流程、人員和技術三大要素的有機結合,根據企業的具體情況制定人員的崗位職責、設計日常流程。以建立完備、關聯的業務資源配置管理數據庫爲基礎和切入點,實施事件管理、問題管理、變更管理、配置管理和發佈管理等核心流程,實現配置管理數據庫相關數據項與核心流程的關聯和融合;使用規範化的流程管理辦法將每一項規章制度在日常工作中進行模式化和固定化。

下面筆者通過幾個具體的流程設計來窺探利用以上基礎的能力實現的具體業務場景化需求的多樣性和複雜度。

SRE 自動化類 - 緩存資源變更申請

在運維自動化的場景下,通過流程引擎的可視化流程模板、動態表單和插件執行功能,能夠以很低的成本將週期性、重複性、規律性的日常工作流轉化爲自助化、自動化工單,例如應用系統維護的自動化、巡檢過程自動化和故障處理自動化等等。基於在運維場景應用流程能力,極大的簡化了運維、SRE 同學將日常中瑣事轉化爲業務自助化的成本,降低了人員瑣事負載。

在具體的落地過程中,由於自動化運維依賴於各場景管控管理平臺提供的管控能力來提升運維效率。像在緩存相關業務場景下,我們藉助流程引擎實現相關資源大小的合理性評估以及緩存資源的自動化創建。該流程往往需要申請人填寫對應的緩存資源的信息,如:內存大小、歸屬集羣、申請應用。同時,根據變更的行爲來控制流程的流轉,保證了變更幅度比較大時的安全性。在與緩存管控平臺的能力交互中,該業務流基於流程插件的回調功能來驅動緩存管控平臺在適時創建緩存資源。
該業務流的具體流程圖如下:

圖片

權限申請類 - 數據平臺表權限申請

在數據平臺的某些表權限申請的場景下,需要根據流程發起時的上下文信息作爲入參來判斷該流程中的節點是否激活,這裏涉及了大量的環節判斷和插件執行。另外在審批過程中,人員需要及時感知節點任務。基於 Comet 流程引擎的環節通知能力,減少了環節之間的審批時延,提高了流程審批的及時性。

在該場景下,一些低安全等級的數據往往可以通過上下文的方式減少審批鏈路,縮減流程整體的審批時長、提高審批效率。人員方面,Comet 集成了 OA 和內部權限系統的角色,在環節配置了對應的角色後系統會根據發起人的不同去獲取相應角色下的審批人員,減少了用戶手動配置和維護的成本。
由於該場景需要使用到並籤的功能,即對某些環節實現根據傳遞的人員組數不同來拆分多個節點,Comet 將該環節的節點及上下游流轉關係進行復制並增加自動節點作爲收束節點(收束節點只有在上游所有審批節點都通過後纔會激活)。這個功能補充了 Comet 在節點橫向擴展的靈活性,滿足了用戶對多組審批人審批的場景需求。用戶發起申請後,根據其所申請的表安全級別及部門信息來走不同的審批鏈。

基於流程條件的靈活性,業務方甚至可以將同一類流程抽象到一個流程來實現,減少了流程模版配置的成本並增加了流程審批場景的多樣化。
該流程具體的流程圖如下所示:

圖片

業務審批類 - 推送任務審批

在執行推送任務的場景下,業務方通過其平臺預先計算出某個流程的審批鏈並傳遞給流程引擎。在這個場景下,Comet 通過動態線性流程的技術方案進行支持覆蓋。該功能是在以流程模版配置功能之外的增強擴展,業務無需配置流程模版和流轉關係,只需要按照順序排列對應的審批人即可動態生成節點數量。

在實際實踐中,業務方通過 Push 平臺創建推送任務,Push 平臺開啓審覈工單。根據此次推送的上下文信息,實時計算出所需要的審批環節後,交由該權限組對應的審覈組進行審覈並記錄流程狀態變更消息,在審覈流程完成後落入數據庫。
動態線性流程減少了用戶配置流程模版的成本、提高了發起流程的靈活度,滿足了用戶動態伸縮流程審批鏈的要求。同時,動態流程可以通過環節指定插件的方式將環節和流程的信息通知給平臺方,平臺方根據具體的環節和上下文做對應的處理。
除上述優勢之外,動態線性流程在部分業務場景是有一定侷限性。

由於動態線性流程在發起時,無法固定參數範圍和數量,這會導致流程維護管理成本增高,後續流程實例的排障成本高

在複雜流程的審批場景下,動態配置流程需要業務自行傳遞大量流程圖的相關參數,這塊會導致業務理解成本增加,從而導致出錯概率提升。因而,複雜的業務流程,仍然需要使用流程模版的方式配置。

下圖是一個動態線性流程的例子:

圖片

設計實現

工作流程

爲了方便讀者更好的理解 Comet 是如何工作的,筆者將簡化後的系統流程程圖繪製在下方。它主要由以下幾個部分組成:

圖片

整體架構

圖片

如上圖所示,整體架構主要分爲三個部分,它們分別是:

關鍵設計

流程模版

對於業務來說,流程模版即對其業務流程的具像化建模。通過配置和拖拽即可定義其流程節點的流向及流程元素的展示。流程模版和流程的關係比較類似程序設計語言中的類和對象的關係,如下圖所示:

圖片

一個流程模版可以實例化多個流程,並且流程模版維護了自身的版本,每一個流程都會關聯到具體的模版版本。流程模版支持節點編輯、節點抄送、流程抄送、延時通知、定時通知等多種功能,其可視化編輯頁面如下圖所示:

圖片

節點的邏輯屬性

對於一個流程圖來說,除了其開始節點和結束節點以外,其他節點的入度和出度都大於或等於 1。我們可以先不考慮節點向下遊流轉的條件並默認爲自動流轉。在這種情形下,對於入度大於 1 的節點往往會持有兩種邏輯屬性,即且(and)和或(or)。下圖說明了這兩種條件的流轉形式:

圖片

節點的激活不僅需要通過其持有的邏輯屬性控制,往往還需要解析其邊所持有的流轉條件。

流轉條件

將流轉條件作爲邊的屬性引入可以豐富流程的適配場景。它的使用需要依賴流程發起時以及過程中傳遞的上下文參數。譬如在一個出度爲 n 的節點,我們希望只有當該節點的符合某個條件時纔會激活某一分支的下游節點,如下圖所示:

圖片

通過上下文參數 n,我們可以控制被激活的節點。具體到某些實際場景來說,比如當申請某個權限時,通過判斷上下文傳遞的數據安全級別可以控制到不同的節點,從而控制其審批流的差異度。

在 Comet 流程引擎中,我們採用的是一種 MongoDB 的條件表達式來做爲流轉條件的表達式。舉個例子,當我們希望滿足 m>1 或 n>2 任意一個條件就激活下游節點時,表達式可以是如下所示:

圖片

這裏表達式由於是一個標準的 Json 格式,無需自實現或引入語法解析器。流程引擎只需遞歸解析流程上下文和該表達式所計算出的布爾值即可判斷下級節點是否激活。

表單模版

Comet 流程引擎除了支持兩種模版類型:組合模版和 HTML 模版。

組合模版

通過在 web 頁面提供表單畫布和標準表單組件,用戶可以通過拖拽的形式將標準表單組件放入到表單模版內。在單個表單的配置中,可以指定表單參數與流程上下文中的參數做綁定實現自動賦值和展示。

目前,我們支持了 16 個通用的表單組件,3 個高級表單組件(與周邊平臺定製的通用組件),基本滿足了日常業務流程設計中的所需要的表單能力。

圖片

HTML 模版

在低代碼模板之外,我們增加了 HTML 模板。該模板通過 iframe 嵌入的方式,將用戶定義的前端代碼在節點環節展示。業務可以通過自定義 HTML、JS 和 CSS 代碼,並自行 mock 相應的上下文參數來進行完全靈活和高擴展的業務表單展示設計。

該功能的設計作爲低代碼模板功能的補充,用來覆蓋特殊業務場景下的表單展示功能,主要是滿足部分業務的複雜展示和表單移動端展示需求。

展示形式如下:

圖片

可視化流程

對於一個既定的流程來說,我們可以使用 mermaid 來實現它的可視化。具體來說,我們只需要將流程中所有的節點編號和流轉條件以字符串的形式提供,並在前端使用該庫,即自動產生流程圖。舉個例子:

圖片

將會產生如下流程圖:

圖片

引擎設計

除了這些概念之外,流程引擎還需要提供一組基礎的動作用於控制流程的行爲,譬如:流轉、拒絕和關閉等。

節點流轉

當節點被審批通過後,下游節點會根據與該節點間邊的流轉條件是否符合而被激活。在沒有自動節點時,流轉是一次性行爲。我們通過遍歷它和下游節點邊的流轉條件,依次判斷條件表達式選擇要推動的分支。但當引入自動流轉的節點後,我們就需要通過遞歸來處理這一動作。僞代碼如下:

圖片

節點拒絕

當然,節點動作還包含了拒絕操作。當節點被拒絕時,如果流程此時不存在一個節點能使其完結,置流程狀態爲拒絕。僞代碼如下:

圖片

這裏的關鍵是如何判斷流程是否可以流轉。一個簡單的思路是:由於上游流轉條件的邏輯屬性存在於下游節點中,我們可以通過自底向上的方法來判斷終止節點是否存在一條指向等待審批狀態的節點的通路這一命題是否成立來確定流程的狀態。Go 版本代碼如下:

圖片

下面的左圖和右圖分別爲活躍態和拒絕態的流程:

圖片

由於 C 節點被拒絕,此時右圖中已不存在可以使 E 節點到達 B 節點的通路了(D 的激活條件時 B、C 均被流轉)。

異步執行設計

插件

流程執行過程中往往需要和外部系統做交互已達到自動化執行某些操作。對於 Comet 流程引擎來說,它支持兩種類型的插件形式:二進制文件和 Python 腳本。由於 Python 腳本相比於二進制文件可維護性更好,所以插件主要採用此種形式來書寫。用戶上傳了插件文件之後,Comet 會將其放置在其容器掛載的 NAS 中。流程中節點插件可以存在兩種,一種是在當前節點狀態變爲待審批之前,另一種是在當前節點執行之後。具體如下圖所示:

圖片

我們可以將節點的待審批通知放在 pre-plugin 中,將節點處理後通知業務方系統的 http 調用放在 after-plugin 中,從而達到相關事件和對應人和系統的綁定。事實上,將流程的所有動作(諸如:發起、流轉、拒絕等)作爲事件,然後配置相關事件的動作(插件執行、通知發送)會比這種方式更加靈活。

用戶上傳插件後,系統將爲其生成唯一 ID 並存入 NAS 中,執行日誌將以日期與插件兩個維度組織存入磁盤,以方便批量清理及讀取供用戶排障,展示形式如下圖所示:

圖片

前後的執行時間爲系統寫入,方便定位插件超時問題。

回調

爲了配合插件達到在節點 / 流程中耦合業務處理代碼邏輯,Comet 流程引擎還實現了回調功能。在業務方處理邏輯需要被融合到節點成功 / 失敗的狀態中時,業務方可以利用插件調用其處理接口並用處理結果回調 Comet。其時序圖如下圖所示:

圖片

通過這種方式,業務方可以非常方便的處理流程引擎產生的事件動作並靈活的定義其處理手段。

合法性檢測算法

在流程模版的創建過程中,我們必須檢測其合法性。在計算機科學領域中,圖是一種抽象的數據類型。通常它由節點組成,並且按照邊的有向性無向性分爲有向圖和無向圖。流程圖作爲有向圖的一個分支,通常從一個起始點(入度爲 0)發散到一個終止點(出度爲 0)收斂。如下圖所示:

圖片

起始點 A 和終止點 F 可以輔助組織流程,並且可以方便我們定義流程的起止狀態(即起 / 終點被激活)。

鄰接矩陣

領接表、領接矩陣關聯矩陣是圖的幾種常見的數據結構。對於鄰接矩陣(adjacency matrix)來說,它是指一種方陣,用來表示有限圖。它的每個元素代表各點之間是否有邊相連。如下圖所示:

圖片

從左到右、從上到下分別代表 A-F 六個節點。方陣中元素爲 1 代表從節點行到節點列存在一條有向通路。

對於流程來說它需要滿足只有一個入度爲 0 和一個出度爲 0 的節點,也就是說在添加了輔助節點的流程的鄰接矩陣中必然有且僅有一行和一列全爲 0。然而僅通過此方式還存在一個問題:當圖中有環時可能會導致流程一直循環,無法終止。因此我們引入另一個概念 - 有向無環圖。

有向無環圖

在圖論中,如果一個有向圖從任意頂點出發無法經過若干條邊回到該點,則這個圖是一個有向無環圖DAGDirected Acyclic Graph)。對於一個流程來說,它也必將從某一節點發散,到某一節點收斂。因此一個有向圖爲合法的流程是該有向圖爲有向無環圖的充分不必要條件。

判斷一個圖是否爲有向無環圖的算法有卡恩算法深度優先搜索(DFS)。深度優先搜索以任意順序循環遍歷圖中的每個節點。若搜索進行中碰到之前已經遇到的節點,或碰到葉節點,則中止算法。下面簡單看下 Go 語言版本的實現:

圖片

總結

工作流是業務流的一部分,而流程引擎就是驅動業務按照公司設定的固定流程去流轉,在複雜多變的業務情況下,使用既定的流程能夠提高工作效率,降低設計業務成本,保證業務執行的準確性。自 Comet 2019 年立項以來,累計多版本迭代並穩定運行至今,業務涵蓋基數據平臺、運維平臺、中間件平臺和推送平臺等多個內部平臺和業務。

本文以 Comet 流程引擎爲例,介紹了其基本的運作流程、軟件架構及代碼實現。對於 Comet 來說,靈活性是其產品設計的最大特點。通過插件、節點、流轉條件三者的組合,業務可以靈活的配置和實現絕大部分審批和自動化場景的訴求。插件提高了業務與 Comet 的交互場景、流轉條件豐富了流程審批鏈的多樣性。Comet 在複雜多變的業務情況下驅動業務按照設定的流程去流轉,使用既定流程大大降低設計業務的成本並保證了業務執行的準確性。同時,由於流程引擎是低代碼平臺的核心,它可以幫助我們去實現非常靈活的流程設計,極大的助力企業實現數據流轉的的規範化和自動化。

參考

[1] 業務流程建模和標註規範:https://www.bpmn.org

[2] 鄰接矩陣:https://zh.wikipedia.org/wiki/%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5

[3] 有向無環圖:https://zh.wikipedia.org/wiki/%E6%9C%89%E5%90%91%E6%97%A0%E7%8E%AF%E5%9B%BE

[4] About Mermaid:https://mermaid.js.org/#/README

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/kDWpJ-Zu96cWW0yGCSbXUQ