亚洲精品中文免费|亚洲日韩中文字幕制服|久久精品亚洲免费|一本之道久久免费

      
      

            <dl id="hur0q"><div id="hur0q"></div></dl>

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

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

                前言

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

                碰巧有個新啟動的項目需要進行訂單狀態(tài)的管理,我著手將Spring StateMachine接入了進來,管理購物訂單狀態(tài),不得不說,Spring StateMachine全家桶的文檔寫的是不錯,并且Spring StateMachine也是有官方背書的。但是,它實在是太”重“了,想要簡單修改一個訂單的狀態(tài),需要十分復雜的代碼來實現(xiàn)。具體就不在這里展開了,不然我感覺可以吐槽一整天。

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

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

                private synchronized boolean sendEvent(Message message, OrderEntity orderEntity) { boolean result = false; try { stateMachine.start(); // 嘗試恢復狀態(tài)機狀態(tài) persister.restore(stateMachine, orderEntity); // 執(zhí)行事件 result = stateMachine.sendEvent(message); // 持久化狀態(tài)機狀態(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)機的實現(xiàn),有大的開源項目,也有小而美的個人實現(xiàn)。結果在COLA架構中發(fā)現(xiàn)了COLA還寫了一套狀態(tài)機實現(xiàn)。COLA的作者給我們提供了一個無狀態(tài)的,輕量化的狀態(tài)機,接入十分簡單。并且由于無狀態(tài)的特點,可以做到線程安全,支持電商的高并發(fā)場景。

                COLA是什么?如果你還沒聽說過COLA,不妨看一看我之前的文章,傳送門如下:

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

                如果你需要在項目中引入狀態(tài)機,此時此刻,我會推薦使用COLA狀態(tài)機。

                COLA狀態(tài)機介紹

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

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

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

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

                比如正則表達式,/d{3}-d{3}-d{4}/就是一個典型的DSL,解決的是字符串匹配這個特定領域的問題。

                文章的后半部分重點闡述了作者為什么要做COLA狀態(tài)機?想必這也是讀者比較好奇的問題。我?guī)痛蠹揖喴幌略牡谋硎觯?/p>

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

                所以COLA狀態(tài)機設計的目標很明確,有兩個核心理念:

              1. 簡潔的僅支持狀態(tài)流轉的狀態(tài)機,不需要支持嵌套、并行等高級玩法。
              2. 狀態(tài)機本身需要是Stateless(無狀態(tài))的,這樣一個Singleton Instance就能服務所有的狀態(tài)流轉請求了。
              3. COLA狀態(tài)機的核心概念如下圖所示,主要包括:

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

                COLA狀態(tài)機原理

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

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

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

                1. 連貫接口 Fluent Interfaces

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

                下圖中可以看到,我們在使用的時候是被嚴格限制的:

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

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

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

                這里簡單梳理一下狀態(tài)機的注冊和觸發(fā)原理。

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

                StateMachine stateMachine = builder.build(MACHINE_ID);

                COLA會將該狀態(tài)機在StateMachineFactory類中,放入一個ConcurrentHashMap,以狀態(tài)機名為key注冊。

                static Map stateMachineMap = new ConcurrentHashMap();

                注冊好后,用戶便可以使用狀態(tài)機,通過類似下方的代碼觸發(fā)狀態(tài)機的狀態(tài)流轉:

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

                內部實現(xiàn)如下:

              4. 首先判斷COLA狀態(tài)機整個組件是否初始化完成。
              5. 通過routeTransition尋找是否有符合條件的狀態(tài)流轉。
              6. transition.transit執(zhí)行狀態(tài)流轉。
              7. transition.transit方法中:

                檢查本次流轉是否符合condition,符合,則執(zhí)行對應的action。

                COLA狀態(tài)機實戰(zhàn)

                **PS:以下實戰(zhàn)代碼取自COLA官方倉庫測試類

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

              8. 從單一狀態(tài)流轉到另一個狀態(tài)
              9. @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); };}

                可以看到,每次進行狀態(tài)流轉時,檢查checkCondition(),當返回true,執(zhí)行狀態(tài)流轉的操作doAction()。

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

              10. 從多個狀態(tài)流傳到新的狀態(tài)
              11. @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);}

              12. 狀態(tài)內部觸發(fā)流轉
              13. @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);}

              14. 多線程測試并發(fā)測試
              15. @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)機時無狀態(tài)的狀態(tài)機,所以性能是很高的。相比起來,SpringStateMachine由于是有狀態(tài)的,就需要使用者自行保證線程安全了。

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

                /*** 測試選擇分支,針對同一個事件: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); }

                可以看到,編寫一個多分支的狀態(tài)機也是非常簡單明了的。

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

                沒想到吧,還能通過代碼定義好的狀態(tài)機反向生成plantUML圖,實現(xiàn)狀態(tài)機的可視化。(可以用圖說話,和產品對比下狀態(tài)實現(xiàn)的是否正確了。)

                四、特殊使用示例

              16. 不滿足狀態(tài)流轉條件時的處理
              17. @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);}

                可以看到,當checkConditionFalse()執(zhí)行時,永遠不會滿足狀態(tài)流轉的條件,則狀態(tài)不會變化,會直接返回原來的STATE1。相關源碼在這里:

              18. 重復定義相同的狀態(tài)流轉
              19. @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());}

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

              20. 重復定義狀態(tài)機
              21. @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”);}

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

                結語

                為了不把篇幅拉得過長,在這里無法詳細地橫向對比幾大主流狀態(tài)機(Spring Statemachine,Squirrel statemachine等)和COLA的區(qū)別,不過基于筆者在Spring Statemachine踩過的深坑,目前來看,COLA狀態(tài)機的簡潔設計適合用在訂單管理等小型狀態(tài)機的維護,如果你想要在你的項目中接入狀態(tài)機,又不需要嵌套、并行等高級玩法,那么COLA是個十分合適的選擇。

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

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

                我的唯一技術公眾號:后端技術漫談

                – END –

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

                相關推薦

                • 分享4條發(fā)微商朋友圈的方法(微商朋友圈應該怎么發(fā))

                  對于微商朋友來說,朋友圈的重要性不言而喻了。 那么微商的朋友圈到底該怎么發(fā)呢? 為什么同樣是經營一個朋友圈,有的微商看起來逼格滿滿,實際效果也不錯;而有的卻動都不動就被屏蔽甚至拉黑…

                  2022年11月27日
                • 30個無加盟費的項目(茶顏悅色奶茶店加盟費多少)

                  茶顏悅色又爆了,8月18日,茶顏悅色南京門店正式開業(yè),開張不到半小時,門店就人滿為患,消費者的購買熱情十分高漲,而由于人流量過大造成擁堵,茶顏悅色也不得不暫停營業(yè)。 當然,這里面排…

                  2022年11月27日
                • 淘寶工廠店的東西是正品嗎?淘寶工廠店為什么便宜

                  淘工廠直營店其實就是鏈接淘寶賣家與工廠的平臺,直接對接工廠型的商家,店鋪里的東西大部分價格都會很低。那么,淘工廠直營店靠譜嗎? 淘工廠直營店是靠譜的,但售后問題可能得不到很好的保障…

                  2022年11月27日
                • 凈利潤率越高越好嗎(凈利潤率多少合適)

                  一、持續(xù)增收不增利,平均凈利潤率首次跌入個位數(shù) 2021年,增收不增利依舊是行業(yè)主流。具體來看,大部分企業(yè)營業(yè)收入呈增長態(tài)勢,E50企業(yè)平均同比增速達到17.3%,但是利潤增速則明…

                  2022年11月26日
                • 《寶可夢朱紫》夢特性怎么獲得?隱藏特性獲取方法推薦

                  寶可夢朱紫里有很多寶可夢都是擁有夢特性會變強的寶可夢,很多玩家不知道夢特性怎么獲得,下面就給大家?guī)韺毧蓧糁熳想[藏特性獲取方法推薦,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 …

                  2022年11月25日
                • 《寶可夢朱紫》奇魯莉安怎么進化?奇魯莉安進化方法分享

                  寶可夢朱紫中的奇魯莉安要怎么進化呢?很多玩家都不知道,下面就給大家?guī)韺毧蓧糁熳掀骠斃虬策M化方法分享,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 奇魯莉安進化方法分享 奇魯莉安…

                  2022年11月25日
                • 數(shù)字看亮點!前十月我國造船三大指標繼續(xù)全球領先

                  央視網(wǎng)消息:工業(yè)和信息化部最新數(shù)據(jù)顯示,今年1—10月,我國造船三大指標繼續(xù)保持全球領先。隨著生產節(jié)奏加快,船企產業(yè)集中度進一步提升。 2022年1—10月,全國造船完工量、新接訂…

                  2022年11月25日
                • 前十個月我國造船業(yè)三大指標穩(wěn)居世界第一

                  今年1—10月,我國造船業(yè)在國際市場的份額繼續(xù)穩(wěn)居世界第一。一批高技術高附加值船舶實現(xiàn)了批量接單、批量生產。 近日,中國船舶上海船舶研究設計院又新接了韓國船東三艘汽車運輸船的設計追…

                  2022年11月25日
                • 規(guī)范透明促PPP高質量發(fā)展——16萬億元大市場迎來新規(guī)

                  近日,財政部印發(fā)《關于進一步推動政府和社會資本合作(PPP)規(guī)范發(fā)展、陽光運行的通知》,從做好項目前期論證、推動項目規(guī)范運作、嚴防隱性債務風險、保障項目陽光運行四個方面進一步規(guī)范P…

                  2022年11月25日
                • 客服的崗位職責怎么寫(客服工作內容及職責)

                  各位小伙伴們大家周一好,又到了每周一給大家分享干貨內容的時候啦~ 本期來跟大家分享一下客服工作管理流程以及客服崗位里面的每項職能崗位的核心細則,也是干貨滿滿推薦收藏~ 一.補償流程…

                  2022年11月25日

                聯(lián)系我們

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