作者:京東物流 呂順
背景
在物流系統(tǒng)中,接單是信息流的關(guān)鍵和重要的一環(huán),每個(gè)業(yè)務(wù)場景都會(huì)對(duì)應(yīng)一種標(biāo)準(zhǔn)接單流程,例如銷售出、采購入等等。標(biāo)準(zhǔn)接單包括統(tǒng)一接口定義、統(tǒng)一數(shù)據(jù)模型、標(biāo)準(zhǔn)接單核心應(yīng)用職責(zé)劃分。而這個(gè)標(biāo)準(zhǔn)并不是在接口定義的初期就規(guī)劃好的,通常會(huì)經(jīng)歷業(yè)務(wù)不斷增長而帶來的需求迭代、業(yè)務(wù)融合、組織架構(gòu)調(diào)整或升級(jí)引起的流程優(yōu)化與拆分。這樣一些系列事件下來,可能一個(gè)接單應(yīng)用會(huì)流轉(zhuǎn)到多個(gè)部門,接單流程就會(huì)越來越豐富,可能包括多業(yè)務(wù)、多場景、個(gè)性化、各種開關(guān)、五花八門的擴(kuò)展實(shí)現(xiàn)。
?
問題
在大接入背景下,我們聚焦在一個(gè)接單應(yīng)用的一個(gè)接單方法上?;蚨嗷蛏僭诠ぷ髦卸紩?huì)遇到一下幾種問題:
?瀑布式迭代,一個(gè)方法最終三五千行,難以閱讀理解,牽一發(fā)動(dòng)全身。
?大量個(gè)性化邏輯散落在下單得每個(gè)環(huán)節(jié),梳理起來無從下手。
?方法串聯(lián)時(shí)上下文樣式各異,如果當(dāng)初沒有擴(kuò)展性,后期改動(dòng)變動(dòng)大。
?
思路
針對(duì)這些問題,可以分為兩個(gè)層面思考,戰(zhàn)略和戰(zhàn)術(shù)。
戰(zhàn)略
這里的戰(zhàn)略指的是模式,而接單場景可以利用工作臺(tái)模式,工人(組件)按順序圍著工作臺(tái)(上下文)生產(chǎn)兩件(執(zhí)行任務(wù)),資源(參數(shù))從工作臺(tái)拿取,這種模式可以做到組件解耦、穩(wěn)定、可復(fù)用。保證業(yè)務(wù)流程靈活。
戰(zhàn)術(shù)
圍繞著工作臺(tái)模式,可以提煉的一下幾個(gè)關(guān)鍵點(diǎn):
?組件定義
?上下文
?執(zhí)行規(guī)則
?
組件定義
組件通常被定義為規(guī)則執(zhí)行的最小單元,我們最常見的是普通組件,也就是調(diào)用就執(zhí)行,這是大部分系統(tǒng)目前都在使用的組件。其實(shí)在流程規(guī)則中,組件就像編譯語言,還應(yīng)該具備布爾組件、條件組件、循環(huán)組件、并行組件、異常捕獲組件等等。由于這些組件都可以包含在普通組件中,通過代碼來實(shí)現(xiàn)條件、循環(huán)等邏輯。所以在執(zhí)行流程定義時(shí),無法清晰識(shí)別這些本應(yīng)體現(xiàn)在流程中的邏輯。
?
組件定義通常還有一個(gè)值得關(guān)注的就是組件的數(shù)量,哪些邏輯可以歸類到一個(gè)組件中,哪些需要分開。這里沒有標(biāo)準(zhǔn)答案,有兩個(gè)思路僅供參考:
1.如果訂單已經(jīng)歸類不同子域,例如發(fā)貨、收貨、承運(yùn)、產(chǎn)品、貨品等,那就按照對(duì)應(yīng)子域劃分組件。這樣更容易達(dá)成語言統(tǒng)一。
2.根據(jù)流程中寫動(dòng)作來定義組件,例如寫庫、下發(fā)wms、下配等。
上下文
在組件定義的時(shí)候都會(huì)定義上下文作為執(zhí)行流程中出入?yún)⒌妮d體。上下文得定義通常需要具備幾個(gè)特點(diǎn):
?傳遞性
?共享性
?動(dòng)態(tài)性
每個(gè)組件都只關(guān)心上下文中與自己相關(guān)的內(nèi)容,可以進(jìn)行讀取和更新,然后在流程中不斷傳遞下去。并且在需求迭代過程中支持?jǐn)U展上下文。
?
執(zhí)行規(guī)則
執(zhí)行規(guī)則就是約定各種組件按照何種規(guī)則執(zhí)行。這里實(shí)現(xiàn)方式大多xml方式、Spring注入方式、顯示組裝方式成執(zhí)行鏈,然后順序執(zhí)行。這種方案弊端就是無法體現(xiàn)條件判斷、循環(huán)、并行。另一個(gè)問題就是大家深受SpringBoot思維的“毒害”:約定大于配置,而逐漸放棄xml配置方式,讓執(zhí)行鏈組裝藏在代碼中。讓執(zhí)行規(guī)則更加不容易被發(fā)現(xiàn),說白了就是執(zhí)行規(guī)則沒有與代碼進(jìn)行解耦。那么如果將執(zhí)行規(guī)則單獨(dú)抽象出來,就可以更進(jìn)一步支持多種方式存儲(chǔ),例如數(shù)據(jù)庫、redis、ducc等,這樣熱更就會(huì)成為可能。
?
答案
在不斷實(shí)踐和學(xué)習(xí)中,我發(fā)現(xiàn)了一個(gè)具備上述所有能力的開源組件LiteFlow。
利用LiteFlow,你可以將瀑布流式的代碼,轉(zhuǎn)變成以組件為核心概念的代碼結(jié)構(gòu),這種結(jié)構(gòu)的好處是可以任意編排,組件與組件之間是解耦的,組件可以用腳本來定義,組件之間的流轉(zhuǎn)全靠規(guī)則來驅(qū)動(dòng)。LiteFlow擁有開源規(guī)則引擎最為簡單的DSL語法。十分鐘就可上手。
?
?
?
?
例子
要實(shí)現(xiàn)下面的流程:
?
?
流程規(guī)則:
?xml version="1.0" encoding="UTF-8"??>
THEN(
SWITCH(businessSwitch).TO(
// 中小件子流程
THEN(smallChain).id("small"),
// 冷鏈子流程
THEN(coldChain).id("cold")
),
// 迭代執(zhí)行
ITERATOR(goodsIterator).DO(goodsItem),
// 選擇器+默認(rèn)
SWITCH(kaSwitch).TO(dajiang, lining, nike).DEFAULT(defaultKa)
);
/chain?>
// 并行
WHEN(commonDept, smallWarehouse);
/chain?>
// 并行
WHEN(commonDept, coldWarehouse);
/chain?>
/flow?>
代碼結(jié)構(gòu):
.
├── LiteFlowDemoApplication.java
└── demos
└── web
├── BasicController.java
├── context
│ └── OrderContext.java
├── dto
│ ├── Dept.java
│ ├── Goods.java
│ ├── Request.java
│ └── WareHouse.java
├── enums
│ ├── BusinessEnum.java
│ └── KaEnum.java
└── node
├── BusinessSwitchCmp.java
├── ColdWarehouseCmp.java
├── CommonDeptCmp.java
├── GoodsItemCmp.java
├── GoodsIteratorCmp.java
├── KaSwitchCmp.java
├── SmallWarehouseCmp.java
└── ka
├── DaJiangCmp.java
├── DefaultCmp.java
├── LiNingCmp.java
└── NikeCmp.java
8 directories, 21 files
業(yè)務(wù)類型判斷:
@LiteflowComponent("businessSwitch")
public class BusinessSwitchCmp extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
Request request = this.getRequestData();
if(Objects.equals(request.getDept().getDeptNo(), "dept1")) {
return BusinessEnum.SMALL.getBusiness();
} else {
return BusinessEnum.COLD.getBusiness();
}
}
}
迭代器組件:
@LiteflowComponent("goodsIterator")
public class GoodsIteratorCmp extends NodeIteratorComponent {
@Override
public Iterator processIterator() throws Exception {
Request requestData = this.getRequestData();
return requestData.getGoodList().iterator();
}
}
循環(huán)執(zhí)行:
@Slf4j
@LiteflowComponent("goodsItem")
public class GoodsItemCmp extends NodeComponent {
@Override
public void process() throws Exception {
log.info("goods item index = {}", this.getLoopIndex());
//獲取當(dāng)前循環(huán)對(duì)象
Goods goods = this.getCurrLoopObj();
//賦值為當(dāng)前循環(huán)索引
goods.setGoodsId(this.getLoopIndex());
OrderContext orderContext = this.getContextBean(OrderContext.class);
List goodsList = orderContext.getData("goods");
if(goodsList == null) {
goodsList = new ArrayList?>();
this.getContextBean(OrderContext.class).setData("goods", goodsList);
}
goodsList.add(goods);
}
}
測試用例
public String testConfig() {
Request request = new Request();
Dept dept = new Dept();
dept.setDeptNo("nike");
request.setDept(dept);
WareHouse wareHouse = new WareHouse();
request.setWareHouse(wareHouse);
Goods goods1 = new Goods();
goods1.setGoodsName("goods1");
Goods goods2 = new Goods();
goods2.setGoodsName("goods2");
request.setGoodList(Arrays.asList(goods1, goods2));
//參數(shù)1為流程標(biāo)識(shí),參數(shù)2為初始入?yún)?,參?shù)3為上下文類型約定
LiteflowResponse liteflowResponse = flowExecutor.execute2Resp("chain1",request, OrderContext.class);
//結(jié)果中獲取上下文
OrderContext contextBean = liteflowResponse.getContextBean(OrderContext.class);
List goodsList = contextBean.getData("goods");
WareHouse warehouse = contextBean.getData("warehouse");
Dept dept1 = contextBean.getData("dept");
log.info("=== dept = {}", JsonUtil.toJsonString(dept1));
log.info("=== warehouse = {}", JsonUtil.toJsonString(warehouse));
log.info("=== goodsList = {}", JsonUtil.toJsonString(goodsList));
return "yes";
}
特點(diǎn)
個(gè)人覺得LiteFlow的特點(diǎn)包括一下幾點(diǎn):
?組件定義統(tǒng)一: 所有的邏輯都是組件,為所有的邏輯提供統(tǒng)一化的組件實(shí)現(xiàn)方式
?規(guī)則持久化: 框架原生支持把規(guī)則存儲(chǔ)在標(biāo)準(zhǔn)結(jié)構(gòu)化數(shù)據(jù)庫,Nacos,Etcd,Zookeeper,Apollo,Redis、自定義擴(kuò)展。
?上下文隔離機(jī)制: 可靠的上下文隔離機(jī)制,無需擔(dān)心高并發(fā)情況下的數(shù)據(jù)串流
?支持廣泛: Springboot,Spring還是任何其他java框架都支持。
?規(guī)則輕量: 基于規(guī)則文件來編排流程,學(xué)習(xí)規(guī)則門檻低
?
總結(jié)
LiteFlow是強(qiáng)大的流程規(guī)則框架,之所以沒有直接把LiteFlow放在標(biāo)題中,是跟大家一起透過問題看本質(zhì),最終找到合適的解決方案,而LiteFlow通過設(shè)計(jì)和抽象能力解決問題,更加值得借鑒和學(xué)習(xí)。
?審核編輯 黃宇
-
組件
+關(guān)注
關(guān)注
1文章
504瀏覽量
17787 -
物流系統(tǒng)
+關(guān)注
關(guān)注
0文章
25瀏覽量
10687
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論