通过实例学习SpringStateMachine之Tasks

背景介绍

本系列通过学习SpringStateMachine中附带的10余个Sample来学习SpringStateMachine中的各个概念和用法。项目是使用的分支为2.2.0.RELEASE[1]。项目参考文档也是2.2.0.RELEASE[1]。

Tasks简介

Tasks演示了在region内如何处理并行任务,并且添加了错误处理方法,在任务恢复执行前,通过自动或手动的方式修正问题。我们在使用状态机时常会遇到当一个事件发生后,会转移到多个状态上,执行对应的操作,当这些操作执行完毕后,在统一转换到一个状态上。Tasks这个demo就是通过fork、join这两个伪状态实现了一到多再到一这样的能力。此外在发生转换时我们希望根据条件转换到不同的状态上,这时候通过choice实现选选择性转换的能力。Tasks这个demo对应的状态机如下图所示:
在这里插入图片描述

  • 状态机总是会进入到READY状态,通过产生RUN事件来执行任务。
  • tasks状态有三个独立的region组成,这个状态处理fork和join状态之间。当fork状态进入到tasks状态后会转换到tasks的三个内部状态。当三个内部状态转换到其结束状态后,会继续转移到JOIN状态。
  • JOIN状态转移到CHOICE状态后会根据跳转检测是转换到READY状态还是ERROR状态
  • 在ERROR状态内转移到AUTOMATIC执行修复操作,如果失败通过触发FALLBACK事件转换到MANUAL状态。随后通过CONTINUE状态再回到READY状态。

Tasks 依赖

项目在实现上述功能时,需要依赖springshell,官方给出的demo[1]使用了spring-shell1.2,本文将其改为spring-shell 2.0.0.RELEASE。

 <dependency>
       <groupId>
            org.springframework.statemachine</groupId>
            <artifactId>spring-statemachine-core</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.shell</groupId>
            <artifactId>spring-shell-starter</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

Tasks 实现

Tasks状态机的状态与事件定义如下:

public enum States {
    READY,
    FORK, JOIN, CHOICE,
    TASKS, T1, T1E, T2, T2E, T3, T3E,
    ERROR, AUTOMATIC, MANUAL
}
public enum Events {
    RUN, FALLBACK, CONTINUE, FIX;
}

接着配置状态。由于需要使用一到多再到一的能力,因此配置中使用了fork和join方法配置fork与join状态。通过choice方法实现选择性状态转移。

@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
		throws Exception {
	states
		.withStates()
			.initial(States.READY)
			.fork(States.FORK)
			.state(States.TASKS)
			.join(States.JOIN)
			.choice(States.CHOICE)
			.state(States.ERROR)
			.and()
			.withStates()
				.parent(States.TASKS)
				.initial(States.T1)
				.end(States.T1E)
				.and()
			.withStates()
				.parent(States.TASKS)
				.initial(States.T2)
				.end(States.T2E)
				.and()
			.withStates()
				.parent(States.TASKS)
				.initial(States.T3)
				.end(States.T3E)
				.and()
			.withStates()
				.parent(States.ERROR)
				.initial(States.AUTOMATIC)
				.state(States.AUTOMATIC, automaticAction(), null)
				.state(States.MANUAL);
}

接着配置转换。为了描述fork状态转换使用withFork方法。join使用withJoin方法描述。选择性状态通过withChoice方法描述。

使用withChoice通过配置的条件方法tasksChoiceGuard实现条件判断。当满足条件转换到ERROR状态,当不满足条件时候转换到READY状态。这里first作用相当与if…else中的if,而last相当与else。

@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
		throws Exception {
	transitions
		.withExternal()
			.source(States.READY).target(States.FORK)
			.event(Events.RUN)
			.and()
		.withFork()
			.source(States.FORK).target(States.TASKS)
			.and()
		.withExternal()
			.source(States.T1).target(States.T1E)
			.and()
		.withExternal()
			.source(States.T2).target(States.T2E)
			.and()
		.withExternal()
			.source(States.T3).target(States.T3E)
			.and()
		.withJoin()
			.source(States.TASKS).target(States.JOIN)
			.and()
		.withExternal()
			.source(States.JOIN).target(States.CHOICE)
			.and()
		.withChoice()
			.source(States.CHOICE)
			.first(States.ERROR, tasksChoiceGuard())
			.last(States.READY)
			.and()
		.withExternal()
			.source(States.ERROR).target(States.READY)
			.event(Events.CONTINUE)
			.and()
		.withExternal()
			.source(States.AUTOMATIC).target(States.MANUAL)
			.event(Events.FALLBACK)
			.and()
		.withInternal()
			.source(States.MANUAL)
			.action(fixAction())
			.event(Events.FIX);
}

@Bean
public Guard<States, Events> tasksChoiceGuard() {
	return new Guard<States, Events>() {

		@Override
		public boolean evaluate(StateContext<States, Events> context) {
			Map<Object, Object> variables = context.getExtendedState().getVariables();
			return !(ObjectUtils.nullSafeEquals(variables.get("T1"), true)
					&& ObjectUtils.nullSafeEquals(variables.get("T2"), true)
					&& ObjectUtils.nullSafeEquals(variables.get("T3"), true));
		}
	};
}

@Bean
public Action<States, Events> automaticAction() {
	return new Action<States, Events>() {

		@Override
		public void execute(StateContext<States, Events> context) {
			Map<Object, Object> variables = context.getExtendedState().getVariables();
			if (ObjectUtils.nullSafeEquals(variables.get("T1"), true)
					&& ObjectUtils.nullSafeEquals(variables.get("T2"), true)
					&& ObjectUtils.nullSafeEquals(variables.get("T3"), true)) {
				context.getStateMachine().sendEvent(Events.CONTINUE);
			} else {
				context.getStateMachine().sendEvent(Events.FALLBACK);
			}
		}
	};
}

@Bean
public Action<States, Events> fixAction() {
	return new Action<States, Events>() {

		@Override
		public void execute(StateContext<States, Events> context) {
			Map<Object, Object> variables = context.getExtendedState().getVariables();
			variables.put("T1", true);
			variables.put("T2", true);
			variables.put("T3", true);
			context.getStateMachine().sendEvent(Events.CONTINUE);
		}
	};
}

接着定义我们的Tasks。定义当转换到fork后,随后再由fork到tasks,在tasks内部的三个子转换所对应的执行方法。

package springboot.statemachine.example.task.demo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.annotation.WithStateMachine;

import java.util.HashMap;
import java.util.Map;

/**
 * Created on 2020/2/23.
 */
@WithStateMachine
public class Tasks {
    private final static Log log = LogFactory.getLog(Tasks.class);

    @Autowired
    private StateMachine<States, Events> stateMachine;

    private final Map<String, Boolean> tasks = new HashMap<String, Boolean>();

    public Tasks() {
        tasks.put("T1", true);
        tasks.put("T2", true);
        tasks.put("T3", true);
    }

    public void run() {
        stateMachine.sendEvent(Events.RUN);
    }

    public void fix() {
        tasks.put("T1", true);
        tasks.put("T2", true);
        tasks.put("T3", true);
        stateMachine.sendEvent(Events.FIX);
    }

    public void fail(String task) {
        if (tasks.containsKey(task)) {
            tasks.put(task, false);
        }
    }

    @StatesOnTransition(target = States.T1)
    public void taskT1(ExtendedState extendedState) {
        runTask("T1", extendedState);
    }

    @StatesOnTransition(target = States.T2)
    public void taskT2(ExtendedState extendedState) {
        runTask("T2", extendedState);
    }

    @StatesOnTransition(target = States.T3)
    public void taskT3(ExtendedState extendedState) {
        runTask("T3", extendedState);
    }

    //tag::snippetA[]
    @StatesOnTransition(target = States.AUTOMATIC)
    public void automaticFix(ExtendedState extendedState) {
        Map<Object, Object> variables = extendedState.getVariables();
        variables.put("T1", true);
        tasks.put("T1", true);
    }
//end::snippetA[]

    private void runTask(String task, ExtendedState extendedState) {
        log.info("run task on " + task);
        sleep(2000);
        extendedState.getVariables().put(task, tasks.get(task));
        log.info("run task on " + task + " done");
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
        }
    }

    @Override
    public String toString() {
        return "Tasks " + tasks;
    }
}

最后定义操tasks状态机用到的命令。

@ShellComponent
public class TasksCommands {


    @Autowired
    private Tasks tasks;

    @ShellMethod(key = "tasks run", value = "Run tasks")
    public void run() {
        tasks.run();
    }

    @ShellMethod(key = "tasks list", value = "List tasks")
    public String list() {
        return tasks.toString();
    }

    @ShellMethod(key = "tasks fix", value = "Fix tasks")
    public void fix() {
        tasks.fix();
    }

    @ShellMethod(key = "tasks fail", value = "Fail task")
    public void fail(@ShellOption(value = {"", "task"}, help = "Task id") String task) {
        tasks.fail(task);
    }
}

总结

通过tasks这个demo我们学到了如果通过fork,join实现产生多个状态的功能。此外通过choice实现选择性的状态转换。

参考

[1] tasks原始demo,https://github.com/spring-projects/spring-statemachine/tree/2.2.0.RELEASE/spring-statemachine-samples/tasks/src/main/java/demo/tasks
[2]tasks doc,https://docs.spring.io/spring-statemachine/docs/2.2.0.RELEASE/reference/#statemachine-examples-tasks

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值