<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      管理訂單狀態,該上狀態機嗎?輕量級狀態機COLA StateMachine保姆級入門教程

      前言

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

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

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

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

      private synchronized boolean sendEvent(Message<PurchaseOrderEvent> message, OrderEntity orderEntity) {
              boolean result = false;
              try {
                  stateMachine.start();
                  // 嘗試恢復狀態機狀態
                  persister.restore(stateMachine, orderEntity);
                  // 執行事件
                  result = stateMachine.sendEvent(message);
                  // 持久化狀態機狀態
                  persister.persist(stateMachine, (OrderEntity) message.getHeaders().get("purchaseOrder"));
              } catch (Exception e) {
                  log.error("sendEvent error", e);
              } finally {
                  stateMachine.stop();
              }
              return result;
          }
      

      吃了一次虧后,我再一次在網上翻閱各種Java狀態機的實現,有大的開源項目,也有小而美的個人實現。結果在COLA架構中發現了COLA還寫了一套狀態機實現。COLA的作者給我們提供了一個無狀態的,輕量化的狀態機,接入十分簡單。并且由于無狀態的特點,可以做到線程安全,支持電商的高并發場景。

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

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

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

      COLA狀態機介紹

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

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

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

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

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

      文章的后半部分重點闡述了作者為什么要做COLA狀態機?想必這也是讀者比較好奇的問題。我幫大家精簡一下原文的表述:

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

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

      1. 簡潔的僅支持狀態流轉的狀態機,不需要支持嵌套、并行等高級玩法。
      2. 狀態機本身需要是Stateless(無狀態)的,這樣一個Singleton Instance就能服務所有的狀態流轉請求了。

      COLA狀態機的核心概念如下圖所示,主要包括:

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

      COLA狀態機原理

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

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

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

      1. 連貫接口 Fluent Interfaces

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

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

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

      這是如何實現的?其實是使用了Java接口來實現。

      2. 狀態機注冊和觸發原理

      這里簡單梳理一下狀態機的注冊和觸發原理。

      用戶執行如下代碼來創建一個狀態機,指定一個MACHINE_ID:

      StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);
      

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

      static Map<String /* machineId */, StateMachine> stateMachineMap = new ConcurrentHashMap<>();
      

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

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

      內部實現如下:

      1. 首先判斷COLA狀態機整個組件是否初始化完成。
      2. 通過routeTransition尋找是否有符合條件的狀態流轉。
      3. transition.transit執行狀態流轉。

      transition.transit方法中:

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

      COLA狀態機實戰

      **PS:以下實戰代碼取自COLA官方倉庫測試類

      一、狀態流轉使用示例

      1. 從單一狀態流轉到另一個狀態
      @Test
      public void testExternalNormal(){
          StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
          builder.externalTransition()
                  .from(States.STATE1)
                  .to(States.STATE2)
                  .on(Events.EVENT1)
                  .when(checkCondition())
                  .perform(doAction());
      
          StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);
          States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context());
          Assert.assertEquals(States.STATE2, target);
      }
      
      private Condition<Context> checkCondition() {
      		return (ctx) -> {return true;};
      }
      
      private Action<States, Events, Context> doAction() {
          return (from, to, event, ctx)->{
              System.out.println(ctx.operator+" is operating "+ctx.entityId+" from:"+from+" to:"+to+" on:"+event);
              };
      }
      

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

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

      1. 從多個狀態流傳到新的狀態
      @Test
      public void testExternalTransitionsNormal(){
          StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
          builder.externalTransitions()
                  .fromAmong(States.STATE1, States.STATE2, States.STATE3)
                  .to(States.STATE4)
                  .on(Events.EVENT1)
                  .when(checkCondition())
                  .perform(doAction());
      
          StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID+"1");
          States target = stateMachine.fireEvent(States.STATE2, Events.EVENT1, new Context());
          Assert.assertEquals(States.STATE4, target);
      }
      
      1. 狀態內部觸發流轉
      @Test
      public void testInternalNormal(){
          StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
          builder.internalTransition()
                  .within(States.STATE1)
                  .on(Events.INTERNAL_EVENT)
                  .when(checkCondition())
                  .perform(doAction());
          StateMachine<States, Events, Context> 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);
      }
      
      1. 多線程測試并發測試
      @Test
      public void testMultiThread(){
      	buildStateMachine("testMultiThread");
      
        for(int i=0 ; i<10 ; i++){
        	Thread thread = new Thread(()->{
            StateMachine<States, Events, Context> 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<10 ; i++) {
            Thread thread = new Thread(() -> {
            StateMachine<States, Events, Context> 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<10 ; i++) {
            Thread thread = new Thread(() -> {
            StateMachine<States, Events, Context> stateMachine = StateMachineFactory.get("testMultiThread");
            States target = stateMachine.fireEvent(States.STATE1, Events.EVENT3, new Context());
            Assert.assertEquals(States.STATE3, target);
            });
            thread.start();
        }
      
      }
      

      由于COLA狀態機時無狀態的狀態機,所以性能是很高的。相比起來,SpringStateMachine由于是有狀態的,就需要使用者自行保證線程安全了。

      二、多分支狀態流轉示例

      /**
      * 測試選擇分支,針對同一個事件:EVENT1
      * if condition == "1", STATE1 --> STATE1
      * if condition == "2" , STATE1 --> STATE2
      * if condition == "3" , STATE1 --> STATE3
      */
      @Test
      public void testChoice(){
        StateMachineBuilder<StateMachineTest.States, StateMachineTest.Events, Context> 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<StateMachineTest.States, StateMachineTest.Events, Context> 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);
        }
      

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

      三、通過狀態機反向生成PlantUml圖

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

      四、特殊使用示例

      1. 不滿足狀態流轉條件時的處理
      @Test
      public void testConditionNotMeet(){
        StateMachineBuilder<StateMachineTest.States, StateMachineTest.Events, StateMachineTest.Context> builder = StateMachineBuilderFactory.create();
        builder.externalTransition()
        .from(StateMachineTest.States.STATE1)
        .to(StateMachineTest.States.STATE2)
        .on(StateMachineTest.Events.EVENT1)
        .when(checkConditionFalse())
        .perform(doAction());
      
        StateMachine<StateMachineTest.States, StateMachineTest.Events, StateMachineTest.Context> 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()執行時,永遠不會滿足狀態流轉的條件,則狀態不會變化,會直接返回原來的STATE1。相關源碼在這里:

      1. 重復定義相同的狀態流轉
      @Test(expected = StateMachineException.class)
      public void testDuplicatedTransition(){
        StateMachineBuilder<StateMachineTest.States, StateMachineTest.Events, StateMachineTest.Context> 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執行到on(StateMachineTest.Events.EVENT1)函數時,拋出StateMachineException異常。拋出異常在on()的verify檢查這里,如下:

      1. 重復定義狀態機
      @Test(expected = StateMachineException.class)
      public void testDuplicateMachine(){
        StateMachineBuilder<StateMachineTest.States, StateMachineTest.Events, StateMachineTest.Context> 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同名狀態機時拋出StateMachineException異常。拋出異常的源碼在狀態機的注冊函數中,如下:

      結語

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

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

      持續的更新原創優質文章,離不開你的點贊,轉發和分享!

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

      posted @ 2022-06-02 22:18  蠻三刀醬  閱讀(6804)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 插插射啊爱视频日a级| 免费无码高H视频在线观看| 美女一区二区三区在线观看视频| 真实单亲乱l仑对白视频| 国产嫩草精品网亚洲av| 漳浦县| 亚洲精品日韩在线丰满| 日本国产精品第一页久久 | 亚洲精品人妻中文字幕| 麻豆久久久9性大片| 亚洲欧洲日产国码高潮αv| 国产成人一区二区三区视频免费| 中文字幕日本六区小电影| 91亚洲国产成人精品性色| 东至县| 在线日韩日本国产亚洲| 中国性欧美videofree精品| 精品国产一区二区三区大| 欧美人与动牲交A免费观看| 亚洲色欲久久久久综合网| 国产一区二区三区不卡观| 孕妇特级毛片ww无码内射| 影音先锋啪啪av资源网站| 激情伊人五月天久久综合| www免费视频com| 夜夜添无码一区二区三区| 人人妻人人爽人人澡av| 在线精品亚洲区一区二区| 男女激情一区二区三区| 亚洲乳大丰满中文字幕| 久久精品久久黄色片看看| 亚洲国产精品午夜福利| 99精品国产一区二区三| 色香欲天天影视综合网| 99国精品午夜福利视频不卡99 | 亚洲中文字幕久久精品品| 亚洲人成网网址在线看| 亚洲性色AV一区二区三区| 色综合视频一区二区三区| 成在线人免费| 韩国深夜福利视频在线观看|