最近正好公司裡有個需求,一個簡訊業務接了多個第三方供應商,某些業務需要查詢第三方供應商剩餘的簡訊包數量去選擇剩餘量最多的渠道去批次傳送。有些業務是指定了某個簡訊供應商,有些場景需要根據業務的值去動態判斷該用哪個供應商。場景非常複雜,還經常變化。
以前的程式碼實在慘不忍睹,選擇剩餘量最多的渠道是一個個去查的,然後獲得結果去比較。至於指定的供應商和根據業務的值去判斷選擇供應商則是用大量的if else去巢狀各種判斷。每次看到這坨程式碼真的覺得太粗糙了。關鍵是有些供應商還經常變,新接的供應商需要替代舊的供應商,加入這一大坨程式碼裡。業務的判斷條件還時不時變化一下。
出了幾次問題之後,領導看不下去了,叫我想辦法去優化。
我理了理邏輯,整個關係圖應該是這樣的,其中我把一個個去查的變成了並行去查,為了節約序列去查的IO耗時問題。
其中有些複雜查庫邏輯,判斷冪等性的步驟我就去掉了。只挑選了關鍵的步驟畫上去。
叫我去重構這個慢慢寫也能寫出,但是關鍵的是,每個步驟和判斷邏輯還時不時變化下。這就要求我的程式碼非常靈活。所以在設計時,一直很苦惱該如何去設計。
在小組開交流會的時候,有其他組的小夥伴和我安利了一個開源框架-LiteFlow。
經過研究這款開源框架,發現LiteFlow是一個國產規則引擎,能夠編排任意複雜的流程,還支援熱重新整理。這基本上完全契合我的需求啊!
檔案很詳細,很NICE,大概看了半天就全部學完了。發現新版本的LiteFlow的規則是用EL表示式來寫的。語法總共數下來也就10個左右,非常好理解。
比如這種圖:
在LiteFlow用規則表示就是:
<chain name="chain1">
THEN(
a,
WHEN(b, THEN(c, d)),
e
)
</chain>
其中THEN是代表序列,WHEN代表並行執行,這種語法,一看就很好理解。
再來看這個圖:
在LiteFlow裡規則表示就是:
<chain name="chain1">
THEN(
a,
WHEN(
b,
SWITCH(c).to(d,e)
),
f
)
</chain>
其中SWITCH關鍵字就是排他閘道器的意思,c這個元件是一個java類,根據執行的結果去選擇到底應該執行d還是e。
所以這樣巢狀多層也應該是毫無問題的。
LiteFlow的檔案裡作者給出了很詳細的例子,還有一些複雜例子,比如:
這種複雜的例子用LiteFlow的表示式可以寫成:
<chain name="chain1">
THEN(
A,
WHEN(
THEN(B, C),
THEN(D, E, F),
THEN(
SWITCH(G).to(
THEN(H, I, WHEN(J, K)).id("t1"),
THEN(L, M).id("t2")
),
N
)
),
Z
)
</chain>
它的表示式還可以進行定義子變數,上述表示式又可以寫成:
<chain name="chain1">
item1 = THEN(B, C);
item2 = THEN(D, E, F);
item3_1 = THEN(H, I, WHEN(J, K)).id("t1");
item3_2 = THEN(L, M).id("t2");
item3 = THEN(SWITCH(G).to(item3_1, item3_2), N);
THEN(
A,
WHEN(item1, item2, item3),
Z
);
</chain>
其實對照圖,仔細看,會覺得這種表示式還是很清晰的。運用到我那個簡訊系統裡是綽綽有餘的。
我研究了下,花了10分鐘時間,就寫出了我那個流程的表示式規則:
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="channelSenderChain">
selectBestChannel = THEN(
WHEN(
channel1Query, channel2Query, channel3Query,
channel4Query, channel5Query, channel6Query
),
channelSelector
).id("branch1");
selectBizChannel = THEN(
biz1,
SWITCH(if_2).to(
channel3,
channel4,
SWITCH(if_3).to(channel5, channel6).id("s3")
).id("s2")
).id("branch2");
THEN(
packageData,
SWITCH(if_1).to(
channel1,
channel2,
selectBestChannel,
selectBizChannel
),
batchSender
);
</chain>
</flow>
我用了檔案中提到的子變數的方式去寫,這種寫法更加清晰。其實我總結了一個小竅門就是:再複雜的圖,都可以拆分成一個個區域性的整體,先定區域性的小變數,然後在主要的流程裡去引入這些區域性變數就可以了。反正我寫這個圖的流程就差不多10分鐘。
至於一個個小元件。我就跟著檔案裡做了一遍,把原來的大邏輯改拆成一個個的小邏輯。封裝在不同的元件裡,給上相應的Id就可以了。
最後通過LiteflowExecutor觸發下就可以了。
LiteflowResponse response = flowExecutor.execute2Resp("channelSenderChain", null, BatchMessageResultContext.class);
BatchMessageResultContext context = response.getFirstContextBean();
if (response.isSuccess()){
log.info("執行成功,最終選擇的渠道是{}", context.getFinalResultChannel());
}else{
log.error("執行失敗", response.getCause());
}
非常簡單有木有!!!
而且特別優雅!!!
我改成上面這種形式了之後,每一個小邏輯塊之間就完全解耦了。當中資料的連線完全是靠上下文
進行連線的,在研究了LiteFlow的理念之後,我發現這理念特別好。直接把原先的耦合性特別強的程式碼給拆分開來了。
現在業務有變動的話,我只需要改寫其中一個元件就可以了。而且元件是可以拿來複用的。之間的順序也是可以隨意切換的。這一切,只需要改規則檔案即可。程式碼是完全不用動的。
我仔細翻看了檔案,這框架還支援完全無縫的熱重新整理,雖然我的程式碼沒用到這特,但是看起來真的是太厲害了,改變規則的編排連重啟應用都不需要!!!不過我打死都不會用這個特性的,領導叫我改業務,我還想多報點工時,這個如果上線了,我就沒法多報工時了。。。