Hatchet CLI 开发模式启动 Worker 完整指南:hatchet worker dev与hatchet.yaml实战解析
【免费下载链接】hatchet🪓 An orchestration engine for background tasks, AI agents, and durable workflows项目地址: https://gitcode.com/GitHub_Trending/ha/hatchet
本文基于仓库中的官方 Agent 技能文档 start-worker.md 展开,并结合 Hatchet CLI 的实际源码实现(worker.go、pm.go、filewatcher.go 等)进行深度补充。你将掌握:如何通过
hatchet.yaml声明式配置 Worker 的开发运行方式,如何用hatchet worker dev启动一个带热重载能力的 Worker,以及这套机制在底层是如何用进程管理 + 文件监听实现的。
导读
在 Hatchet 中,Worker 是真正执行任务(Task)的进程。日常开发调试时,最关心的问题是:如何快速把本地代码跑成一个可被调度器调度的 Worker,并在改代码后免去手动重启。本文讲解的hatchet worker dev开发模式正是为此设计:它通过项目根目录下的hatchet.yaml声明 Worker 的启动命令、监听文件与热重载行为,让"改代码 → 自动重启 → 立刻验证"成为一条顺畅的开发流水线。读完本文,你将能够独立完成 Worker 开发环境的搭建、配置与故障排查,并理解命令背后进程管理器与文件监听器的完整实现链路。
前提条件:Profile 或嵌入式模式
文档明确假设你已有一个"指向 Hatchet 部署的 Profile"。Profile 是 CLI 连接 Hatchet 实例(Hatchet Cloud 或自托管)的凭证载体,一个 Profile 对应一个 API Token。相关的安装与配置流程见 setup-cli.md:
# 检查是否已安装 hatchet --version # 创建 Profile(HATCHET_PROFILE 为自定义名称,如 local/staging/production) hatchet profile add --name HATCHET_PROFILE --token <API_TOKEN> # 可选:设为默认 Profile,后续命令可省略 -p hatchet profile set-default --name HATCHET_PROFILE关键决策点在于连接模式:
- 本地开发(默认推荐):使用嵌入式模式(embedded mode)。它直接在 Worker 进程内启动一个完整的 Hatchet 引擎,并内置 Postgres,不需要 API Token、账号、Docker 或独立服务器。具体见 local-dev-embedded.md。
- 连接 Hatchet Cloud 或自托管实例:必须使用上述基于 API Token 的 Profile。
这也解释了为什么hatchet worker dev默认会要求指定 Profile——它面向的是"连接已有部署"的场景;纯本地零依赖开发则走嵌入式模式。
创建hatchet.yaml:dev 配置详解
启动开发模式 Worker 前,项目根目录必须存在hatchet.yaml。如果不存在,按以下结构创建:
dev: runCmd: "python src/worker.py" files: - "**/*.py" reload: truedev区块由四个配置项组成,它们与源码中的结构体一一对应(见 config.go):
| 配置项 | 类型 | 说明 | 源码字段 |
|---|---|---|---|
runCmd | string | 启动 Worker 子进程的完整命令 | WorkerDevConfig.RunCmd |
files | []string | 用于文件监听与热重载的 glob 模式列表 | WorkerDevConfig.Files |
reload | bool | 是否在监听文件变化时自动重启 Worker | WorkerDevConfig.Reload |
preCmds | []string | 在 Worker 启动前执行的准备命令 | WorkerDevConfig.PreCmds |
runCmd需要按项目语言与入口调整:
- Python:
poetry run python src/worker.py或python src/worker.py - TypeScript/Node:
npx ts-node src/worker.ts或npm run dev - Go:
go run ./cmd/worker
files中的 glob 模式决定了哪些文件被纳入监听范围;reload: true则开启"监听文件变化 → 自动重启 Worker"的能力。
可选:preCmds 前置命令
如果 Worker 启动前需要安装依赖或执行其他准备动作,可以添加preCmds:
dev: preCmds: - "poetry install" - "npm install" runCmd: "poetry run python src/worker.py" files: - "**/*.py" reload: true按文档描述,这些命令会在每次 Worker 启动时(包括热重载)执行。从源码实现看(详见下文"热重载机制深入"一节),preCmds在RunWorkerDev启动阶段被逐一执行,而热重载路径只重启runCmd子进程——因此实际行为以"首次启动时执行一次、重载时是否重复执行取决于版本实现"为准,建议将幂等的安装类命令放入其中。
补充:triggers顶层配置
除了dev区块,hatchet.yaml的顶层还支持triggers配置(源码中对应WorkerConfig.Triggers),用于声明可触发的命令,每个触发器包含command、可选name与description。日常开发调试通常只需关注dev区块。
配置加载机制:从 YAML 到结构体
配置的解析位于 config.go 的LoadWorkerConfig:CLI 以当前工作目录下的hatchet.yaml为唯一配置源,用 Viper 读取并反序列化到WorkerConfig。也就是说,必须在项目根目录(即hatchet.yaml所在目录)执行hatchet worker dev,否则命令会因找不到配置而直接退出。若文件不存在,LoadWorkerConfig返回 nil,CLI 会渲染workerConfigMissingView提示信息,引导用户用hatchet quickstart生成项目或手工创建配置文件。
启动开发模式 Worker:hatchet worker dev
在后台终端(Worker 是必须持续存活的长驻进程)中执行:
hatchet worker dev -p HATCHET_PROFILE命令会用指定 Profile 连接 Hatchet,然后按hatchet.yaml的dev配置启动 Worker 并开始监听任务。
完整命令与 Flags
dev子命令挂在worker命令族下(hatchet worker dev),相关 Flags 定义于 worker.go:
| Flag | 简写 | 默认值 | 说明 |
|---|---|---|---|
--profile | -p | 默认/唯一 Profile,否则交互式选择 | 指定连接 Hatchet 用的 Profile |
--no-reload | - | false | 关闭文件变化自动重启 |
--run-cmd | -r | 取自hatchet.yaml | 覆盖runCmd,无需改配置文件 |
典型用法:
# 使用默认或唯一 Profile 启动 hatchet worker dev # 指定 Profile hatchet worker dev --profile local # 指定 Profile 并关闭自动重载 hatchet worker dev --profile production --no-reload # 用命令行参数覆盖运行命令 hatchet worker dev --run-cmd "npm run dev"注意 Flags 的优先级:--no-reload与--run-cmd会在加载配置后覆盖hatchet.yaml中的对应值,方便临时调整而无需编辑文件。
无 Profile 时的交互式引导
如果既没传-p也没有可用的默认 Profile,CLI 会弹出交互式表单(handleNoProfiles,见 worker.go),提供三个选项:
- Start a local Hatchet server (requires Docker):启动本地服务器并自动创建 Profile;
- Connect to an existing Hatchet instance with an API token:输入 Token 创建远程 Profile;
- Cancel:取消。
选定后 Worker 会立即以该 Profile 启动。整个worker命令族还包含hatchet worker list与hatchet worker get <worker-id>,分别以 TUI 或-o json形式查看 Worker 列表与详情,便于开发时确认 Worker 是否已注册。
源码级原理:从命令到子进程
hatchet worker dev的执行链路由三条关键路径构成,理解它们能帮你更好地使用这个命令。
第一步:startWorker解析 Profile
startWorker(worker.go)负责:优先使用-p指定的 Profile → 否则进入 Profile 选择/创建流程 → 从cli.Profiles.GetProfile取出 Profile 对象 → 创建可被 Ctrl+C 中断的 Context → 打印启动信息(workerStartingView,会展示所用 Profile 与 Auto-reload 状态)→ 调用RunWorkerDev。
第二步:RunWorkerDev编排 preCmds 与进程
RunWorkerDev(worker.go)按顺序执行:
- 依次执行
devConfig.PreCmds(每条命令打印 "Running pre-command: ..." 后通过pm.Exec同步执行,失败即中止); - 用
pm.NewProcessManager(devConfig.RunCmd, profile)创建进程管理器; - 若
reload: true,调用pm.WatchFiles(ctx, devConfig.Files, proc)进入"监听 + 重启"循环;否则直接proc.StartProcess(ctx)常驻运行,等待ctx.Done()后proc.KillProcess()收尾。
第三步:ProcessManager管理子进程生命周期
ProcessManager(pm.go)是核心的进程抽象,负责启动、停止与重启子进程:
- 命令解析:
StartProcess用shellquote.Split把runCmd拆成 argv,避免引号与空格带来的解析问题,再通过exec.Command启动; - 环境注入:
prepareEnviron会把 Profile 的 Token 以HATCHET_CLIENT_TOKEN=<token>注入子进程环境;当 Profile 的TLSStrategy不是默认的tls时,还会追加HATCHET_CLIENT_TLS_STRATEGY=<strategy>。这正是"Worker 用 Profile 连接 Hatchet"的机制核心——凭证通过环境变量透传给你的 Worker 代码; - 输出透传:子进程的 stdout/stderr 直接连接到 CLI 终端,因此 Worker 的日志会原样显示;
- 非阻塞等待:启动后不阻塞,由独立 goroutine 等待退出并汇报错误。
热重载机制深入
reload: true的热重载不是简单的"轮询文件修改时间",而是一套完整的文件监听实现,位于 filewatcher.go:
- 构建监听器:使用
fsnotify创建底层文件系统事件监听; - 编译 glob 模式:把
files中的模式交给patternmatcher编译(该实现源自 moby 的 patternmatcher,见 patternmatcher.go)。它支持标准的文件通配语法,并额外支持以!开头的排除模式(例如files: ["**/*.go", "!**/vendor/**"]),为精细控制监听范围留出空间; - 递归注册监听:从当前工作目录递归遍历整棵目录树,凡是命中模式(或父目录命中)的目录与文件都被加入 watcher,因此新建文件/目录也能被感知,无需重启;
- 事件驱动重载:收到
Write事件且文件匹配模式 → 触发重载;收到Create事件 → 新文件/新目录匹配时注册监听,文件则同时触发重载。重载信号通过容量为 1 的缓冲 channel 传递(非阻塞写入,天然去抖),收到信号后打印 "Reloading worker..." 并调用pm.StartProcess重启runCmd子进程; - 启动即重载循环:
WatchFiles会先执行一次StartProcess启动 Worker,随后持续监听直到 Context 取消。
启动时会打印Watching pattern: ...、Watching file: ...与Watching N file(s) and directories等日志,方便你确认监听范围是否符合预期。
进程的优雅停止
重启或退出时的进程清理由 process_unix.go 完成:子进程以独立进程组启动(Setpgid: true),停止时先对整个进程组发送SIGTERM优雅退出,等待 3 秒仍无响应则升级为SIGKILL强制终止,确保 Worker 及其所有子进程(比如 Python 的解释器及其派生的协程进程)都被干净回收,不会留下僵尸进程占用端口或任务槽位。
注意事项与最佳实践
Worker 必须先于任务触发运行
Worker 必须在触发工作流之前处于运行状态。如果工作流被触发时没有任何 Worker 在跑,任务会无限期停留在QUEUED状态。开发时若发现任务迟迟不执行,第一反应应是检查后台终端里的 Worker 是否还活着。
热重载的正确打开方式
- 开启
reload: true后,编辑任何被监听的文件(例如一个 Task 函数)都会触发 Worker 自动重启,改完代码立刻生效,无需手动重启; - 需要临时关闭自动重载,加
--no-reload; - 需要临时换启动命令,用
--run-cmd "your command here"覆盖,两个 Flag 都不会改动hatchet.yaml文件本身。
排错清单
Worker 启动失败时,按以下顺序排查:
- Profile 是否存在且指向正确:
hatchet profile list查看;缺失时参考 setup-cli.md 用hatchet profile add创建; - Hatchet 服务器是否可达:可用
hatchet runs list -o json -p HATCHET_PROFILE --since 1h --limit 1验证连通性(返回 JSON 即连接正常,空 rows 也 OK); - 是否在正确的目录执行:
hatchet.yaml必须位于当前工作目录; runCmd是否正确:命令解析失败、可执行文件不存在都会在启动阶段报错,可用--run-cmd快速试错。
快速生成项目骨架
如果连hatchet.yaml都不想手写,hatchet quickstart(quickstart.go)可以根据语言(python/typescript/go)、包管理器(Python 的 poetry/uv/pip,TypeScript 的 npm/pnpm/yarn/bun)与用例模板(如scheduled)直接生成一个带完整hatchet.yaml与示例 Worker 代码的工程,是上手开发模式最快捷的路径。
本地开发的替代方案:嵌入式模式
最后要再次强调:hatchet worker dev -p <profile>面向"连接已有部署"的场景。如果你只是在本机做纯开发,local-dev-embedded.md 推荐的嵌入式模式通常体验更佳——它不需要 Token、Profile 或独立服务器,直接在进程内启动完整引擎:
- Python:
hatchet = Hatchet.from_embedded() - TypeScript:
HatchetEmbeddedClient.init() - Go:
hatchet.NewClient(hatchet.WithEmbedded())(需 blank importhatchet-embedded模块)
嵌入式模式首次运行会下载引擎与内置 Postgres(数十 MB,可能耗时数分钟),之后的运行从缓存秒级启动。两种模式配合使用,可以覆盖从"零依赖本地联调"到"连真实环境"的全部开发场景,而hatchet.yaml+hatchet worker dev的这套配置与进程管理能力,则是你在这两种模式下都绕不开的坚实基础。
【免费下载链接】hatchet🪓 An orchestration engine for background tasks, AI agents, and durable workflows项目地址: https://gitcode.com/GitHub_Trending/ha/hatchet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考