运维运行时语义
本文档包含 Shield 运维和调试相关的运行时语义决策。
当前状态:
shield_ops是 official optional module 契约。- 它不提供业务 Lua API,也不属于最小运行路径。
- 横向 owner、配置归属和 disabled 语义见 官方可选模块契约。
设计目标
shield_ops 不给游戏业务提供新 API,而是让运行时在不侵入核心语义的前提下,可被观察、诊断、采样和控制。
- 观察运行时内部状态,而不暴露 CAF 细节给业务脚本。
- 在开发、测试、预发环境提供足够的诊断信息。
- 在生产环境提供受控、低开销、可鉴权的运维入口。
- 所有 ops 能力都应可关闭,不影响
shield_core启动。
典型能力:
| 能力 | 说明 |
|---|---|
| Metrics | 计数器、直方图、gauge,用于吞吐、延迟、错误率 |
| Diagnostics | 服务列表、actor 数量、pending call、timer、Lua VM 池、连接数 |
| Console | 交互式调试控制台,查询、调用、列服务、看状态 |
| Profile | 采样式热点分析、消息延迟剖面、慢调用追踪 |
| Health | 进程存活、关键模块就绪、资源状态 |
Lua 诊断控制台与 Lua 内存观测是 Console / Diagnostics 的专项能力,单独设计见 Lua 诊断控制台设计。
可观测性现状与非目标
当前产品只保留日志模块(控制台日志;文件日志作为 shield_log 的可选 sink,默认关闭;Lua 目标 API shield.log.info/warn/error/debug)。日志不是 shield_core 语义的一部分。
Core 不内建:Prometheus metrics、HealthCheckRegistry、/metrics 端点、配置热重载监控器。这些归入 shield_ops 官方可选模块或独立扩展,且不能反向污染 runtime core。
已落地的运维面(实现于 shield_bootstrap 的 console 组件,http.enabled: true 时生效):
- Unix socket 诊断控制台(
root.*/ Lua 命令,见 Lua 诊断控制台设计) - HTTP ops 端点:
/ops/health、/ops/status、/ops/metrics、/ops/services、/ops/plugins、/ops/config、/ops/eval - Lua 业务侧
shield.httpd.*可注册自有管理端点(见 Lua API 契约)
HTTP ops 服务端安全基线:
http.host默认127.0.0.1(环回);对外暴露必须显式配置并在前置层(防火墙/反代)再加控制。/ops/eval为远程代码入口,默认不注册(请求 404)。启用需同时满足:http.eval_enabled: truehttp.eval_token: <非空 token>,否则启动时报错且路由不注册
- 已启用的
/ops/eval要求Authorization: Bearer <token>(常量时间比较),未授权请求返回 401。 - eval 代码运行在受限 VM 中:
os.execute/os.exit/os.getenv/os.remove/os.rename/os.setlocale、io库、require/package均被移除(os.time/os.date/os.clock保留)。
注意:这些能力当前编译在 shield_bootstrap 而非 shield_ops 空壳 target 内;模块归属对齐是后续工作。轻量 /ops/health 探针(进程内读取、不经 Lua actor 往返,actor 网格卡死时仍可应答)、/ops/metrics Prometheus 导出与 /ops/services/:name 服务详情(含流量/uptime/timers/pending_calls/pending_tasks/coroutines/memory_kb 统计)已提供(P0);console 侧 L2 受限 inspect(lua.inspect/lua.snapshot/lua.diff)已提供;/ops/profile(采样式热点分析 + 慢调用追踪)已提供,见下文 Profile;消息延迟剖面另行立项评估。
shield_ops 默认策略
shield_ops 不属于 core,但作为官方可选模块保留。
默认策略:
- 开发环境可本地启用。
- 生产环境默认关闭远程控制入口。
- metrics 可以独立启用。
- profile 必须显式启用。
- console 必须显式启用。
- HTTP 只用于管理入口,不作为业务 HTTP server。
core 只提供可读取的 runtime snapshot 和 counters,不反向依赖 shield_ops。
Public Surface
shield_ops 的 public surface 只有管理入口:
HTTP debug endpoints
local admin socket
console
metrics exporter
profile controls它不定义 shield.ops.* 业务 Lua API。 它也不提供业务 REST router、middleware chain 或 Web framework 集成。
运维端点
shield_ops 在显式启用 HTTP 管理入口时暴露以下端点:
| 端点 | 方法 | 说明 |
|---|---|---|
/ops/health | GET | 健康检查(已提供) |
/ops/status | GET | 运行时状态 |
/ops/metrics | GET | 指标导出(Prometheus 格式,已提供) |
/ops/services | GET | 服务列表 |
/ops/services/:name | GET | 服务详情(已提供) |
/ops/plugins | GET | 插件实例列表(id/package/state/required,已提供) |
/ops/config | GET | 单键查询(?key=<key>),非全量快照;不带 key 时返回提示文本 "Use ?key=<key> to query",键不存在返回 null |
/ops/eval | POST | 受限 VM 远程代码执行;opt-in——需 http.eval_enabled: true 且非空 http.eval_token,否则路由不注册(请求 404),见上文安全基线 |
/ops/profile | POST | 采样式热点分析与慢调用追踪的启动/停止/查询(已提供,见 Profile) |
如果启用了 Lua 诊断控制台,管理面还可以额外暴露只读 Lua 观测能力,例如:
| 能力 | 形态 | 说明 |
|---|---|---|
lua.inspect <service> summary | console | 查看 service 对应 Lua VM 的摘要 |
lua.snapshot <service> | console / HTTP | 记录 Lua 内存摘要快照 |
lua.diff <service> <a> <b> | console / HTTP | 比较两次 Lua 快照 |
任意 Lua 执行不是默认能力;如后续支持 lua.eval,也必须显式启用并默认仅限本地入口。详见 Lua 诊断控制台设计。
健康检查
GET /ops/healthP0 实现为轻量探针:全部读取在 HTTP 线程内进程内完成,不经 Lua actor 往返,因此 actor 网格卡死时探针仍可应答。verdict 同时映射到 HTTP 状态码:ok → 200,degraded → 503(body 信封不变,负载均衡/编排器可以只看状态码做门禁,探针仍可读 body 里的 per-check 明细);unhealthy 档位保留给后续(如 actor 网格失联检测)。
响应(与其他 ops 端点一致的 type/data 信封):
{
"type": "result",
"data": {
"status": "ok", // ok | degraded;任一 check 非 ok 即 degraded
"uptime": 3600.5,
"checks": {
"core": { "status": "ok", "uptime_seconds": 3600.5 },
"plugins": { "status": "ok", "started": 2, "required_down": 0 }
}
}
}checks 按编译开关与运行时状态裁剪:
core:恒存在;uptime 秒数。plugins:恒存在;required 实例未达started即 degraded(optional 实例缺失不影响)。server(SERVER=ON且 ServerManager 已初始化):仅running计为 ok,starting/maintenance/shutdown均 degraded。cluster(CLUSTER=ON且 ClusterManager 已初始化):有 Offline/Removed 节点即 degraded。global(GLOBAL=ON且 GlobalManager 已初始化):manager 存在即 ok。
与 server/cluster 一致的裁剪约定:模块已编译但本节点未启用该角色(manager 不存在)时省略该 check,而非 degrade——global 节点角色是部署形态,不是健康缺陷。database/redis 等依赖探活留后续。
运行时状态
GET /ops/status响应(实际 payload):
{
"services": ["gateway", "room_1"],
"plugins": [
{ "id": "db.default", "package": "sqlite", "state": "started", "required": true }
],
"cluster": {
"node_id": "node-1",
"node_epoch": "1",
"nodes": [
{ "node_id": "node-2", "address": "10.0.0.2:9000", "state": "online",
"epoch": "1", "last_heartbeat_ms": 123456, "heartbeat_age_ms": 1200 }
]
},
"server": {
"state": "running",
"uptime_seconds": 3600.5,
"name": "s1",
"watchers": 0,
"shutdown_scheduled": false
},
"global": {
"data": { "keys": 42 },
"cache": { "size": 10, "hits": 100, "misses": 3, "hit_rate": 0.97 },
"queues": { "normal": 0, "delay": 0, "priority": 0, "broadcast": 0, "reliable": 0 }
}
}字段口径:
services:已发布服务名的字符串数组(经 actor 网格的 forked task,2 秒超时时整字段置为字符串"timeout",HTTP 状态仍为 200)。plugins:插件实例数组,每项含id/package/state/required。cluster/server/global为条件性块:分别在CLUSTER=ON且 ClusterManager 已初始化、SERVER=ON且 ServerManager 已初始化、GLOBAL=ON且 GlobalManager 存在(global 快照非null)时才出现。- 无
app/runtime/resources块;进程 uptime 等信息在/ops/health与/ops/metrics中。
指标导出
GET /ops/metricsP0 导出 Prometheus 0.0.4 文本格式(Content-Type: text/plain; version=0.0.4; charset=utf-8,HELP/TYPE 头齐全,标签值转义 \ " \n)。进程内读取为主,唯一经 actor 网格的 shield_services 在 500ms 超时时省略——网格卡死不会拖死抓取。
按编译开关与运行时状态裁剪的指标族:
| 指标 | 类型 | 说明 |
|---|---|---|
shield_uptime_seconds | gauge | 进程 uptime |
shield_plugin_instances{state} | gauge | 插件实例按生命周期状态(planned/loaded/started/unavailable/failed/stopped) |
shield_plugin_pool_size / shield_plugin_pool_in_use / shield_plugin_pool_idle | gauge | 插件连接池快照,标签 plugin/instance(shield.pool.stats.v1 采集,见 Plugin Pool Stats);报 -1(无池/未知)的字段不导出 |
shield_plugin_db_pending_async / shield_plugin_db_holding | gauge | 插件 DB 异步入口的在途调用数 / 打开事务持有连接数,标签同上;-1 不导出 |
shield_gateway_connections_total{port} | counter | 完成 TCP accept 的连接数(含随后被黑名单/限连拒绝的) |
shield_gateway_rejections_total{port,reason} | counter | accept 阶段拒绝数,reason ∈ blocked_ip / connection_limit / ip_limit |
shield_gateway_active_sessions{port} | gauge | 网关活跃会话数(瞬时值) |
shield_gateway_rate_limited_messages_total{port} | counter | 被每连接 ingress 限流丢弃的消息数(listener 级累计,会话退出不影响单调性) |
shield_services | gauge | 已注册 Lua 服务数(actor 往返,500ms 超时省略) |
shield_service_requests_total{service} | counter | 服务收到的消息数(send/call/system 合流,按服务本轮生命周期累计,respawn 归零) |
shield_service_errors_total{service} | counter | 服务 handler 失败数(口径同上) |
shield_service_uptime_seconds{service} | gauge | 服务本轮生命周期存活秒数(自 publish 起单调计时,respawn 重计) |
shield_service_timers{service} | gauge | 服务当前活跃 actor timer 数 |
shield_service_pending_calls{service} | gauge | 以该服务名义挂起在协程 call 上的条目数(caller 侧瞬时值) |
shield_service_pending_tasks{service} | gauge | 已入队但尚未被其 actor 取走的 fork 任务数(瞬时值) |
shield_service_coroutines{service} | gauge | 该服务当前挂起或活跃的 handler coroutine 数(工厂启动登记、终态 resume/teardown 擦除的瞬时值) |
shield_service_memory_kb{service} | gauge | 该服务 Lua VM 堆占用 KB(owner 线程在每次 dispatch 退出采样的 Lua allocator 视角,非进程 RSS) |
shield_server_state{state} | gauge | Server 状态机(SERVER=ON 且 manager 存在) |
shield_global_data_keys / shield_global_cache_entries | gauge | global 数据键 / 本地缓存条目(GLOBAL=ON 且 manager 存在) |
shield_global_cache_hits_total / shield_global_cache_misses_total | counter | 缓存命中/未命中 |
shield_global_queues{type} | gauge | 队列数(normal/delay/priority/broadcast/reliable) |
shield_global_locks{kind} | gauge | 存活锁(mutex/spinlock/rwlock) |
shield_global_rank_boards / shield_global_rank_members | gauge | 排行榜板数 / 成员总数 |
shield_global_scheduler_tasks / shield_global_scheduler_active | gauge | 调度任务总数 / 活跃数 |
shield_cluster_nodes{state} | gauge | 集群节点按状态(CLUSTER=ON 且 manager 存在) |
shield_cluster_transport_connections / ..._reconnects_total | gauge/counter | 传输连接数 / 重连次数(transport 存在) |
shield_cluster_transport_messages_total{direction} | counter | 收发消息数(tx/rx) |
shield_cluster_transport_heartbeats_total{direction} | counter | 收发心跳数(tx/rx) |
shield_requests_total、shield_request_duration_seconds 等聚合请求量/时延指标不在当前范围,留后续。
抓取口径(granularity):
- 单进程口径。所有指标只描述被抓取的这个 shield 进程;多节点聚合由 外部监控系统(Prometheus federation / remote write)负责,运行时不提供 跨进程聚合。
- gauge 是抓取时刻的瞬时采样:
pending_calls/pending_tasks/coroutines/timers/memory_kb/ global 族 / cluster 连接数都是 读取瞬间的值,不做窗口平均;短促尖峰可能被稀疏抓取漏掉,告警阈值应基于 连续多个样本。 - counter 单调(进程生命周期内不减、不重置):
requests_total/errors_total/ cache hits/misses / cluster reconnects / tx/rx 计数。 注意服务级 counter 的归属是"服务本轮生命周期"——respawn 后同名服务从 0 重新累计,rate 计算要容忍断点(配合shield_service_uptime_seconds的重计可识别 respawn 边界)。 shield_services是唯一经 actor 网格的族:500ms 超时即整族省略 (其余族进程内直读,永远在场);抓取方应把该族的缺失视为"网格忙/卡" 信号而不是抓取失败。- 抓取间隔建议 ≥5s:内存采样点在 owner 线程 dispatch 退出(非抓取 时刻实时),更密的抓取只会重复读到同一采样值。
服务列表
GET /ops/services当前实现返回 type/data 信封,data 为已发布服务名的字符串数组(与 list_services() 一致;on_init 中与已退出的名字不出现)。带统计字段的列表条目形态留后续——per-service 统计现可经 /ops/services/:name 与 /ops/metrics 获取。
服务详情
GET /ops/services/:nameP0 实测口径:注册表内只读快照,走 ""-id forked task(与 /ops/services 列表同路),2s 超时返回 504。未发布的名字(on_init 中、已退出)与列表口径一致按不存在处理,返回 404。
响应:
{
"type": "result",
"data": {
"name": "gateway",
"state": "running",
"script": "/path/to/gateway.lua",
"rpc_routes": 2,
"requests": 1234,
"errors": 2,
"uptime_seconds": 3600.5,
"timers": 1,
"pending_calls": 0,
"pending_tasks": 0,
"coroutines": 0,
"memory_kb": 2048
}
}requests/errors 为该服务本轮生命周期的累计流量(spawn 归零、exit 移除);uptime_seconds 自 publish 起单调计时(respawn 重计);timers 为该服务当前活跃 actor timer 数;pending_calls 为以该服务名义挂起在协程 call 上的条目数(caller 侧瞬时值);pending_tasks 为已入队但尚未被其 actor 取走的 fork 任务数(瞬时值);coroutines 为该服务当前挂起或活跃的 handler coroutine 数(工厂启动时登记、终态 resume 或 teardown 时擦除);memory_kb 为该服务 Lua VM 堆占用 KB——owner 线程在 dispatch 退出采样 lua_gc(GCCOUNT)(O(1)),语义为采样值而非实时读取(绝不跨线程触碰 lua_State),Lua allocator 视角、非进程 RSS;script 仅配置态 runtime actor 有记录,spawn 服务可缺省。
Profile
POST /ops/profile {"action": "status" | "start" | "stop" | "report", ...}采样式热点分析与慢调用追踪共用这一个入口,两者独立工作:采样会话回答"时间花在服务内哪里",慢调用环形缓冲回答"哪些跨服务调用慢"。需要 http.profile_enabled=true + 非空 http.profile_token 才注册路由(Bearer 令牌鉴权,时序安全比较);未启用时端点 404。
动作语义:
status:纯注册表读,总是应答。active/service/elapsed_ms/duration_ms描述采样会话;slow_calls段始终附带。start:service必填,duration_ms(1..60000,默认见下)与interval(>=1)可选。单会话管理器 +http.profile_cooldown_seconds(默认 10s)双重限流;409=会话已在跑,429=冷却中,404=服务不存在。stop/report:结束会话并返回聚合树(report是stop的只读命名别名)。慢调用计量与采样会话解耦:没有活跃采样会话时report仍以active=false+slow_calls段应答,而不是 409。- 聚合结果包含树形命中计数(模块/函数/行)与耗时分布;无 payload、无密钥数据。
slow_calls 段(status/report 均附带):
{
"recent": [
{"caller": "auth", "callee": "db", "elapsed_ms": 42, "ok": true, "at": 1727000000}
],
"total_recorded": 3
}recent 为最近样本、新者在前、至多 16 条;进程级环形缓冲容量 64,超出淘汰最旧;total_recorded 累计计数(不受环形覆盖影响)。elapsed_ms 从 call 挂起计到 resume 回来,ok=false 表示调用以失败收场(非超时路径);at 为记录时刻的 unix 秒。
配置键:
| 键 | 默认 | 说明 |
|---|---|---|
http.profile_enabled | false | 注册 /ops/profile 路由的总开关 |
http.profile_token | 空 | Bearer 令牌;enabled 但为空则拒绝注册 |
http.profile_cooldown_seconds | 10 | 两次 start 之间的最小间隔 |
http.slow_call_threshold_ms | 0 | 慢调用阈值;0=不计量。随端点构造置位,进程内不回收(sticky)——置位前已挂起的调用不计入,避免半程样本 |
开销边界:gate 关闭时调用热路径只付一次 relaxed 原子读;gate 开启时每次跨服务调用在挂起/完成各记一个时间戳,仅超过阈值的完成落入互斥锁保护的环形缓冲。采样器开销为按 interval 触发的 Lua debug hook(见 docs/superpowers/plans/2026-09-22-ops-profile-v1.md)。
ops 安全
ops 暴露必须遵守:
- 默认绑定 localhost 或 local admin socket。
- 远程访问必须鉴权。
- 不默认输出完整 payload。
- 对敏感字段做 redaction。
- 控制类 API 需要单独权限。
- profile 和 dump 需要速率限制。
允许暴露:
service list
registry names
pending calls
timer count
coroutine count
node heartbeat status
slow call summary禁止默认暴露:
CAF actor handle
完整业务 payload
密钥、token、密码
未鉴权远程 console配置示例
ops: 配置段未实现:bootstrap 硬编码 ops_enabled = false,配置中出现任何 ops: 段都会在运行时校验阶段失败并拒绝启动(optional module config 'ops' requires shield_ops),全仓也没有任何 ops.* 配置读取者。实际运维面由 http: 与 console: 键门控(实现于 shield_bootstrap):
http:
enabled: true # 默认 false;总开关
host: "127.0.0.1" # 默认 127.0.0.1(环回)
port: 8080 # 默认 8080
eval_enabled: false # 默认 false;/ops/eval 注册开关
eval_token: "" # 默认空;eval_enabled=true 时必须非空
profile_enabled: false # 默认 false;/ops/profile 注册开关
profile_token: "" # 默认空;profile_enabled=true 时必须非空
profile_cooldown_seconds: 10 # 默认 10
slow_call_threshold_ms: 0 # 默认 0(不计量)
console:
enabled: false # 默认 false;Unix socket 诊断控制台开关
socket_path: "/tmp/shield-console.sock" # 默认路径节点状态与心跳
本地 service 不做 per-service heartbeat;其存活与清理由 runtime 的 service stop/exit、registry 注销和 handle 失效流程维护。
IPC / cluster 节点状态由链路 heartbeat 驱动:online -> suspect -> offline。判定是时间窗语义(自最近一次心跳/连接起点的经过时长与阈值比较),不是 miss 计数。实际默认值(配置键 cluster.*,ClusterManager 启动时读取):
cluster.heartbeat_interval_ms = 5000 # 心跳周期,下限 clamp 到 50ms
cluster.suspect_timeout_ms = 15000 # Online:超过该时长无心跳 -> Suspect
cluster.offline_timeout_ms = 30000 # Suspect:超过该时长无心跳 -> Offline
# Connecting:握手超过该时长 -> Offline无 offline -> removed 自动转移:Removed 状态只在 ClusterManager::stop() 停机时对全部节点统一置入,运行期不发生。
节点观测入口:/ops/status 的 cluster 块提供节点列表、状态与 heartbeat_age_ms(见上文)。heartbeat RTT、miss 计数、offline tombstone、因 node offline 失败的 pending call 统计均未实现。
数据流
shield_core / modules
-> internal collectors
-> shield_ops
-> console / HTTP / exporter / profileshield_core 不直接依赖 shield_ops;shield_ops 只读取、聚合和导出运行时状态。