このプロジェクトは最終的に Spring ステート マシンを使用します。これは非常にエレガントです。

1. ステートマシンとは何ですか

1.1 状態とは

まずは「状態」(State)とは何かについて説明しましょう。現実のものにはさまざまな状態があり、たとえば自動ドアには開いた状態と閉じた状態があります。私たちが通常ステートマシンと呼んでいるものは有限状態マシン、つまり記述されたものの状態の数が有限であるもので、例えば自動ドアの状態は開いている状態と閉じている状態の2つです。

ステートマシン、つまりState Machineは実際の機械を指すのではなく、数学的モデルを指します。平たく言えば、一般に状態遷移図のことを指します。例えば、自動ドアの動作ルールによれば、次のようなグラフが抽象化できます。

自動ドアには開状態と閉状態があり、閉状態でドア開信号を読み取ると開状態に切り替わります。開状態でドア閉信号を読み取ると閉状態に切り替わります。

ステートマシンの正式名称は有限状態オートマトンですが、自動という言葉にも重要な意味が含まれています。ステート マシンが与えられ、その現在の状態と入力が与えられると、出力状態を明示的に計算できます。たとえば、自動ドアの場合、初期状態が閉じていて、「ドアを開けてください」という入力が与えられると、次の状態を計算できます。

このようにして、ステート マシンの基本的な定義を導入しました。繰り返しますが、ステートマシンとは有限状態オートマトンの略で、現実の物の動作規則を抽象化した数理モデルです。

1.2 4つの概念

ステートマシンの 4 つの概念を以下に示します。

  • 1つ目は州、州です。ステート マシンには少なくとも 2 つの状態が含まれている必要があります。たとえば、上記の自動ドアの例では、開いている状態と閉じている状態の 2 つの状態があります。

  • 2つ目はイベント、イベントです。イベントは、操作を実行するためのトリガー条件またはパスワードです。自動ドアの場合は「ドア開ボタンを押す」がイベントとなります。

  • 3つ目はアクション、アクションです。イベント発生後に実行されるアクション。たとえば、イベントは「ボタンを押してドアを開ける」であり、アクションは「ドアを開ける」です。プログラミングする場合、通常、アクションは関数に対応します。

  • 4つ目はトランジション、変革です。つまり、ある状態から別の状態に変化することです。例えば「ドアを開ける処理」は変形です。

1.3 ステートマシン

有限状態マシン (Finite-state machine、FSM) は、有限状態オートマトン、または略してステート マシンとも呼ばれ、有限数の状態と、これらの状態間の遷移やアクションなどの動作を表す数学モデルです。

FSM はアルゴリズムの概念であり、簡単に言うと、有限状態マシンは一連の状態、初期状態、入力、および入力と既存の状態に基づいて次の状態に遷移する遷移関数で構成されます。

その機能は主に、オブジェクトがそのライフサイクルで経験する一連の状態と、外界からのさまざまなイベントにどのように応答するかを記述することです。

2. ステートマシン図

要件を作成するときは、開始、終了、現在の状態、次の状態 (目標状態)、アクション、条件の 6 つの要素を理解する必要があります。これでステート マシン図が完成します。

注文を例に挙げます。支払い保留ステータスから配送保留ステータスへの移行を例に挙げます。

  • ①現状:現在の状態を指します。支払われるべきもの

  • ②条件:「イベント」とも呼ばれ、条件が満たされるとアクションがトリガーされたり、状態遷移が行われます。支払いイベント

  • ③アクション:条件成立後に実行されるアクションです。アクションの実行後、新しい状態に移行することも、元の状態にとどまることもできます。アクションは必要ありません。条件が満たされると、アクションを実行せずに新しい状態に直接移行できます。ステータスが「出荷待ち」に移行しました

  • ④ 2 番目の状態: 条件が満たされた後に移行する新しい状態。「二次状態」は「現在の状態」に対して相対的なものであり、「二次状態」が活性化されると新たな「現在の状態」に変化します。配送上のご注意

1. 特定の「プログラムの動作」を「状態」として扱うことは避けてください。では、「アクション」と「状態」をどのように区別すればよいのでしょうか?「アクション」は不安定であり、条件トリガがなくても「アクション」は実行されれば終了しますが、「状態」は比較的安定しており、外部条件トリガがなければ状態は永遠に続きます。

2. 状態分割中に一部の状態が失われるため、ジャンプ ロジックが不完全になります。したがって、ステートマシンを設計する際には、設計された状態図または状態テーブルを繰り返し確認し、最終的に壊れない設計スキームを達成する必要があります。

3、スプリングステートマシン

3.1 ステートマシン Spring Statemachine の概要

Spring Statemachine は、アプリケーション開発者が Spring アプリケーションでステート マシンの概念を使用するためのフレームワークです。

Spring Statemachine は、次の機能を提供するように設計されています。

  1. シンプルなユースケース向けの使いやすいフラットなシングルステージステートマシン。

  2. 複雑なステート構成を簡素化するための階層型ステート マシン構造。

  3. ステート マシン領域では、より複雑なステート構成が提供されます。

  4. トリガー、トランジション、ガード、アクションを使用します。

  5. セキュリティ構成アダプターを入力します。

  6. Spring Application コンテキストの外で使用する一般的なユースケース レシピを簡単にインスタンス化するためのビルダー パターン

  7. Zookeeper に基づく分散ステート マシン

  8. ステートマシンのイベントリスナー。

  9. UML Eclipse Papyrus モデリング。

  10. コンピューターの構成を永続ストレージに保存します。

  11. Spring IOC 統合は、Bean をステートマシンに関連付けます。

ステート マシンは、動作の一貫性が常に保証されているため強力であり、デバッグが比較的簡単になります。これは、マシンの起動時に動作ルールが記述されるためです。考え方としては、アプリケーションは有限数の状態に存在し、事前定義された特定のトリガーによってアプリケーションをある状態から別の状態に移行できるということです。このようなトリガーは、イベントまたはタイマーベースにすることができます。

アプリケーションの外部で高レベルのロジックを定義し、ステート マシンに依存して状態を管理する方がはるかに簡単です。イベントを送信したり、変更をリッスンしたり、現在の状態を要求したりすることで、ステート マシンと対話できます。

公式サイト:spring.io/projects/sp…

3.2 クイックスタート

例として、注文ステータスの取り消しの例を見てみましょう。

テーブル構造は次のように設計されています。

CREATE TABLE `tb_order` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
      `order_code` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '订单编码',
      `status` smallint(3) DEFAULT NULL COMMENT '订单状态',
      `name` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '订单名称',
      `price` decimal(12,2) DEFAULT NULL COMMENT '价格',
      `delete_flag` tinyint(2) NOT NULL DEFAULT '0' COMMENT '删除标记,0未删除  1已删除',
      `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
      `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',
      `create_user_code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人',
      `update_user_code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '更新人',
      `version` int(11) NOT NULL DEFAULT '0' COMMENT '版本号',
      `remark` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';
    
    /*Data for the table `tb_order` */
    
    insert  into `tb_order`(`id`,`order_code`,`status`,`name`,`price`,`delete_flag`,`create_time`,`update_time`,`create_user_code`,`update_user_code`,`version`,`remark`) values 
    (2,'A111',1,'A','22.00',0,'2022-10-15 16:14:11','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),
    (3,'A111',1,'订单A','22.00',0,'2022-10-02 21:53:13','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),
    (4,'A111',1,'订单A','22.00',0,'2022-10-02 21:53:13','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),
    (5,'A111',1,'订单A','22.00',0,'2022-10-03 09:08:30','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL);

1) 依存関係を導入する

 <!-- redis持久化状态机 -->
    <dependency>
        <groupId>org.springframework.statemachine</groupId>
        <artifactId>spring-statemachine-redis</artifactId>
        <version>1.2.9.RELEASE</version>
    </dependency>
    <!--状态机-->
    <dependency>
        <groupId>org.springframework.statemachine</groupId>
        <artifactId>spring-statemachine-starter</artifactId>
        <version>2.0.1.RELEASE</version>
    </dependency>

2) ステートマシンの状態とイベントを定義する

状態の列挙:

/**
* author:公众号:码猿技术专栏
*/
public enum OrderStatus {
        // 待支付,待发货,待收货,已完成
        WAIT_PAYMENT(1, "待支付"),
        WAIT_DELIVER(2, "待发货"),
        WAIT_RECEIVE(3, "待收货"),
        FINISH(4, "已完成");
        private Integer key;
        private String desc;
        OrderStatus(Integer key, String desc) {
            this.key = key;
            this.desc = desc;
        }
        public Integer getKey() {
            return key;
        }
        public String getDesc() {
            return desc;
        }
        public static OrderStatus getByKey(Integer key) {
            for (OrderStatus e : values()) {
                if (e.getKey().equals(key)) {
                    return e;
                }
            }
            throw new RuntimeException("enum not exists.");
        }
    }

イベント:

/**
* author:公众号:码猿技术专栏
*/
public enum OrderStatusChangeEvent {
        // 支付,发货,确认收货
        PAYED, DELIVERY, RECEIVED;
}

3) ステート マシン ルールを定義し、ステート マシンを構成する

 @Configuration
    @EnableStateMachine(name = "orderStateMachine")
    public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {
        /**
         * 配置状态
         *
         * @param states
         * @throws Exception
         */
        public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {
            states
                    .withStates()
                    .initial(OrderStatus.WAIT_PAYMENT)
                    .states(EnumSet.allOf(OrderStatus.class));
        }
        /**
         * 配置状态转换事件关系
         *
         * @param transitions
         * @throws Exception
         */
        public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> transitions) throws Exception {
            transitions
                    //支付事件:待支付-》待发货
                    .withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
                    .and()
                    //发货事件:待发货-》待收货
                    .withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)
                    .and()
                    //收货事件:待收货-》已完成
                    .withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);
        }
    }

構成の永続性:

 /**
 * author:公众号:码猿技术专栏
 */ 
    @Configuration
    @Slf4j
    public class Persist<E, S> {
        /**
         * 持久化到内存map中
         *
         * @return
         */
        @Bean(name = "stateMachineMemPersister")
        public static StateMachinePersister getPersister() {
            return new DefaultStateMachinePersister(new StateMachinePersist() {
                @Override
                public void write(StateMachineContext context, Object contextObj) throws Exception {
                    log.info("持久化状态机,context:{},contextObj:{}", JSON.toJSONString(context), JSON.toJSONString(contextObj));
                    map.put(contextObj, context);
                }
                @Override
                public StateMachineContext read(Object contextObj) throws Exception {
                    log.info("获取状态机,contextObj:{}", JSON.toJSONString(contextObj));
                    StateMachineContext stateMachineContext = (StateMachineContext) map.get(contextObj);
                    log.info("获取状态机结果,stateMachineContext:{}", JSON.toJSONString(stateMachineContext));
                    return stateMachineContext;
                }
                private Map map = new HashMap();
            });
        }
    
        @Resource
        private RedisConnectionFactory redisConnectionFactory;
        /**
         * 持久化到redis中,在分布式系统中使用
         *
         * @return
         */
        @Bean(name = "stateMachineRedisPersister")
        public RedisStateMachinePersister<E, S> getRedisPersister() {
            RedisStateMachineContextRepository<E, S> repository = new RedisStateMachineContextRepository<>(redisConnectionFactory);
            RepositoryStateMachinePersist p = new RepositoryStateMachinePersist<>(repository);
            return new RedisStateMachinePersister<>(p);
        }
    }

4) 業務体制

コントローラ:

 /**
 * author:公众号:码猿技术专栏
 */ 
    @RestController
    @RequestMapping("/order")
    public class OrderController {
        @Resource
        private OrderService orderService;
        /**
         * 根据id查询订单
         *
         * @return
         */
        @RequestMapping("/getById")
        public Order getById(@RequestParam("id") Long id) {
            //根据id查询订单
            Order order = orderService.getById(id);
            return order;
        }
        /**
         * 创建订单
         *
         * @return
         */
        @RequestMapping("/create")
        public String create(@RequestBody Order order) {
            //创建订单
            orderService.create(order);
            return "sucess";
        }
        /**
         * 对订单进行支付
         *
         * @param id
         * @return
         */
        @RequestMapping("/pay")
        public String pay(@RequestParam("id") Long id) {
            //对订单进行支付
            orderService.pay(id);
            return "success";
        }
    
        /**
         * 对订单进行发货
         *
         * @param id
         * @return
         */
        @RequestMapping("/deliver")
        public String deliver(@RequestParam("id") Long id) {
            //对订单进行确认收货
            orderService.deliver(id);
            return "success";
        }
        /**
         * 对订单进行确认收货
         *
         * @param id
         * @return
         */
        @RequestMapping("/receive")
        public String receive(@RequestParam("id") Long id) {
            //对订单进行确认收货
            orderService.receive(id);
            return "success";
        }
    }

提供:

 /**
 * author:公众号:码猿技术专栏
 */ 
    @Service("orderService")
    @Slf4j
    public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
        @Resource
        private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
        @Resource
        private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
        @Resource
        private OrderMapper orderMapper;
        /**
         * 创建订单
         *
         * @param order
         * @return
         */
        public Order create(Order order) {
            order.setStatus(OrderStatus.WAIT_PAYMENT.getKey());
            orderMapper.insert(order);
            return order;
        }
        /**
         * 对订单进行支付
         *
         * @param id
         * @return
         */
        public Order pay(Long id) {
            Order order = orderMapper.selectById(id);
            log.info("线程名称:{},尝试支付,订单号:{}" ,Thread.currentThread().getName() , id);
            if (!sendEvent(OrderStatusChangeEvent.PAYED, order)) {
                log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);
                throw new RuntimeException("支付失败, 订单状态异常");
            }
            return order;
        }
        /**
         * 对订单进行发货
         *
         * @param id
         * @return
         */
        public Order deliver(Long id) {
            Order order = orderMapper.selectById(id);
            log.info("线程名称:{},尝试发货,订单号:{}" ,Thread.currentThread().getName() , id);
            if (!sendEvent(OrderStatusChangeEvent.DELIVERY, order)) {
                log.error("线程名称:{},发货失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);
                throw new RuntimeException("发货失败, 订单状态异常");
            }
            return order;
        }
        /**
         * 对订单进行确认收货
         *
         * @param id
         * @return
         */
        public Order receive(Long id) {
            Order order = orderMapper.selectById(id);
            log.info("线程名称:{},尝试收货,订单号:{}" ,Thread.currentThread().getName() , id);
            if (!sendEvent(OrderStatusChangeEvent.RECEIVED, order)) {
                log.error("线程名称:{},收货失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);
                throw new RuntimeException("收货失败, 订单状态异常");
            }
            return order;
        }
        /**
         * 发送订单状态转换事件
         * synchronized修饰保证这个方法是线程安全的
         *
         * @param changeEvent
         * @param order
         * @return
         */
        private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
            boolean result = false;
            try {
                //启动状态机
                orderStateMachine.start();
                //尝试恢复状态机状态
                stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
                Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
                result = orderStateMachine.sendEvent(message);
                //持久化状态机状态
                stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            } catch (Exception e) {
                log.error("订单操作失败:{}", e);
            } finally {
                orderStateMachine.stop();
            }
            return result;
        }
    }

状態の変化をリッスンします。

 /**
 * author:公众号:码猿技术专栏
 */     
    @Component("orderStateListener")
    @WithStateMachine(name = "orderStateMachine")
    @Slf4j
    public class OrderStateListenerImpl {
        @Resource
        private OrderMapper orderMapper;
        
        @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
        public void payTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
        @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
        public void deliverTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("发货,状态机反馈信息:{}",  message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
        @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
        public void receiveTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("确认收货,状态机反馈信息:{}",  message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.FINISH.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
    }

3.3 テスト検証

1) ビジネスの確認

  • 注文を追加する

    http://localhost:8084/order/create

  • 注文の代金を支払う

    http://localhost:8084/order/pay?id=2

  • 注文品を発送する

    http://localhost:8084/order/deliver?id=2

  • 注文の受領を確認する

    http://localhost:8084/order/receive?id=2

通常の処理が終了します。注文の支払いを行って再度支払うと、エラーが報告されます。http://localhost:8084/order/pay?id=2

エラーは次のとおりです。

2) 永続性を確認する

メモリー

メモリ永続クラスを使用して永続化します。

 /**
 * author:公众号:码猿技术专栏
 */ 
 @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
    
    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            //持久化状态机状态
            stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

Redis の永続性

依存関係をインポートします。

<!-- redis持久化状态机 -->
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-redis</artifactId>
    <version>1.2.9.RELEASE</version>
</dependency>

yaml を設定します。

spring:
  redis:
    database: 0
    host: localhost
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: ''
        min-idle: 0
    password: ''
    port: 6379
    timeout: 0

Redis 永続クラスを使用して永続化します。

 /**
 * author:公众号:码猿技术专栏
 */ 
 @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineRedisPersister;
    
    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineRedisPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            //持久化状态机状态
            stateMachineRedisPersister.persist(orderStateMachine, String.valueOf(order.getId()));
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

3.4 ステートマシンの問題

1) stateMachine は例外をスローできず、例外はステート マシンによってダイジェストされます。

問題現象

orderStateMachine.sendEvent(message); から得られる結果は認識できません。実行が正常であったか、例外がスローされたかにかかわらず、true を返します。

 @Resource
    private OrderMapper orderMapper;
    
    @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    @Transactional(rollbackFor = Exception.class)
    public void payTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order) message.getHeaders().get("order");
        log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());
        try {
            //更新订单
            order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
            //模拟异常
            if(Objects.equals(order.getName(),"A")){
                throw new RuntimeException("执行业务异常");
            }
        } catch (Exception e) {
            //如果出现异常,记录异常信息,抛出异常信息进行回滚
            log.error("payTransition 出现异常:{}",e);
            throw e;
        }
    }

リスニング イベントは例外をスローしますが、送信イベントでは感知できません。

 private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
             //事件执行异常了,依然返回true,无法感知异常
            result = orderStateMachine.sendEvent(message);
            if(result){
                //持久化状态机状态,如果根据true持久化,则会出现问题
                stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            }
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

デバッグの結果、イベントの送信とイベントのリッスンがスレッドであり、リッスン操作が完了した後にイベント送信の結果が返されることがわかりました。

リスニングスレッド:

解決策: 例外を自分でデータベースまたはメモリに保存し、判断する

インターフェース org.springframework.statemachine.StateMachine##getExtendedState を渡すこともできます。

メソッドは実行状態をこの変数に入れます

public interface ExtendedState {
        Map<Object, Object> getVariables();
        <T> T get(Object var1, Class<T> var2);
        void setExtendedStateChangeListener(ExtendedState.ExtendedStateChangeListener var1);
        public interface ExtendedStateChangeListener {
            void changed(Object var1, Object var2);
        }
    }

org.springframework.statemachine.support.DefaultExtendedState##getVariables

private final Map<Object, Object> variables;
    
    public DefaultExtendedState() {
        this.variables = new ObservableMap(new ConcurrentHashMap(), new DefaultExtendedState.LocalMapChangeListener());
    }
    
    public Map<Object, Object> getVariables() {
        return this.variables;
    }

監視状態の変更: ビジネスの実行結果を保存します。1 は成功、0 は失敗します。

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    @Transactional(rollbackFor = Exception.class)
    public void payTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order) message.getHeaders().get("order");
        log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());
        try {
            //更新订单
            order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
            //模拟异常
            if(Objects.equals(order.getName(),"A")){
                throw new RuntimeException("执行业务异常");
            }
            //成功 则为1
            orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
        } catch (Exception e) {
            //如果出现异常,则进行回滚
            log.error("payTransition 出现异常:{}",e);
            //将异常信息变量信息中,失败则为0
            orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);
            throw e;
        }
    }

イベント送信のレトロフィット: ビジネス実行例外が取得された場合、失敗が返され、ステート マシンは永続化されません。 com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##sendEvent

 @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
    
    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order){
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            if(!result){
                return false;
            }
            //获取到监听的结果信息
            Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());
            //操作完成之后,删除本次对应的key信息
            orderStateMachine.getExtendedState().getVariables().remove(CommonConstants.payTransition+order.getId());
            //如果事务执行成功,则持久化状态机
            if(Objects.equals(1,Integer.valueOf(o))){
                //持久化状态机状态
                stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            }else {
                //订单执行业务异常
                return false;
            }
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

コードの最適化

  • 送信イベントは支払いのみを対象としていますが、非支払いイベントの場合はどうなりますか?

//获取到监听的结果信息
Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());
  • 設定状況を監視するコードに重複コードがあるため最適化が必要 AOPが使用可能

try {
        //TODO 其他业务
        //成功 则为1
        orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
    } catch (Exception e) {
        //如果出现异常,则进行回滚
        log.error("payTransition 出现异常:{}",e);
        //将异常信息变量信息中,失败则为0
        orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);
        throw e;
    }

定数クラス:

public interface CommonConstants {
        String orderHeader="order";
        String payTransition="payTransition";
        String deliverTransition="deliverTransition";
        String receiveTransition="receiveTransition";
    }

支払い送信イベント: com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##pay

 @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
    @Resource
    private OrderMapper orderMapper;
    
    /**
     * 对订单进行支付
     *
     * @param id
     * @return
     */
    public Order pay(Long id) {
        Order order = orderMapper.selectById(id);
        log.info("线程名称:{},尝试支付,订单号:{}" ,Thread.currentThread().getName() , id);
        if (!sendEvent(OrderStatusChangeEvent.PAYED, order,CommonConstants.payTransition)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);
            throw new RuntimeException("支付失败, 订单状态异常");
        }
        return order;
    }
    
    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order,String key){
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            if(!result){
                return false;
            }
            //获取到监听的结果信息
            Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(key + order.getId());
            //操作完成之后,删除本次对应的key信息
            orderStateMachine.getExtendedState().getVariables().remove(key+order.getId());
            //如果事务执行成功,则持久化状态机
            if(Objects.equals(1,Integer.valueOf(o))){
                //持久化状态机状态
                stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            }else {
                //订单执行业务异常
                return false;
            }
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

aop を使用してイベントの側面をリッスンし、ビジネスの実行結果をステート マシンの変数にカプセル化します。

 @Retention(RetentionPolicy.RUNTIME)
    public @interface LogResult {
        /**
         *执行的业务key
         *
         * @return String
         */
        String key();
    }

セクション:

 @Component
    @Aspect
    @Slf4j
    public class LogResultAspect {
    
        //拦截 LogHistory注解
        @Pointcut("@annotation(com.zengqingfa.springboot.state.demo.aop.annotation.LogResult)")
        private void logResultPointCut() {
            //logResultPointCut 日志注解切点
        }
        @Resource
        private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
        
        @Around("logResultPointCut()")
        public Object logResultAround(ProceedingJoinPoint pjp) throws Throwable {
            //获取参数
            Object[] args = pjp.getArgs();
            log.info("参数args:{}", args);
            Message message = (Message) args[0];
            Order order = (Order) message.getHeaders().get("order");
            //获取方法
            Method method = ((MethodSignature) pjp.getSignature()).getMethod();
            // 获取LogHistory注解
            LogResult logResult = method.getAnnotation(LogResult.class);
            String key = logResult.key();
            Object returnVal = null;
            try {
                //执行方法
                returnVal = pjp.proceed();
                //如果业务执行正常,则保存信息
                //成功 则为1
                orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 1);
            } catch (Throwable e) {
                log.error("e:{}", e.getMessage());
                //如果业务执行异常,则保存信息
                //将异常信息变量信息中,失败则为0
                orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 0);
                throw e;
            }
            return returnVal;
        }
    }

リスナー クラスはアノテーションを使用します。

 @Component("orderStateListener")
    @WithStateMachine(name = "orderStateMachine")
    @Slf4j
    public class OrderStateListenerImpl {
        @Resource
        private OrderMapper orderMapper;
    
        @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
        @Transactional(rollbackFor = Exception.class)
        @LogResult(key = CommonConstants.payTransition)
        public void payTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("支付,状态机反馈信息:{}", message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
            //模拟异常
            if (Objects.equals(order.getName(), "A")) {
                throw new RuntimeException("执行业务异常");
            }
        }
        @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
        @LogResult(key = CommonConstants.deliverTransition)
        public void deliverTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("发货,状态机反馈信息:{}", message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
        @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
        @LogResult(key = CommonConstants.receiveTransition)
        public void receiveTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("确认收货,状态机反馈信息:{}", message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.FINISH.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
    }

おすすめ

転載: blog.csdn.net/m0_71777195/article/details/130700027