3. 埋点生命周期状态定义

埋点从创建到销毁的完整生命过程由一组离散状态及其合法转移规则描述。每个埋点实例在同一时刻只能处于一个状态,状态变更由外部事件(用户操作、SDK 回调、定时器、配置更新)触发。本章定义六个核心状态——初始化(Initialized)、活跃(Active)、暂停(Paused)、错误(Error)、完成(Completed)、归档(Archived)——并明确每个状态的含义与转换条件。

3.1 状态含义

状态含义典型表现
初始化埋点实例已创建,配置已加载,但尚未开始数据采集。SDK 持有句柄,资源已预分配,等待 start 指令。
活跃正在实时采集并上报数据,遵循采样率、节流策略。事件队列正常消费,网络连接可用。
暂停主动停止采集,但保留上下文,可恢复。不再产生新事件,已缓存数据暂时不发送。
错误运行时检测到不可忽略的故障(网络断连、配置损坏、校验失败)。重试机制生效,或等待修复后手动恢复。
完成采集目标达成(如计时任务到期、事件层级到达上限),正常结束。调用 finish 回调,释放写缓冲。
归档数据已持久化到冷存储,埋点实例可被安全销毁。元数据标记为只读,业务侧不可再激活。
状态设计原则:每个状态都应有明确的进入/退出钩子,便于埋点 SDK 在状态切换时执行资源分配、日志记录、缓存清理等操作。六个状态覆盖了从出生到退役的完整轨迹,避免了“未定义”或“游离”状态。

3.2 状态转换条件

下表列出所有合法转换:

源状态目标状态触发事件/条件备注
初始化活跃调用 start() 且前置校验通过最常规启动路径
活跃暂停调用 pause()用户手动或策略暂停
暂停活跃调用 resume()恢复采集,不丢失上下文
活跃/暂停错误运行时异常(网络超时、数据异常)进入错误状态后自动尝试恢复
错误活跃错误已被清除且调用 recover()手动或自动恢复
活跃/暂停/错误完成采集结束条件达成(时间/事件量阈值)正常终态
完成归档调用 archive() 或系统自动归档数据移至冷存储
初始化完成配置指定无数据采集快速结束路径
初始化/错误归档强制销毁(如应用卸载前清理)跳过中间状态
禁止非法转换:从“活跃”不能直接到“归档”;从“暂停”不能到“完成”除非满足采集终止条件。SDK 内部应实现一个有限状态机,在每次事件回调中校验转换合法性,并记录 INVALID_TRANSITION 日志。

3.3 状态机视觉映射

下图为六个核心状态及其唯九条合法转换路径:

埋点生命周期状态转换图 埋点生命周期状态转换图 初始化 活跃 暂停 错误 完成 归档 start() pause() resume() 异常 异常 recover() 条件达成 条件达成 条件达成 archive() 直接完成 强制归档 实线为常规路径,虚线为特殊或强制路径。所有箭头绘制了埋点生命周期中完整的九种合法转换。

3.4 状态枚举在 SDK 中的定义

实际工程中,状态通常用枚举类型表示,并在状态机中注册钩子:

// 埋点生命周期状态枚举(C++/TypeScript 示意)
enum class TrackState : uint8_t {
    kInitialized = 0,
    kActive      = 1,
    kPaused      = 2,
    kError       = 3,
    kCompleted   = 4,
    kArchived    = 5
};

// 状态机转换表(简化版)
const std::unordered_map<std::pair<TrackState, Event>, TrackState> kTransitionTable = {
    {{TrackState::kInitialized, Event::kStart},   TrackState::kActive},
    {{TrackState::kActive,      Event::kPause},   TrackState::kPaused},
    {{TrackState::kPaused,      Event::kResume},  TrackState::kActive},
    {{TrackState::kActive,      Event::kError},   TrackState::kError},
    {{TrackState::kPaused,      Event::kError},   TrackState::kError},
    {{TrackState::kError,       Event::kRecover}, TrackState::kActive},
    {{TrackState::kActive,      Event::kFinish},  TrackState::kCompleted},
    {{TrackState::kPaused,      Event::kFinish},  TrackState::kCompleted},
    {{TrackState::kError,       Event::kFinish},  TrackState::kCompleted},
    {{TrackState::kCompleted,   Event::kArchive}, TrackState::kArchived},
    {{TrackState::kInitialized, Event::kForceFinish}, TrackState::kCompleted},
    {{TrackState::kInitialized, Event::kForceArchive}, TrackState::kArchived},
    {{TrackState::kError,       Event::kForceArchive}, TrackState::kArchived}
};
设计检查清单:
  • 每个状态是否都有明确的进入钩子和退出钩子?
  • 是否禁止了所有非法转换(如活跃→归档)?是否在日志中记录非法尝试?
  • 初始化状态的默认动作(如资源预分配)是否幂等?
  • “完成”和“归档”是否只允许单向进入,确保数据不可变?
  • 错误状态的重试策略(指数退避、次数上限)是否与状态机解耦?

3.5 状态持久化与恢复

在客户端 SDK 中,埋点实例偶尔会因进程重建或冷启动而丢失实时状态。推荐在关键转换点(进入活跃、暂停、归档)将当前状态写入本地存储(如 SharedPreferences、UserDefaults 或 SQLite)。重启后,加载持久化状态,自动恢复到最近的有效状态,避免重复采集或意外丢失数据。

// 状态持久化示例(JSON 格式)
{
  "track_id": "evt_page_view_3a8f",
  "state": "paused",
  "transition_timestamp": 1719200001,
  "context": {
    "session_id": "abc123",
    "page": "/home"
  }
}
生产建议:不要在每次状态变更时写入磁盘,避免 I/O 抖动。可以采用“定时持久化 + 关键事件立即持久化”策略,并在状态机中提供 syncState() 公开方法供外部调用。