DeepSeek Harness: 插件生命周期:Fiber 状态机

最后更新:2026-08-31

每个插件在 Cordis 中不是一个静态的代码块,而是一个有生命周期的实体——Fiber。理解 Fiber 状态机,就是理解插件从"等待加载"到"运行中"到"优雅退出"的全过程,这是编写健壮插件的关键。

💡 提示:Fiber 的核心价值是"可观测的生命周期"——每个状态转换都有明确的触发条件和可观测的副作用。你永远知道一个插件处于什么状态,以及为什么。

📋 前置知识:已完成 13-effect.md,理解自动清理与 ctx.effect()

1. 你将学到


2. Fiber 概念

(1) 什么是 Fiber

Fiber 是 Cordis 对插件运行时状态的抽象——每个插件实例对应一个 Fiber 对象,记录插件当前所处的生命周期阶段。

TYPESCRIPT
interface Fiber {
  id: string
  name: string
  state: FiberState
  context: Context
  parent: Fiber | null
  children: Fiber[]
}

(2) 为什么需要 Fiber

没有 Fiber 时,插件只有两种状态:"加载"和"未加载"。但实际运行中:

Fiber 为这些中间状态提供了明确的状态机模型。

(3) ▶ 示例 3

TEXT 📖 仅展示
Fiber = 插件的"灵魂"(状态信息)
Context = 插件的"身体"(资源与环境)

每个 Fiber 持有一个 Context,Context 的生命周期与 Fiber 绑定。


3. 生命周期状态

Fiber 状态机

(1) ▶ 示例 1

100%
stateDiagram-v2
    [*] --> pending: 插件注册
    pending --> active: 依赖就绪 + apply 成功
    pending --> errored: apply 失败
    active --> disposing: 请求卸载
    errored --> active: 重试成功
    errored --> disposing: 放弃重试
    disposing --> disposed: 清理完成

(2) 各状态含义

状态 含义 ctx 可用 可逆
pending 等待依赖就绪 有限
active 正常运行 完全
disposing 正在清理 只读
disposed 已销毁 不可用
errored 启动失败 不可用

(3) pending 状态

插件声明了 inject 但依赖尚未注册时,Fiber 进入 pending:

TYPESCRIPT
export const inject = ['tools']

// tools 服务尚未注册 → Fiber 处于 pending
// tools 注册完成 → Fiber 转为 active,调用 apply

pending 期间:

(4) active 状态

apply 成功执行后,Fiber 进入 active:

TYPESCRIPT
export function apply(ctx: Context) {
  // 此时 Fiber 处于 active
  ctx.logger.info('I am alive!')
  
  ctx.on('session/created', (s) => {
    // 可以正常使用所有服务
  })
}

active 期间:

(5) disposing 状态

收到卸载请求后,Fiber 进入 disposing,开始清理资源:

TEXT 📖 仅展示
 disposing 流程:
 1. 停止接受新请求
 2. 逆序执行 ctx.effect() 注册的清理函数
 3. 自动移除事件监听器
 4. 清除定时器
 5. 注销命令和服务

(6) disposed 状态

所有资源清理完毕后,Fiber 进入 disposed:


4. Fiber 的创建与销毁

(1) 创建时机

Fiber 在插件注册时自动创建:

TYPESCRIPT
// 框架内部逻辑(伪代码)
function registerPlugin(plugin: PluginDefinition) {
  const fiber = new Fiber({
    id: generateId(),
    name: plugin.name,
    state: hasDeps(plugin) ? 'pending' : 'active'
  })
  
  if (fiber.state === 'active') {
    fiber.context = createContext(fiber)
    plugin.apply(fiber.context)
  }
}

(2) 销毁触发

Fiber 销毁由以下事件触发:

触发方式 说明
ctx.dispose() 主动卸载
父 Fiber 销毁 级联卸载
配置变更重载 旧 Fiber 销毁,新 Fiber 创建

(3) ▶ 示例 3

100%
graph TD
    TRIGGER[卸载触发] --> STOP[停止新请求]
    STOP --> CHILD[销毁子 Fiber]
    CHILD --> CLEAN[执行清理函数]
    CLEAN --> DISPOSED[标记 disposed]

5. 父子 Fiber 关系

(1) 层级结构

Fiber 支持父子关系,形成树形结构:

100%
graph TD
    ROOT[Root Fiber<br/>dsh-core] --> A[Plugin A Fiber]
    ROOT --> B[Plugin B Fiber]
    A --> A1[Sub-plugin A1]
    A --> A2[Sub-plugin A2]

(2) 父子关系的建立

通过 ctx.plugin() 创建子 Fiber:

TYPESCRIPT
export function apply(ctx: Context) {
  ctx.plugin({
    name: 'sub-plugin',
    apply(subCtx: Context) {
      subCtx.logger.info('I am a child fiber')
    }
  })
}

(3) 级联销毁

父 Fiber 销毁时,所有子 Fiber 自动销毁:

TEXT 📖 仅展示
卸载 Plugin A:
  → 先销毁 Sub-plugin A1
  → 再销毁 Sub-plugin A2
  → 最后销毁 Plugin A

这种"先子后父"的销毁顺序确保依赖关系不被破坏。

(4) 作用域继承

子 Fiber 继承父 Fiber 的 ctx:

TYPESCRIPT
// 父插件注册的服务,子插件可以直接使用
export function apply(ctx: Context) {
  ctx.provide('parent-service', { ... })
  
  ctx.plugin({
    name: 'child',
    inject: ['parent-service'],
    apply(childCtx) {
      childCtx['parent-service'] // ✅ 可访问父级服务
    }
  })
}

6. 错误处理与状态回退

(1) apply 失败

当 apply 抛出异常时,Fiber 进入 errored 状态:

TYPESCRIPT
export function apply(ctx: Context) {
  throw new Error('initialization failed')
  // Fiber → errored
}

(2) 自动重试

Cordis 会对 errored 状态的 Fiber 自动重试:

TEXT 📖 仅展示
1st attempt: apply() → throw Error → errored
2nd attempt: (等待 1s) apply() → throw Error → errored
3rd attempt: (等待 2s) apply() → throw Error → errored
4th attempt: (等待 4s) apply() → success → active

重试间隔指数退避:1s → 2s → 4s → 8s → ... → 最大 60s。

(3) 重试策略配置

TYPESCRIPT
export const Config = Schema.object({
  maxRetries: Schema.number().default(5).description('Max retry attempts'),
  retryInterval: Schema.number().default(1000).description('Initial retry interval (ms)')
})

(4) 手动重试

TYPESCRIPT
ctx.on('fiber/errored', (fiber) => {
  ctx.logger.warn(`plugin ${fiber.name} errored, retrying...`)
  fiber.retry()
})

(5) 不可恢复的错误

某些错误不应重试:

TYPESCRIPT
export function apply(ctx: Context) {
  if (!process.env.REQUIRED_VAR) {
    // 配置错误,重试也没用
    throw new NonRetryableError('REQUIRED_VAR is not set')
  }
}

(6) 清理阶段的错误

disposing 阶段的错误不会阻止 Fiber 转为 disposed,但会被记录:

TEXT 📖 仅展示
[warn] cleanup error in plugin my-plugin: Connection already closed
[info] plugin my-plugin disposed (with 1 cleanup warnings)

7. Fiber 上下文隔离

(1) 每个 Fiber 独立 ctx

TYPESCRIPT
const fiberA = new Fiber({ name: 'plugin-a' })
const fiberB = new Fiber({ name: 'plugin-b' })

fiberA.context !== fiberB.context  // true

(2) 隔离边界

资源 是否隔离 说明
事件监听器 各 Fiber 独立注册
定时器 各 Fiber 独立清理
命令 ⚠️ 全局共享,但有作用域
服务 全局共享
配置 各插件独立

(3) 服务共享与隔离的平衡

服务是全局共享的——这是 Cordis 的设计决策。插件 A 注册的服务,插件 B 可以通过 inject 使用。如果需要隔离,使用 scope 机制(见 20-scope.md)。

(4) 状态查询

TYPESCRIPT
// 查询 Fiber 状态
ctx.fiber.state           // 'active'
ctx.fiber.id              // 'fiber-abc-123'
ctx.fiber.parent          // parent Fiber or null
ctx.fiber.children        // child Fiber[]

❓ 常见问题

Q Fiber 和 Thread 有什么关系?
A 没有关系。Fiber 不是操作系统线程,而是 Cordis 的逻辑状态单元。所有 Fiber 在同一个 Node.js 事件循环中运行。
Q 可以手动创建 Fiber 吗?
A 不推荐直接创建。通过 ctx.plugin() 注册子插件时,框架自动创建 Fiber。
Q errored 状态的插件会占用资源吗?
A 不会。errored 状态的 Fiber 不会持有 ctx 资源,只有 Fiber 对象本身(极小内存)保留用于状态追踪。
Q 父 Fiber 销毁后,子 Fiber 还能存活吗?
A 不能。父子关系是强依赖——父销毁时子一定销毁。如果需要独立生命周期,不要建立父子关系。
Q 如何监控所有 Fiber 的状态?
A 监听 Fiber 生命周期事件: typescript ctx.on('fiber/created', (fiber) => { ... }) ctx.on('fiber/active', (fiber) => { ... }) ctx.on('fiber/disposing', (fiber) => { ... }) ctx.on('fiber/disposed', (fiber) => { ... }) ctx.on('fiber/errored', (fiber) => { ... })
Q 配置变更导致 Fiber 重建,旧的清理逻辑会执行吗?
A 会。配置变更时,旧 Fiber 走完整的 disposing → disposed 流程,所有清理函数都会执行。然后新 Fiber 创建并进入 pending/active。 ---

📖 小节


📝 作业

1. ⭐ 基础题:编写一个插件,在 apply 中输出当前 Fiber 的 state 和 id。启动后观察日志,确认 Fiber 处于 active 状态。

2. ⭐⭐ 进阶题:编写一个插件,在 apply 中故意抛出错误(模拟初始化失败)。观察 Fiber 的 errored 状态和重试行为。然后修正错误,验证 Fiber 恢复到 active。

3. ⭐⭐⭐ 挑战题:创建一个父子 Fiber 结构:父插件注册一个服务,子插件通过 inject 使用该服务。卸载父插件,验证子插件也被级联销毁。在清理函数中输出销毁顺序,确认"先子后父"。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏