Skip to Content
Ⅰ · 系统骨架02 · 启动与生命周期

02 · 启动与生命周期

ComfyUI 的启动代码有一个很强的约束:设备可见性、路径、补丁和自定义节点的预启动逻辑,必须在真正初始化模型运行时之前完成。 顺序错了,不只是“配置晚生效”,还可能让 CUDA 枚举、内存策略或模块替换永久落在错误状态。

总调用链

main.py module body → apply_custom_paths → execute_prestartup_script → import torch/comfy → DynamicVRAM probe → start_comfyui → PromptServer → init_extra_nodes → database.setup → worker thread + event loop

阶段 0:为什么参数解析最先导入

main.py 开头先打开 comfy.options.enable_args_parsing(),随后才导入 CLI 参数对象。某些参数决定环境变量和设备选择;如果 torch 已经导入并触发设备探测,再设置它们通常已经太晚。

main.pyL1-35参数解析与环境变量准备位于重量级导入之前。

源码还主动检查 torch 是否过早出现在 sys.modules 中并警告。这是一种很实用的“启动不变量”:不是寄希望于贡献者记住隐含顺序,而是在运行时检测违反顺序的导入。

典型的早期参数

参数类别为什么必须早
CUDA device / deterministic影响设备可见性和底层库行为
--base-directory后续所有默认目录都从这里派生
input / output / user / temp服务路由和节点读取都依赖
extra model pathscheckpoint、VAE、LoRA 搜索表需先完整
Windows standalone build包路径和更新器行为可能不同

阶段 1:构造文件系统命名空间

apply_custom_paths() 合并三类来源:基础目录参数、额外模型路径配置 YAML、显式 input/output/user/temp 路径。最终写入 folder_paths 的全局注册表。

main.pyL128-168路径参数与 extra_model_paths.yaml 的应用顺序。

这张注册表不只是 UI 展示模型列表:Checkpoint Loader、上传/查看接口、安全包含关系检查以及 Assets 扫描都依赖它。路径初始化因此是服务和推理的共同前置条件。

冲突时谁覆盖谁

代码按顺序应用配置,显式 CLI 目录最接近最终值。理解这一点比背默认目录更重要:部署环境应把“共享模型盘”和“用户输出盘”作为不同的配置层,而不是在节点里拼绝对路径。

阶段 2:自定义节点的 prestartup

在核心运行时完整导入前,ComfyUI 遍历自定义节点目录,寻找 prestartup_script.py 并直接执行。管理器可以借此安装依赖、调整路径或准备环境。

main.pyL171-225预启动脚本发现、执行、计时与异常记录。

这个机制强大,也意味着明确的信任边界:

  • 脚本与 ComfyUI 进程拥有相同权限;
  • 它发生在普通节点模块加载之前;
  • 单个脚本失败会被记录,不应悄悄吞掉;
  • 禁用全部 custom nodes 时,这一阶段应一起跳过;
  • 自定义节点目录本身就是代码供应链,而不是纯数据目录。

部署在共享服务器时,审核 custom_nodes/ 的重要性不低于审核 Python 依赖锁文件。

阶段 3:导入运行时与选择 Patcher

早期环境就绪后才导入 torch、comfy.utils、execution、server 等模块。随后,启动逻辑尝试探测 comfy_aimdo 支持;满足设备与能力条件时,会把核心使用的 Patcher 类切换为 ModelPatcherDynamic。

main.pyL233-282过早 torch 导入检查,以及 Dynamic VRAM 探测和类替换。

这里不是给每个节点添加 if dynamic_vram。它利用统一构造点替换抽象实现,让后面的 checkpoint 加载仍然通过 CoreModelPatcher。这是“在 composition root 选择策略”的典型做法。

必须保留的版本边界

Dynamic VRAM 是本快照中的条件路径,不代表所有机器都使用它。文档后面讨论 ModelPatcher 时,会先讲共同协议,再分别标出经典和动态实现。

阶段 4:组装应用对象

start_comfyui() 负责把此前的全局配置变成活跃对象:

  1. 清理临时目录并注册退出清理;
  2. 创建新的 asyncio event loop;
  3. 构造 PromptServer(loop);
  4. 初始化 extra/custom/API nodes;
  5. 初始化数据库和路由;
  6. 安装进度回调;
  7. 创建 PromptQueue;
  8. 启动后台 worker thread;
  9. 返回 event loop、server 与启动协程。
main.pyL501-560应用 composition root。

PromptServer.instance、PromptQueue 等对象带有进程级单例味道。优点是节点和回调能方便访问服务;代价是同一 Python 进程内运行多个独立 ComfyUI 实例并非自然用法,测试也必须注意清理全局状态。

阶段 5:节点装载

nodes.init_extra_nodes() 先准备核心节点映射,再扫描内置 extras、自定义目录与 API nodes。服务端的 /object_info 读取最终映射,把后端能力暴露给前端。

节点加载失败通常不会让整个应用立即退出;加载器记录失败模块和导入耗时,服务仍可用。这个选择适合插件生态,但也意味着“服务能打开”不能证明所有预期节点已经成功加载。

阶段 6:数据库

数据库默认 URL 指向绝对路径 user/comfyui.db,也可用 --database-url 替换,甚至指向内存 SQLite。初始化包含文件锁、连接设置和迁移。

comfy/cli_args.pyL263-270默认 SQLite URL 与覆盖参数。

数据库是应用服务的一部分,而不是 GPU 执行的硬依赖抽象。尤其要区分:

  • PromptQueue 的等待项、运行项和 history 是内存结构;
  • 用户、设置和可选 Assets 等使用数据库服务;
  • 数据库迁移失败与单个 Prompt 执行失败属于不同故障域。

阶段 7:两个并发世界

asyncio 世界

主线程事件循环负责 HTTP、WebSocket、异步验证、消息发布和服务生命周期。run() 同时等待服务启动协程与 publish_loop()。

main.pyL427-433服务与消息发布循环并行运行。

工作线程世界

prompt_worker() 在 daemon thread 中循环:

get queue item → restore sensitive extra_data → PromptExecutor.execute → register output assets (optional) → task_done + history → honor unload/free flags → maybe GC / cache cleanup

重推理没有运行在 aiohttp loop 中,因此前端仍能查询状态、取消任务并收到消息。但 Python 节点函数本身若操作共享全局状态,依然需要自己保证线程语义。

进度桥接安装在哪里

启动期通过 comfy.utils.set_progress_bar_global_hook() 安装全局 hook。采样器只需要报告 (value, total, preview);hook 再读取当前节点与 prompt ID,构造 progress JSON 和二进制预览事件。

main.pyL435-464模型层进度到服务层消息的适配器。

这避免了 comfy.samplers 直接依赖 aiohttp 或 Socket。模型代码只理解进度,composition root 负责接线。

正常退出与清理

临时目录通过退出处理注册清理。进程级 daemon worker 会随主进程结束;服务循环异常或键盘中断走外层清理。由于队列和 history 默认在内存,非正常退出不会自动恢复尚未执行的 Prompt。

对于需要可恢复任务的生产部署,这是一项架构边界:应在 ComfyUI 外层增加持久任务编排,而不是把内存 PromptQueue 当 durable queue。

常见启动故障的定位顺序

表现优先检查
指定 GPU 没生效torch 是否过早导入;设备参数是否在进程启动前生效
模型列表为空extra model paths 与 folder_paths 注册,而非 sampler
某一组节点消失节点导入日志、禁用策略、模块依赖、V3 entrypoint
页面能开但 API 被拒origin/CORS 中间件和监听地址
数据库被占用SQLite .lock、是否启动了第二实例、database URL
第一次启动很慢custom node prestartup、节点导入、数据库迁移、Assets 扫描分别计时

设计方法

  1. 把不可逆的全局选择前置。 设备枚举和模块补丁必须发生在第一次使用前。
  2. 用 composition root 汇合副作用。 进度回调、节点映射、DB 和线程都在启动层接线。
  3. 插件失败可降级,但要可观测。 记录失败模块和耗时,不让错误消失。
  4. 把计算与传输放入不同并发域。 GPU 工作线程不直接维护网络连接。
  5. 把外部代码目录视作执行权限。 “自定义节点”绝不只是配置文件。
Last updated on