在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    管理訂單狀態(tài),該上狀態(tài)機(jī)嗎?

    管理訂單狀態(tài),該上狀態(tài)機(jī)嗎?

    前言

    在平常的后端項(xiàng)目開(kāi)發(fā)中,狀態(tài)機(jī)模式的使用其實(shí)沒(méi)有大家想象中那么常見(jiàn),筆者之前由于不在電商領(lǐng)域工作,很少在業(yè)務(wù)代碼中用狀態(tài)機(jī)來(lái)管理各種狀態(tài),一般都是手動(dòng)get/set狀態(tài)值。去年筆者進(jìn)入了電商領(lǐng)域從事后端開(kāi)發(fā)。電商領(lǐng)域,狀態(tài)又多又復(fù)雜,如果仍然在業(yè)務(wù)代碼中東一塊西一塊維護(hù)狀態(tài)值,很容易陷入出了問(wèn)題難于Debug,難于追責(zé)的窘境。

    碰巧有個(gè)新啟動(dòng)的項(xiàng)目需要進(jìn)行訂單狀態(tài)的管理,我著手將Spring StateMachine接入了進(jìn)來(lái),管理購(gòu)物訂單狀態(tài),不得不說(shuō),Spring StateMachine全家桶的文檔寫(xiě)的是不錯(cuò),并且Spring StateMachine也是有官方背書(shū)的。但是,它實(shí)在是太”重“了,想要簡(jiǎn)單修改一個(gè)訂單的狀態(tài),需要十分復(fù)雜的代碼來(lái)實(shí)現(xiàn)。具體就不在這里展開(kāi)了,不然我感覺(jué)可以吐槽一整天。

    說(shuō)到底Spring StateMachine上手難度非常大,如果沒(méi)有用來(lái)做重型狀態(tài)機(jī)的需求,十分不推薦普通的小項(xiàng)目進(jìn)行接入。

    最最重要的是,由于Spring StateMachine狀態(tài)機(jī)實(shí)例不是無(wú)狀態(tài)的,無(wú)法做到線程安全,所以代碼要么需要使用鎖同步,要么需要用Threadlocal,非常的痛苦和難用。 例如下面的Spring StateMachine代碼就用了重量級(jí)鎖保證線程安全,在高并發(fā)的互聯(lián)網(wǎng)應(yīng)用中,這種代碼留的隱患非常大。

    private synchronized boolean sendEvent(Message message, OrderEntity orderEntity) { boolean result = false; try { stateMachine.start(); // 嘗試恢復(fù)狀態(tài)機(jī)狀態(tài) persister.restore(stateMachine, orderEntity); // 執(zhí)行事件 result = stateMachine.sendEvent(message); // 持久化狀態(tài)機(jī)狀態(tài) persister.persist(stateMachine, (OrderEntity) message.getHeaders().get(“purchaseOrder”)); } catch (Exception e) { log.error(“sendEvent error”, e); } finally { stateMachine.stop(); } return result; }

    吃了一次虧后,我再一次在網(wǎng)上翻閱各種Java狀態(tài)機(jī)的實(shí)現(xiàn),有大的開(kāi)源項(xiàng)目,也有小而美的個(gè)人實(shí)現(xiàn)。結(jié)果在COLA架構(gòu)中發(fā)現(xiàn)了COLA還寫(xiě)了一套狀態(tài)機(jī)實(shí)現(xiàn)。COLA的作者給我們提供了一個(gè)無(wú)狀態(tài)的,輕量化的狀態(tài)機(jī),接入十分簡(jiǎn)單。并且由于無(wú)狀態(tài)的特點(diǎn),可以做到線程安全,支持電商的高并發(fā)場(chǎng)景。

    COLA是什么?如果你還沒(méi)聽(tīng)說(shuō)過(guò)COLA,不妨看一看我之前的文章,傳送門(mén)如下:

    https://mp.weixin.qq.com/s/07i3FjcFrZ8rxBCACgeWVQ

    如果你需要在項(xiàng)目中引入狀態(tài)機(jī),此時(shí)此刻,我會(huì)推薦使用COLA狀態(tài)機(jī)。

    COLA狀態(tài)機(jī)介紹

    COLA狀態(tài)機(jī)是在Github開(kāi)源的,作者也寫(xiě)了介紹文章:

    https://blog.csdn.net/significantfrank/article/details/104996419

    官方文章的前半部分重點(diǎn)介紹了DSL(Domain Specific Languages),這一部分比較抽象和概念化,大家感興趣,可以前往原文查看。我精簡(jiǎn)一下DSL的主要含義:

    什么是DSL? DSL是一種工具,它的核心價(jià)值在于,它提供了一種手段,可以更加清晰地就系統(tǒng)某部分的意圖進(jìn)行溝通。

    比如正則表達(dá)式,/d{3}-d{3}-d{4}/就是一個(gè)典型的DSL,解決的是字符串匹配這個(gè)特定領(lǐng)域的問(wèn)題。

    文章的后半部分重點(diǎn)闡述了作者為什么要做COLA狀態(tài)機(jī)?想必這也是讀者比較好奇的問(wèn)題。我?guī)痛蠹揖?jiǎn)一下原文的表述:

    • 首先,狀態(tài)機(jī)的實(shí)現(xiàn)應(yīng)該可以非常的輕量,最簡(jiǎn)單的狀態(tài)機(jī)用一個(gè)Enum就能實(shí)現(xiàn),基本是零成本。
    • 其次,使用狀態(tài)機(jī)的DSL來(lái)表達(dá)狀態(tài)的流轉(zhuǎn),語(yǔ)義會(huì)更加清晰,會(huì)增強(qiáng)代碼的可讀性和可維護(hù)性。
    • 開(kāi)源狀態(tài)機(jī)太復(fù)雜: 就我們的項(xiàng)目而言(其實(shí)大部分項(xiàng)目都是如此)。我實(shí)在不需要那么多狀態(tài)機(jī)的高級(jí)玩法:比如狀態(tài)的嵌套(substate),狀態(tài)的并行(parallel,fork,join)、子狀態(tài)機(jī)等等。
    • 開(kāi)源狀態(tài)機(jī)性能差: 這些開(kāi)源的狀態(tài)機(jī)都是有狀態(tài)的(Stateful)的,因?yàn)橛袪顟B(tài),狀態(tài)機(jī)的實(shí)例就不是線程安全的,而我們的應(yīng)用服務(wù)器是分布式多線程的,所以在每一次狀態(tài)機(jī)在接受請(qǐng)求的時(shí)候,都不得不重新build一個(gè)新的狀態(tài)機(jī)實(shí)例。

    所以COLA狀態(tài)機(jī)設(shè)計(jì)的目標(biāo)很明確,有兩個(gè)核心理念:

  • 簡(jiǎn)潔的僅支持狀態(tài)流轉(zhuǎn)的狀態(tài)機(jī),不需要支持嵌套、并行等高級(jí)玩法。
  • 狀態(tài)機(jī)本身需要是Stateless(無(wú)狀態(tài))的,這樣一個(gè)Singleton Instance就能服務(wù)所有的狀態(tài)流轉(zhuǎn)請(qǐng)求了。
  • COLA狀態(tài)機(jī)的核心概念如下圖所示,主要包括:

    State:狀態(tài) Event:事件,狀態(tài)由事件觸發(fā),引起變化 Transition:流轉(zhuǎn),表示從一個(gè)狀態(tài)到另一個(gè)狀態(tài) External Transition:外部流轉(zhuǎn),兩個(gè)不同狀態(tài)之間的流轉(zhuǎn) Internal Transition:內(nèi)部流轉(zhuǎn),同一個(gè)狀態(tài)之間的流轉(zhuǎn) Condition:條件,表示是否允許到達(dá)某個(gè)狀態(tài) Action:動(dòng)作,到達(dá)某個(gè)狀態(tài)之后,可以做什么 StateMachine:狀態(tài)機(jī)

    COLA狀態(tài)機(jī)原理

    這一小節(jié),我們先講幾個(gè)COLA狀態(tài)機(jī)最重要兩個(gè)部分,一個(gè)是它使用的連貫接口,一個(gè)是狀態(tài)機(jī)的注冊(cè)和使用原理。如果你暫時(shí)對(duì)它的實(shí)現(xiàn)原理不感興趣,可以直接跳過(guò)本小節(jié),直接看后面的實(shí)戰(zhàn)代碼部分。

    PS:講解的代碼版本為cola-component-statemachine 4.2.0-SNAPSHOT

    下圖展示了COLA狀態(tài)機(jī)的源代碼目錄,可以看到非常的簡(jiǎn)潔。

    1. 連貫接口 Fluent Interfaces

    COLA狀態(tài)機(jī)的定義使用了連貫接口Fluent Interfaces,連貫接口的一個(gè)重要作用是,限定方法調(diào)用的順序。比如,在構(gòu)建狀態(tài)機(jī)的時(shí)候,我們只有在調(diào)用了from方法后,才能調(diào)用to方法,Builder模式?jīng)]有這個(gè)功能。

    下圖中可以看到,我們?cè)谑褂玫臅r(shí)候是被嚴(yán)格限制的:

    StateMachineBuilder builder = StateMachineBuilderFactory.create(); builder.externalTransition() .from(States.STATE1) .to(States.STATE2) .on(Events.EVENT1) .when(checkCondition()) .perform(doAction());

    這是如何實(shí)現(xiàn)的?其實(shí)是使用了Java接口來(lái)實(shí)現(xiàn)。

    2. 狀態(tài)機(jī)注冊(cè)和觸發(fā)原理

    這里簡(jiǎn)單梳理一下?tīng)顟B(tài)機(jī)的注冊(cè)和觸發(fā)原理。

    用戶執(zhí)行如下代碼來(lái)創(chuàng)建一個(gè)狀態(tài)機(jī),指定一個(gè)MACHINE_ID:

    StateMachine stateMachine = builder.build(MACHINE_ID);

    COLA會(huì)將該狀態(tài)機(jī)在StateMachineFactory類(lèi)中,放入一個(gè)ConcurrentHashMap,以狀態(tài)機(jī)名為key注冊(cè)。

    static Map stateMachineMap = new ConcurrentHashMap();

    注冊(cè)好后,用戶便可以使用狀態(tài)機(jī),通過(guò)類(lèi)似下方的代碼觸發(fā)狀態(tài)機(jī)的狀態(tài)流轉(zhuǎn):

    stateMachine.fireEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new Context(“1”));

    內(nèi)部實(shí)現(xiàn)如下:

  • 首先判斷COLA狀態(tài)機(jī)整個(gè)組件是否初始化完成。
  • 通過(guò)routeTransition尋找是否有符合條件的狀態(tài)流轉(zhuǎn)。
  • transition.transit執(zhí)行狀態(tài)流轉(zhuǎn)。
  • transition.transit方法中:

    檢查本次流轉(zhuǎn)是否符合condition,符合,則執(zhí)行對(duì)應(yīng)的action。

    COLA狀態(tài)機(jī)實(shí)戰(zhàn)

    **PS:以下實(shí)戰(zhàn)代碼取自COLA官方倉(cāng)庫(kù)測(cè)試類(lèi)

    一、狀態(tài)流轉(zhuǎn)使用示例

  • 從單一狀態(tài)流轉(zhuǎn)到另一個(gè)狀態(tài)
  • @Testpublic void testExternalNormal(){ StateMachineBuilder builder = StateMachineBuilderFactory.create(); builder.externalTransition() .from(States.STATE1) .to(States.STATE2) .on(Events.EVENT1) .when(checkCondition()) .perform(doAction()); StateMachine stateMachine = builder.build(MACHINE_ID); States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context()); Assert.assertEquals(States.STATE2, target);}private Condition checkCondition() { return (ctx) -> {return true;};}private Action doAction() { return (from, to, event, ctx)->{ System.out.println(ctx.operator+” is operating “+ctx.entityId+” from:”+from+” to:”+to+” on:”+event); };}

    可以看到,每次進(jìn)行狀態(tài)流轉(zhuǎn)時(shí),檢查checkCondition(),當(dāng)返回true,執(zhí)行狀態(tài)流轉(zhuǎn)的操作doAction()。

    后面所有的checkCondition()和doAction()方法在下方就不再重復(fù)貼出了。

  • 從多個(gè)狀態(tài)流傳到新的狀態(tài)
  • @Testpublic void testExternalTransitionsNormal(){ StateMachineBuilder builder = StateMachineBuilderFactory.create(); builder.externalTransitions() .fromAmong(States.STATE1, States.STATE2, States.STATE3) .to(States.STATE4) .on(Events.EVENT1) .when(checkCondition()) .perform(doAction()); StateMachine stateMachine = builder.build(MACHINE_ID+”1″); States target = stateMachine.fireEvent(States.STATE2, Events.EVENT1, new Context()); Assert.assertEquals(States.STATE4, target);}

  • 狀態(tài)內(nèi)部觸發(fā)流轉(zhuǎn)
  • @Testpublic void testInternalNormal(){ StateMachineBuilder builder = StateMachineBuilderFactory.create(); builder.internalTransition() .within(States.STATE1) .on(Events.INTERNAL_EVENT) .when(checkCondition()) .perform(doAction()); StateMachine stateMachine = builder.build(MACHINE_ID+”2″); stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context()); States target = stateMachine.fireEvent(States.STATE1, Events.INTERNAL_EVENT, new Context()); Assert.assertEquals(States.STATE1, target);}

  • 多線程測(cè)試并發(fā)測(cè)試
  • @Testpublic void testMultiThread(){ buildStateMachine(“testMultiThread”); for(int i=0 ; i{ StateMachine stateMachine = StateMachineFactory.get(“testMultiThread”); States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context()); Assert.assertEquals(States.STATE2, target); }); thread.start(); } for(int i=0 ; i { StateMachine stateMachine = StateMachineFactory.get(“testMultiThread”); States target = stateMachine.fireEvent(States.STATE1, Events.EVENT4, new Context()); Assert.assertEquals(States.STATE4, target); }); thread.start(); } for(int i=0 ; i { StateMachine stateMachine = StateMachineFactory.get(“testMultiThread”); States target = stateMachine.fireEvent(States.STATE1, Events.EVENT3, new Context()); Assert.assertEquals(States.STATE3, target); }); thread.start(); }}

    由于COLA狀態(tài)機(jī)時(shí)無(wú)狀態(tài)的狀態(tài)機(jī),所以性能是很高的。相比起來(lái),SpringStateMachine由于是有狀態(tài)的,就需要使用者自行保證線程安全了。

    二、多分支狀態(tài)流轉(zhuǎn)示例

    /*** 測(cè)試選擇分支,針對(duì)同一個(gè)事件:EVENT1* if condition == “1”, STATE1 –> STATE1* if condition == “2” , STATE1 –> STATE2* if condition == “3” , STATE1 –> STATE3*/@Testpublic void testChoice(){ StateMachineBuilder builder = StateMachineBuilderFactory.create(); builder.internalTransition() .within(StateMachineTest.States.STATE1) .on(StateMachineTest.Events.EVENT1) .when(checkCondition1()) .perform(doAction()); builder.externalTransition() .from(StateMachineTest.States.STATE1) .to(StateMachineTest.States.STATE2) .on(StateMachineTest.Events.EVENT1) .when(checkCondition2()) .perform(doAction()); builder.externalTransition() .from(StateMachineTest.States.STATE1) .to(StateMachineTest.States.STATE3) .on(StateMachineTest.Events.EVENT1) .when(checkCondition3()) .perform(doAction()); StateMachine stateMachine = builder.build(“ChoiceConditionMachine”); StateMachineTest.States target1 = stateMachine.fireEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new Context(“1”)); Assert.assertEquals(StateMachineTest.States.STATE1,target1); StateMachineTest.States target2 = stateMachine.fireEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new Context(“2”)); Assert.assertEquals(StateMachineTest.States.STATE2,target2); StateMachineTest.States target3 = stateMachine.fireEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new Context(“3”)); Assert.assertEquals(StateMachineTest.States.STATE3,target3); }

    可以看到,編寫(xiě)一個(gè)多分支的狀態(tài)機(jī)也是非常簡(jiǎn)單明了的。

    三、通過(guò)狀態(tài)機(jī)反向生成PlantUml圖

    沒(méi)想到吧,還能通過(guò)代碼定義好的狀態(tài)機(jī)反向生成plantUML圖,實(shí)現(xiàn)狀態(tài)機(jī)的可視化。(可以用圖說(shuō)話,和產(chǎn)品對(duì)比下?tīng)顟B(tài)實(shí)現(xiàn)的是否正確了。)

    四、特殊使用示例

  • 不滿足狀態(tài)流轉(zhuǎn)條件時(shí)的處理
  • @Testpublic void testConditionNotMeet(){ StateMachineBuilder builder = StateMachineBuilderFactory.create(); builder.externalTransition() .from(StateMachineTest.States.STATE1) .to(StateMachineTest.States.STATE2) .on(StateMachineTest.Events.EVENT1) .when(checkConditionFalse()) .perform(doAction()); StateMachine stateMachine = builder.build(“NotMeetConditionMachine”); StateMachineTest.States target = stateMachine.fireEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new StateMachineTest.Context()); Assert.assertEquals(StateMachineTest.States.STATE1,target);}

    可以看到,當(dāng)checkConditionFalse()執(zhí)行時(shí),永遠(yuǎn)不會(huì)滿足狀態(tài)流轉(zhuǎn)的條件,則狀態(tài)不會(huì)變化,會(huì)直接返回原來(lái)的STATE1。相關(guān)源碼在這里:

  • 重復(fù)定義相同的狀態(tài)流轉(zhuǎn)
  • @Test(expected = StateMachineException.class)public void testDuplicatedTransition(){ StateMachineBuilder builder = StateMachineBuilderFactory.create(); builder.externalTransition() .from(StateMachineTest.States.STATE1) .to(StateMachineTest.States.STATE2) .on(StateMachineTest.Events.EVENT1) .when(checkCondition()) .perform(doAction()); builder.externalTransition() .from(StateMachineTest.States.STATE1) .to(StateMachineTest.States.STATE2) .on(StateMachineTest.Events.EVENT1) .when(checkCondition()) .perform(doAction());}

    會(huì)在第二次builder執(zhí)行到on(StateMachineTest.Events.EVENT1)函數(shù)時(shí),拋出StateMachineException異常。拋出異常在on()的verify檢查這里,如下:

  • 重復(fù)定義狀態(tài)機(jī)
  • @Test(expected = StateMachineException.class)public void testDuplicateMachine(){ StateMachineBuilder builder = StateMachineBuilderFactory.create(); builder.externalTransition() .from(StateMachineTest.States.STATE1) .to(StateMachineTest.States.STATE2) .on(StateMachineTest.Events.EVENT1) .when(checkCondition()) .perform(doAction()); builder.build(“DuplicatedMachine”); builder.build(“DuplicatedMachine”);}

    會(huì)在第二次build同名狀態(tài)機(jī)時(shí)拋出StateMachineException異常。拋出異常的源碼在狀態(tài)機(jī)的注冊(cè)函數(shù)中,如下:

    結(jié)語(yǔ)

    為了不把篇幅拉得過(guò)長(zhǎng),在這里無(wú)法詳細(xì)地橫向?qū)Ρ葞状笾髁鳡顟B(tài)機(jī)(Spring Statemachine,Squirrel statemachine等)和COLA的區(qū)別,不過(guò)基于筆者在Spring Statemachine踩過(guò)的深坑,目前來(lái)看,COLA狀態(tài)機(jī)的簡(jiǎn)潔設(shè)計(jì)適合用在訂單管理等小型狀態(tài)機(jī)的維護(hù),如果你想要在你的項(xiàng)目中接入狀態(tài)機(jī),又不需要嵌套、并行等高級(jí)玩法,那么COLA是個(gè)十分合適的選擇。

    我是后端工程師,蠻三刀醬。

    持續(xù)的更新原創(chuàng)優(yōu)質(zhì)文章,離不開(kāi)你的點(diǎn)贊,轉(zhuǎn)發(fā)和分享!

    我的唯一技術(shù)公眾號(hào):后端技術(shù)漫談

    – END –

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
    上一篇 2022年7月1日 02:44
    下一篇 2022年7月1日 02:45

    相關(guān)推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息