计算单元
Wippy 提供三种运行代码的方式:函数、进程和工作流。它们共享相同的底层机制,但在生存时间、状态存储位置以及失败时的处理方式上有所不同。
函数
函数是最简单的模型。你调用它们,它们运行,它们返回结果。调用之间不保持状态。
local result = funcs.call("app.math:add", 2, 3)
函数在调用者的上下文中执行。如果调用者取消或退出,任何正在运行的函数也会被取消。这让事情保持简单——你不必考虑清理。
进程
进程是 Actor。它们跨多个消息维护状态,独立于启动它们的人运行,并通过消息传递进行通信。
local pid = process.spawn("app.workers:handler", "app:processes")
process.send(pid, "job", {task = "process_data"})
当你生成一个进程时,即使你的代码完成后它仍会继续运行。进程可以相互监控、链接在一起,并形成自动重启失败子进程的监管树。
调度器在工作线程池中多路复用数千个进程。每个进程在等待 I/O 时让出,让其他进程运行。
工作流
工作流用于绝对不能失败的操作。它们将状态持久化到工作流提供者(Temporal 或其他),可以在崩溃、重启或基础设施变更后从中断处精确恢复。
-- 这可以运行数天,在重启后存活,永不丢失进度
workflow.execute("app.orders:process", order_id)
代价是延迟。每个步骤都会被记录,因此工作流比函数或进程慢。但对于多步骤业务流程或长时间运行的编排,这种持久性是值得的。
比较
| 函数 | 进程 | 工作流 | |
|---|---|---|---|
| 状态 | 无 | 内存中 | 持久化 |
| 生命周期 | 单次调用 | 直到退出或崩溃 | 能存活一切 |
| 通信 | 返回值 + 消息 | 消息传递 | Activity 调用 + 消息 |
| 失败处理 | 调用者处理 | 监管树 | 自动重试 |
| 延迟 | 最低 | 低 | 较高 |
相同代码,不同行为
许多模块会自动适应其上下文。例如,函数中的 time.sleep() 阻塞工作线程,进程中它让出以让其他进程运行,而在工作流中它记录一个在恢复时正确重放的定时器。