RPC 返回 -28 不是故障:预热状态文本从哪来、脚本该怎么等 图 1
RPC 返回 -28 不是故障:预热状态文本从哪来、脚本该怎么等 · 图 1

比特币核心启动日志的 RPC 段落里藏着一个大多数人不读的机制:RPC 服务不是”端口一开就能干活”,它先以 warmup(预热)状态上线——端口在监听、连接能建立,但绝大多数命令会立刻被拒,返回一条负 -28 号错误,消息文本直接告诉你它正在等什么。对运维来说,读懂这条错误比写睡眠重试脚本体面得多,也正确得多。

机制在 v31.0 源码里很简洁。RPC 服务器在初始化早期就随 HTTP 接口一起启动,源码注释写明”此刻 RPC 已启动但仍在 warmup 状态”——这是故意的:让外部探针此刻就能连上端口区分”进程没起来”和”进程在干活”,同时用一道全局标志挡住所有依赖未完成子系统的命令。预热状态文本的来源有个巧妙接线:初始化流程的进度消息(就是启动时在 GUI 进度条或终端显示的那些”Importing blocks…""Loading wallet…”)被直接接到 SetRPCWarmupStatus 上——init.cpp 里一行 uiInterface.InitMessage_connect(SetRPCWarmupStatus) 把两路信息并成一路。你在报错里看到的”Not started”或某句英文进度描述,就是子系统实时写入的当前初始化步骤。命令分发入口在预热标志未清除时统一抛出 -28(错误码常量 RPC_IN_WARMUP),消息体就是这句话。全部子系统完成后,初始化收尾统一把预热标志关掉,命令恢复应答。

这个设计解决的实际问题很具体。没有 warmup 机制的世界里,启动后几秒内的 RPC 调用会撞上各种半成品状态:钱包还没挂载时查询余额会报”钱包不存在”,索引没建完时查交易索引会报”未启用索引”——每一种都长得像真故障,脚本无法区分”再等三秒”和”配置错了”。有了 warmup,所有”还没好”收敛成一个错误码:-28(源码里叫 RPC_IN_WARMUP,附带实时状态文本)表示重试可能成功,其他错误表示应该去检查配置。检查发生在命令分发的入口:预热期间任何命令——包括本身极轻的 getrpcinfo——都先撞上这道统一拦截再谈方法名,因此探测脚本的正确姿势是拿任意便宜命令轮询:遇到 -28 继续等,遇到别的错误才报警,遇到正常返回即就绪。

三个易踩的坑值得点名。第一,把 -28 当故障报警:节点冷启动导入大文件、重建索引时预热可能持续很久,此时报警只是制造噪音,应该基于持续时间设阈值而不是见到就报。第二,用固定 sleep 代替探测:脚本 sleep 30 后再调用,在慢机器上仍然太早、在快机器上白白等待,而预热状态文本里本来就有进度信息,getrpcinfo 的返回值里同样能观察服务状态。第三,对 ZMQ 订阅者而言,RPC 预热完成不等于事件流就绪——区块通知的推送始于节点进入服务阶段,与 RPC 预热窗口有重叠但不完全重合,集成测试里两者要分别确认。

还有一个与桌面用户相关的场景:GUI 钱包启动后立刻点”发送”,偶尔会碰到类似”服务未就绪”的提示——那就是预热窗口撞上了人手动操作。理解机制之后处置很简单:等同步图标出现再操作,或者在命令行用 getrpcinfo 确认预热结束。顺带一提,预热期的错误文本里如果反复出现同一句钱包相关的状态描述,通常意味着某个钱包文件体积大或磁盘慢——那是性能信号,不是 RPC 故障,该去看磁盘而不是重启节点。

一个负错误码,一段状态文本,一套标志位——RPC 预热是全系统”我还没好但我知道我在等什么”的自我介绍。会读这段自我介绍的脚本,才配得上自动重启未确认交易的钱包。

风险提示:错误码与行为以 Bitcoin Core v31.0 源码为准,版本升级可能调整;自动化脚本误设重试策略可能掩盖真实故障,本文不构成运维建议或投资建议。

RPC 返回 -28 不是故障:预热状态文本从哪来、脚本该怎么等 图 2
RPC 返回 -28 不是故障:预热状态文本从哪来、脚本该怎么等 · 图 2