Skip to content

运维运行时语义 ​

本文档包含 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: true
    • http.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 只有管理入口:

text
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/healthGET健康检查(已提供)
/ops/statusGET运行时状态
/ops/metricsGET指标导出(Prometheus 格式,已提供)
/ops/servicesGET服务列表
/ops/services/:nameGET服务详情(已提供)
/ops/pluginsGET插件实例列表(id/package/state/required,已提供)
/ops/configGET单键查询(?key=<key>),非全量快照;不带 key 时返回提示文本 "Use ?key=<key> to query",键不存在返回 null
/ops/evalPOST受限 VM 远程代码执行;opt-in——需 http.eval_enabled: true 且非空 http.eval_token,否则路由不注册(请求 404),见上文安全基线
/ops/profilePOST采样式热点分析与慢调用追踪的启动/停止/查询(已提供,见 Profile)

如果启用了 Lua 诊断控制台,管理面还可以额外暴露只读 Lua 观测能力,例如:

能力形态说明
lua.inspect <service> summaryconsole查看 service 对应 Lua VM 的摘要
lua.snapshot <service>console / HTTP记录 Lua 内存摘要快照
lua.diff <service> <a> <b>console / HTTP比较两次 Lua 快照

任意 Lua 执行不是默认能力;如后续支持 lua.eval,也必须显式启用并默认仅限本地入口。详见 Lua 诊断控制台设计。

健康检查 ​

GET /ops/health

P0 实现为轻量探针:全部读取在 HTTP 线程内进程内完成,不经 Lua actor 往返,因此 actor 网格卡死时探针仍可应答。verdict 同时映射到 HTTP 状态码:ok → 200,degraded → 503(body 信封不变,负载均衡/编排器可以只看状态码做门禁,探针仍可读 body 里的 per-check 明细);unhealthy 档位保留给后续(如 actor 网格失联检测)。

响应(与其他 ops 端点一致的 type/data 信封):

json
{
  "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):

json
{
  "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/metrics

P0 导出 Prometheus 0.0.4 文本格式(Content-Type: text/plain; version=0.0.4; charset=utf-8,HELP/TYPE 头齐全,标签值转义 \ " \n)。进程内读取为主,唯一经 actor 网格的 shield_services 在 500ms 超时时省略——网格卡死不会拖死抓取。

按编译开关与运行时状态裁剪的指标族:

指标类型说明
shield_uptime_secondsgauge进程 uptime
shield_plugin_instances{state}gauge插件实例按生命周期状态(planned/loaded/started/unavailable/failed/stopped)
shield_plugin_pool_size / shield_plugin_pool_in_use / shield_plugin_pool_idlegauge插件连接池快照,标签 plugin/instance(shield.pool.stats.v1 采集,见 Plugin Pool Stats);报 -1(无池/未知)的字段不导出
shield_plugin_db_pending_async / shield_plugin_db_holdinggauge插件 DB 异步入口的在途调用数 / 打开事务持有连接数,标签同上;-1 不导出
shield_gateway_connections_total{port}counter完成 TCP accept 的连接数(含随后被黑名单/限连拒绝的)
shield_gateway_rejections_total{port,reason}counteraccept 阶段拒绝数,reason ∈ blocked_ip / connection_limit / ip_limit
shield_gateway_active_sessions{port}gauge网关活跃会话数(瞬时值)
shield_gateway_rate_limited_messages_total{port}counter被每连接 ingress 限流丢弃的消息数(listener 级累计,会话退出不影响单调性)
shield_servicesgauge已注册 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}gaugeServer 状态机(SERVER=ON 且 manager 存在)
shield_global_data_keys / shield_global_cache_entriesgaugeglobal 数据键 / 本地缓存条目(GLOBAL=ON 且 manager 存在)
shield_global_cache_hits_total / shield_global_cache_misses_totalcounter缓存命中/未命中
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_membersgauge排行榜板数 / 成员总数
shield_global_scheduler_tasks / shield_global_scheduler_activegauge调度任务总数 / 活跃数
shield_cluster_nodes{state}gauge集群节点按状态(CLUSTER=ON 且 manager 存在)
shield_cluster_transport_connections / ..._reconnects_totalgauge/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/:name

P0 实测口径:注册表内只读快照,走 ""-id forked task(与 /ops/services 列表同路),2s 超时返回 504。未发布的名字(on_init 中、已退出)与列表口径一致按不存在处理,返回 404。

响应:

json
{
  "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 均附带):

json
{
  "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_enabledfalse注册 /ops/profile 路由的总开关
http.profile_token空Bearer 令牌;enabled 但为空则拒绝注册
http.profile_cooldown_seconds10两次 start 之间的最小间隔
http.slow_call_threshold_ms0慢调用阈值;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 需要速率限制。

允许暴露:

txt
service list
registry names
pending calls
timer count
coroutine count
node heartbeat status
slow call summary

禁止默认暴露:

txt
CAF actor handle
完整业务 payload
密钥、token、密码
未鉴权远程 console

配置示例 ​

ops: 配置段未实现:bootstrap 硬编码 ops_enabled = false,配置中出现任何 ops: 段都会在运行时校验阶段失败并拒绝启动(optional module config 'ops' requires shield_ops),全仓也没有任何 ops.* 配置读取者。实际运维面由 http: 与 console: 键门控(实现于 shield_bootstrap):

yaml
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 启动时读取):

text
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 统计均未实现。

数据流 ​

text
shield_core / modules
  -> internal collectors
  -> shield_ops
  -> console / HTTP / exporter / profile

shield_core 不直接依赖 shield_ops;shield_ops 只读取、聚合和导出运行时状态。

Apache License 2.0