进程管理

启动、监控子进程并与之通信。实现带消息传递、监督和生命周期管理的 Actor 模型模式。

全局变量 process 始终可用——无需 require(),也不需要列在 modules: 中。

进程信息

获取当前帧 ID 或进程 ID:

local frame_id = process.id()  -- 调用链标识符
local pid = process.pid()       -- 进程 ID

发送消息

通过 PID 或注册名称向进程发送消息:

local ok, err = process.send(destination, topic, ...)
参数 类型 描述
destination string PID 或注册名称
topic string 主题名称(不能以 @ 开头)
... any 负载值

权限: 目标 PID 上的 process.send

启动进程

-- 基本启动
local pid, err = process.spawn(id, host, ...)

-- 带监控(接收 EXIT 事件)
local pid, err = process.spawn_monitored(id, host, ...)

-- 带链接(异常退出时接收 LINK_DOWN)
local pid, err = process.spawn_linked(id, host, ...)

-- 同时链接和监控
local pid, err = process.spawn_linked_monitored(id, host, ...)
参数 类型 描述
id string 进程源 ID(如 "app.workers:handler")
host string 主机 ID(如 "app:processes")
... any 传递给启动进程的参数

权限:

  • 进程 id 上的 process.spawn
  • 主机 id 上的 process.host
  • 进程 id 上的 process.spawn.monitored(用于监控变体)
  • 进程 id 上的 process.spawn.linked(用于链接变体)

进程控制

-- 强制终止进程
local ok, err = process.terminate(destination)

-- 请求优雅取消,可附带可选原因
local ok, err = process.cancel(destination, "shutting down")
参数 类型 描述
destination string PID 或注册名称
reason string 传递给目标的可选原因

权限: 目标 PID 上的 process.terminate、process.cancel

监控和链接

监控或链接到现有进程:

-- 监控:目标退出时接收 EXIT 事件
local ok, err = process.monitor(destination)
local ok, err = process.unmonitor(destination)

-- 链接:双向,异常退出时接收 LINK_DOWN
local ok, err = process.link(destination)
local ok, err = process.unlink(destination)

权限: 目标 PID 上的 process.monitor、process.unmonitor、process.link、process.unlink

进程选项

local options = process.get_options()
local ok, err = process.set_options({trap_links = true})
字段 类型 描述
trap_links boolean LINK_DOWN 事件是否传递到事件通道
upgradable boolean 选择加入:当进程的代码失效时接收 OUTDATED 事件

收件箱和事件

获取接收消息和生命周期事件的通道:

local inbox = process.inbox()    -- 来自 @inbox 主题的消息对象
local events = process.events()  -- 来自 @events 主题的生命周期事件

事件类型

常量 描述
process.event.CANCEL 请求取消
process.event.EXIT 被监控进程退出
process.event.LINK_DOWN 链接进程异常终止
process.event.OUTDATED 进程的代码或某个导入的依赖在注册表中发生了变更

事件字段

字段 类型 描述
kind string 事件类型常量
from string 源 PID(OUTDATED 时不存在)
result table EXIT/LINK_DOWN 时:一个 {value, error} 记录;进程返回值位于 result.value,错误位于 result.error
reason string CANCEL 时:进程被取消的原因
sources string[] OUTDATED 时:发生变更或受传递性影响的注册表 ID

OUTDATED 仅投递给通过 process.set_options({upgradable = true}) 选择加入的进程;其他进程永远不会看到它。多次失效会合并为一个待处理事件,sources 取并集。预期的响应方式是通过 process.upgrade 进行热替换。

主题订阅

订阅自定义主题:

local ch = process.listen(topic, options)
process.unlisten(ch)
参数 类型 描述
topic string 主题名称(不能以 @ 开头)
options.message boolean 若为 true,接收 Message 对象;若为 false,接收原始负载

消息对象

从收件箱接收或使用 {message = true} 时:

local msg = inbox:receive()

msg:topic()            -- string: 主题名称
msg:from()             -- string: 发送方 PID(未知时为空字符串)
msg:payload()          -- Payload: 包装器(调用 :data() 提取);为空时为 nil,多个值时为包装器组成的表
msg:payload():data()   -- any: 实际负载值

同步调用

启动进程,等待其结果并返回:

local result, err = process.exec(id, host, ...)

权限: 进程 id 上的 process.exec,主机 id 上的 process.host

进程升级

升级当前进程到新定义同时保留 PID:

-- 升级到新版本,传递状态
process.upgrade(id, ...)

-- 保持相同定义,用新状态重新运行
process.upgrade(nil, preserved_state)

上下文启动器

创建带自定义上下文的子进程启动器:

local spawner = process.with_context({request_id = "123"})

权限: "context" 上的 process.context

带选项的启动器

process.with_options(options) 创建一个携带启动时选项(如网络选择器)而非上下文值的启动器:

local spawner = process.with_options({network = "app:tor_proxy"})
选项 类型 描述
network string 用于子进程出站连接的 network.* 条目的注册表 ID
terminal string 将虚拟终端附加到子进程的视口授权

权限: "context" 上的 process.context;选择网络还需要在该网络 ID 上的 network.select。

终端附加

terminal 授权来自 viewport:grant(),它为子进程提供属于自己的终端端口,使其可以像在终端宿主上一样使用 TTY 模块:

local view = assert(tty.viewport({width = 80, height = 24}))
local child = assert(process.with_options({terminal = assert(view:grant())})
    :spawn_monitored("app:child", "app:workers"))

该授权是一次性的,在准入时被消耗:启动被拒绝会使授权保持未解析且可重复使用,解析了端口的子进程会永久消耗它,而不支持终端附加的宿主会拒绝该 spawn,而不是丢弃此选项。发起 spawn 的进程通过它创建的视口继续读取子进程的帧。参见 终端。

SpawnBuilder 方法

SpawnBuilder 是不可变的——每个方法返回新实例:

spawner:with_context(values)      -- 添加上下文值
spawner:with_actor(actor)         -- 设置安全 actor
spawner:with_scope(scope)         -- 设置安全作用域
spawner:with_name(name)           -- 启动时注册名称;若名称已被占用,spawn 返回已有的 PID,排队的消息发往它
spawner:with_message(topic, ...)  -- 排队启动后发送的消息
spawner:with_options(options)     -- 合并启动时选项(如 network)

权限: 对 :with_actor() 和 :with_scope(),需要 "security" 上的 process.security

启动器的 Spawn 方法

spawner:spawn(id, host, ...)
spawner:spawn_monitored(id, host, ...)
spawner:spawn_linked(id, host, ...)
spawner:spawn_linked_monitored(id, host, ...)

与模块级 spawn 函数权限相同。

启动器的 Exec

local result, err = spawner:exec(id, host, ...)

在构建器的上下文、actor 和 scope 下同步运行目标进程,并返回其结果值 — 模块级 process.exec 的受限对应版本。延迟执行的 worker 可以通过 with_actor/with_scope 重建所有者身份并代其执行。

权限: 进程 id 上的 process.exec,主机 id 上的 process.host

名称注册表

以名称注册进程,通过名称而非 PID 访问它。任何接受 destination 参数的函数(send、terminate、cancel、monitor、link 等)都可以接受注册名称代替 PID。

local ok, err = process.registry.register(name)               -- 自身,本地作用域
local pid, err = process.registry.lookup(name)
local ok, err = process.registry.unregister(name)

作用域

可选的 scope 参数选择名称的一致性保障。默认为 LOCAL。四种作用域及其保障在集群指南中有详细说明;简要说明:

常量 可见性 保障
process.registry.LOCAL 仅此节点 即时,节点本地
process.registry.EVENTUAL 集群范围 最终一致(gossip)
process.registry.CONSISTENT 集群范围 线性化单例(Raft)
process.registry.STRONG 集群范围 一致 + 所有存活节点确认

在独立节点上只有 LOCAL 有意义;集群作用域需要启用集群。

register

local ok, err = process.registry.register(name, pid, scope)
参数 类型 必填 默认值 描述
name string 是 要注册的名称
pid string 否 自身 要注册的 PID;默认为调用进程
scope number 否 LOCAL 上述作用域常量之一

成功返回 true,失败返回 nil, error。冲突(名称已注册给不同 PID)返回 errors.ALREADY_EXISTS。将同一名称注册给同一 PID 是幂等的。STRONG 注册会阻塞,直到所有存活节点确认或预留截止时间到期;超时返回错误。

代表不同 PID 注册时,还需要在目标 PID 上的 process.registry.foreign 权限。

lookup

local pid, err = process.registry.lookup(name)

返回已注册的 PID 字符串,或在名称未注册时返回 nil, error(类型为 errors.NOT_FOUND)。

unregister

local ok, err = process.registry.unregister(name, scope)

scope 默认为 LOCAL,必须与注册时使用的作用域匹配。对于 CONSISTENT 和 STRONG,拥有进程才能注销;注销另一个 PID 拥有的名称返回 false。当拥有进程退出时(以及对于集群作用域,当其节点离开时),名称也会自动释放,因此显式注销仅用于提前释放。

权限

权限控制调用进程可以做什么。所有检查使用调用方的安全上下文(actor)针对目标资源。

策略评估

策略可基于以下内容允许/拒绝:

  • Actor: 发起请求的安全主体
  • Action: 正在执行的操作(如 process.send)
  • Resource: 目标(PID、进程 id、主机 id 或名称)
  • Attributes: 附加上下文,包括 pid(调用方的进程 ID)

权限参考

权限 函数 资源
process.spawn spawn*() 进程 id
process.spawn.monitored spawn_monitored()、spawn_linked_monitored() 进程 id
process.spawn.linked spawn_linked()、spawn_linked_monitored() 进程 id
process.host spawn*()、exec() 主机 id
process.send send() 目标 PID
process.exec exec() 进程 id
process.terminate terminate() 目标 PID
process.cancel cancel() 目标 PID
process.monitor monitor() 目标 PID
process.unmonitor unmonitor() 目标 PID
process.link link() 目标 PID
process.unlink unlink() 目标 PID
process.context with_context()、with_options() "context"
process.security :with_actor()、:with_scope() "security"
process.registry.register registry.register() 名称
process.registry.unregister registry.unregister() 名称
process.registry.foreign registry.register() 目标 PID

集群名称作用域通过这些操作的作用域后缀变体授权(process.registry.register.eventual、.consistent、.strong 以及对应的 unregister 操作),策略可以单独授予本地命名和集群范围命名权限。

多重权限

某些操作需要多个权限:

操作 所需权限
spawn() process.spawn + process.host
spawn_monitored() process.spawn + process.spawn.monitored + process.host
spawn_linked() process.spawn + process.spawn.linked + process.host
spawn_linked_monitored() process.spawn + process.spawn.monitored + process.spawn.linked + process.host
exec() process.exec + process.host
使用自定义 actor/scope 启动 spawn 权限 + process.security

错误

条件 类型
未找到上下文 errors.INTERNAL
未找到帧上下文 errors.INTERNAL
缺少必需参数 errors.INVALID
保留主题前缀(@) errors.INVALID
目标既不是 PID 也不是已注册的名称 errors.NOT_FOUND
名称未注册 errors.NOT_FOUND
权限被拒绝 errors.PERMISSION_DENIED
名称已注册 errors.ALREADY_EXISTS

错误处理参见错误处理。

另请参阅