4、状态管理(SM)基础:SM的职责与定位、机器状态与函数组状态、状态切换的触发机制

状态管理,简称SM。这玩意儿在Adaptive平台里,说白了就是整个系统的「交通警察」。

我刚开始接触Adaptive平台时,觉得状态管理不就是个状态机嘛,有啥好学的?后来在项目中吃过亏,才明白——状态管理要是没搞对,整个系统就像没头苍蝇一样乱转。

SM的职责与定位

SM的职责,我用一句话概括:它负责管理整个机器的生命周期,以及各个函数组的运行状态

你想想看,一个Adaptive平台里跑着几十个甚至上百个功能。有的功能需要开机就启动,有的需要等网络就绪后才启动,有的出了故障要自动重启。谁来协调这些?就是SM。

SM的定位很明确:

  • 它是系统的「大脑」——决定什么时候该做什么事
  • 它是「总调度」——协调各个函数组的启停顺序
  • 它是「看门狗」——监控状态异常,及时处理

核心要点:SM不负责具体业务逻辑,它只关心「状态」本身。业务逻辑是函数组的事,SM只管「什么时候让谁跑起来」。

我记得有个项目,同事把业务逻辑塞进了SM的状态机里,结果状态切换时还要等业务处理完,整个系统响应慢得像蜗牛。嗯,后来重构时花了两周才拆干净。

机器状态与函数组状态

这里要区分两个概念:机器状态函数组状态。很多人一开始会搞混,包括我自己。

机器状态(Machine State)

机器状态是整个Adaptive平台的全局状态。它定义了系统当前处于什么阶段。

状态 含义 说明
Startup 启动中 系统正在初始化,核心服务逐步启动
Running 运行中 系统正常运行,所有功能可用
Shutdown 关闭中 系统正在有序关闭,释放资源
Restart 重启中 系统需要重启,先关闭再启动
Error 错误状态 系统检测到不可恢复的错误

机器状态是全局的。整个平台同一时刻只能处于一个机器状态。就像一辆车,要么在开,要么在停,不可能同时既开又停。

函数组状态(Function Group State)

函数组状态是每个函数组自己的状态。不同的函数组可以处于不同的状态。

状态 含义 说明
Off 关闭 函数组未运行,资源未分配
Starting 启动中 函数组正在初始化,逐步启动内部进程
Running 运行中 函数组正常运行,提供服务
Stopping 停止中 函数组正在关闭,释放资源
Error 错误 函数组内部出现故障

举个例子:系统处于Running状态时,A函数组可能正在Running,B函数组可能还在Starting,C函数组可能因为故障进入了Error状态。这完全允许。

我的经验:设计函数组状态时,尽量保持状态数量在5个以内。状态太多,状态机复杂度会指数级上升。我曾经见过一个项目搞了12个状态,最后调试时自己都搞不清当前在哪个状态。

状态切换的触发机制

状态不会自己变,得有东西触发它。触发机制主要有三种:

1. 外部请求触发

来自系统管理员的指令,或者上层管理器的命令。比如:

  • 用户点击「关机」按钮 → 触发Shutdown状态
  • OTA升级请求 → 触发Restart状态
  • 诊断工具发送「重启函数组」指令 → 触发函数组重启

2. 内部事件触发

系统内部检测到某些条件满足时自动触发。比如:

  • 所有核心服务启动完成 → 自动从Startup切换到Running
  • 函数组内的所有进程退出 → 自动从Stopping切换到Off
  • 看门狗超时 → 触发Error状态

3. 错误检测触发

系统检测到异常时,SM会主动介入。比如:

  • 函数组崩溃 → SM检测到后,将该函数组置为Error状态
  • 关键服务不可用 → SM可能触发整个机器的Error状态
  • 资源耗尽 → SM触发降级策略,关闭低优先级函数组

避坑指南:我曾经在一个项目中,错误检测触发写得太敏感。网络抖动一下,SM就认为函数组挂了,直接触发重启。结果一上午重启了十几次。后来加了「连续失败3次才触发」的防抖逻辑,才稳定下来。

状态切换的代码实现,通常是一个状态机。我习惯用枚举加状态表的方式:

// 机器状态枚举
enum class MachineState {
    Startup,
    Running,
    Shutdown,
    Restart,
    Error
};

// 状态切换表
struct StateTransition {
    MachineState from;
    MachineState to;
    bool (*condition)(void);  // 切换条件
    void (*action)(void);     // 切换时执行的动作
};

// 示例:从Startup切换到Running
StateTransition transitions[] = {
    {MachineState::Startup, MachineState::Running, 
     allServicesReady, onStartupComplete},
    // ... 其他转换
};

这里要注意:状态切换不是瞬间完成的。从Starting到Running,中间可能涉及启动多个进程、等待它们就绪。所以状态切换通常是一个异步过程。

我个人习惯在状态切换时加一个「中间态」的日志输出。这样调试时能清楚看到:

[SM] Machine state: Startup -> Running (in progress)
[SM] Waiting for service A... OK
[SM] Waiting for service B... OK
[SM] Machine state: Startup -> Running (completed)

嗯,有了这些日志,出问题时定位起来快多了。

最后总结一下:状态管理是Adaptive平台的骨架。机器状态管全局,函数组状态管局部。触发机制要设计得既灵敏又稳定。搞懂了这些,后面学状态切换的具体实现就轻松多了。