分布式任务调度器中的状态机设计详解

DSL简介

DSL是一种工具,它的核心价值在于,它提供了一种手段,可以更加清晰地就系统某部分的意图进行沟通。

这种清晰并非只是审美追求。一段代码越容易看懂,就越容易发现错误,也就越容易对系统进行修改。因此,我们鼓励变量名要有意义,文档要写清楚,代码结构要写清晰。基于同样的理由,我们应该也鼓励采用DSL。

按照类型,DSL可以分为三类:内部DSL(Internal DSL)、外部DSL(External DSL)、以及语言工作台(Language Workbench)。

Internal DSL是一种通用语言的特定用法。用内部DSL写成的脚本是一段合法的程序,但是它具有特定的风格,而且只用到了语言的一部分特性,用于处理整个系统一个小方面的问题。 用这种DSL写出的程序有一种自定义语言的风格,与其所使用的宿主语言有所区别。例如我们的状态机就是Internal DSL,它不支持脚本配置,使用的时候还是Java语言,但并不妨碍它也是DSL。

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

状态机领域模型:

鉴于我们的诉求是实现一个仅支持简单状态流转的状态机,该状态机的核心概念如下图所示,主要包括:

  • State:状态

  • Event:事件,状态由事件触发,引起变化

  • Transition:流转,表示从一个状态到另一个状态

  • External Transition:外部流转,两个不同状态之间的流转

  • Internal Transition:内部流转,同一个状态之间的流转

  • Condition:条件,表示是否允许到达某个状态

  • Action/process:动作,到达某个状态之后,可以做什么

  • StateMachine:状态机

image.png

状态机实现:

根据上述的状态机整个核心语义模型,调度器实现的部分核心代码如下所示:

public class TaskStateMachineFactory {

    private static final Map<TaskStatus, Map<String, TaskProcessor>> statusMachineMaps = new ConcurrentHashMap<>();

    static {
        // define task status transition relations
        addTransition(TaskStatus.none, "preparing", new TaskPreparingProcessor());
        addTransition(TaskStatus.none, "recovering", new TaskRecoveringProcessor());

        addTransition(TaskStatus.preparing, "running", new TaskRunningProcessor());
        addTransition(TaskStatus.preparing, "failed", new TaskPreparingFailedProcessor());
        addTransition(TaskStatus.preparing, "disabled", new TaskDisabledProcessor());
        addTransition(TaskStatus.preparing, "skipped", new TaskSkippedProcessor());
        addTransition(TaskStatus.preparing, "kill", new TaskPreparingKillProcessor());
        addTransition(TaskStatus.preparing, "patching", new TaskPatchingProcessor());

        addTransition(TaskStatus.running, "finished", new TaskFinishedProcessor());
        addTransition(TaskStatus.running, "failed", new TaskFailedProcessor());
        addTransition(TaskStatus.running, "decommission", new TaskDecommissionedProcessor());
        addTransition(TaskStatus.running, "kill", new TaskKillProcessor());
        addTransition(TaskStatus.running, "failed_succeeded", new TaskFailedSucceededProcessor());
        addTransition(TaskStatus.running, "lost", new TaskLostProcessor());

        addTransition(TaskStatus.recovering, "running", new TaskRunningProcessor());
        addTransition(TaskStatus.recovering, "failed", new TaskFailedProcessor());
        addTransition(TaskStatus.recovering, "disabled", new TaskDisabledProcessor());
        addTransition(TaskStatus.recovering, "skipped", new TaskSkippedProcessor());
        addTransition(TaskStatus.recovering, "kill", new TaskKillProcessor());
        addTransition(TaskStatus.recovering, "patching", new TaskPatchingProcessor());

        addTransition(TaskStatus.finished, "zombie", new TaskZombieProcessor());
        addTransition(TaskStatus.failed, "zombie", new TaskZombieProcessor());
        addTransition(TaskStatus.decommissioned, "zombie", new TaskZombieProcessor());
        addTransition(TaskStatus.killed, "zombie", new TaskZombieProcessor());
        addTransition(TaskStatus.failed_succeeded, "zombie", new TaskZombieProcessor());
        addTransition(TaskStatus.lost, "zombie", new TaskZombieProcessor());
    }

    /**
     * add state transition processor
     *
     * @param preStatus
     * @param eventName
     * @param processor
     */
    public static void addTransition(TaskStatus preStatus, String eventName, TaskProcessor processor) {
        if (statusMachineMaps.get(preStatus) != null) {
            statusMachineMaps.get(preStatus).put(eventName, processor);
        } else {
            Map<String, TaskProcessor> eventMap = new ConcurrentHashMap<>();
            eventMap.put(eventName, processor);
            statusMachineMaps.put(preStatus, eventMap);
        }
    }

      /**
     * handle task event with proper processor and transfer task state
     *
     * @param preStatus
     * @param taskEventInfo
     */
    public static TaskEventResult doTransition(TaskStatus preStatus, TaskEventInfo taskEventInfo) throws NaviException {
        Map<String, TaskProcessor> eventMap = statusMachineMaps.get(preStatus);
        TaskEvent event = taskEventInfo.getTaskEvent();
        if (eventMap == null || eventMap.get(event.getEventName()) == null) {
            TaskEventResult result = new TaskEventResult();
            result.setProcessInfo(
                    "[ERROR] Can not process event " + event.getEventName() + " when task status is " + preStatus
                            .toString());
            result.setSuccess(false);
            return result;
        }
        TaskProcessor processor = eventMap.get(event.getEventName());
        return processor.process(taskEventInfo);
    }
}

注意:其中XXXProcessor即为到达某个状态后需要执行的处理器方法,TaskStatus定义了所有任务状态,EventName为触发的事件名称,addTransition方法定义了所有状态之间的流转关系,doTransition方法明确了如何处理正确的状态以及流转至下一状态的过程。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值