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%

🙏 帮我们做得更好

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

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