一、RPC 服务器的三本容量账
比特币核心内嵌一个 HTTP 服务器处理 RPC。源码里的默认值可以直接抄下来:服务线程 16 个(-rpcthreads),等待队列深度 64(-rpcworkqueue),单个请求的服务端超时 30 秒(-rpcservertimeout)。请求进来先进队列,有空闲线程才执行。两个客户端侧还有对应参数:-rpcclienttimeout 控制 bitcoin-cli 自己等多久,-timeout 管的是套接字连接超时。平时感觉不到这套机制,是因为多数命令毫秒级返回;一旦节点开始干重活,三本账立刻开始碍事。

二、503 的真实含义:队列满了不是节点坏了
当在途请求堆到队列上限,新请求会被当场拒绝,返回 503,正文写着 Work queue depth exceeded,日志同步记一句”可以用 rpcworkqueue 调大”。看到 503 请先数并发:典型肇事者是监控脚本每秒全量抓 getrawmempool、备份脚本同时遍历所有钱包、或对每个地址循环 getaddressinfo。正确顺序是先降并发、再合并请求,把”调大队列”当作最后手段——队列调大只是把拒绝改成排队,线程没变多。
三、30 秒超时与长任务:别把异步当失败
服务端默认给单请求 30 秒,超时先回一个内部错误。真正的长任务有两个官方入口留了后门:钱包扫描类命令文档明确写着”请用 -rpcclienttimeout=0 调用,避免客户端超时”;scanblocks、rescanblockchain、带早期时间戳的 importdescriptors 都属于此类。以 importdescriptors 为例,导入很旧的时间戳意味着从早期区块开始重扫,官方文档估计可以超过一小时;期间其他 RPC 会显示密钥、地址已存在但相关交易还没出现——不是丢了,是扫描还没跑到。进度查询走 getwalletinfo 的扫描字段,别用重复导入来”催进度”,重复导入只会再排一次队。
四、把长任务从交互里拆出来
经验纪律有四条。其一,凡是可能长尾的命令(扫描、重扫、描述符导入)放独立会话跑,客户端超时设为无限,并用轮询进度代替干等。其二,监控采集只读轻量命令,重查询类(scantxoutset、全量 getblock 遍历)错峰到凌晨。其三,多钱包服务场景用钱包端点分流,同一钱包的重命令天然串行,不要对同一钱包并发发起扫描。其四,调 -rpcworkqueue 前先用 getrpcinfo 看活跃请求画像——多数”RPC 卡死”的最终结论是某个慢命令占着线程,扩容解决的是症状不是设计问题。
五、边界提醒
这些参数全部只影响本机 RPC 接口,不碰共识、不碰 P2P。RPC 通道没有加密,默认只监听回环地址;把它暴露到内网以外属于需要额外谨慎的架构决定,跟本文的容量参数无关,却常被混为一谈。队列、线程、超时三个数字管的是”请求怎么排队”,安全边界那是另一篇文章的事。
补充:一次容量诊断的顺序
遇到”RPC 越来越慢”时按固定顺序走:先 getrpcinfo 看当前活跃请求里有谁,占着线程的慢命令一目了然;再翻 debug.log 找 work queue 警告的时间点,和采集脚本的执行记录对时间轴;然后单发轻量命令确认服务本身活着。三步都正常仍慢,才考虑调 -rpcthreads 或 -rpcworkqueue,并给调整设定复评日期。跳步的常见结局是把监控洪水喂成更大的并发洪水:队列深了,请求不报错了,但每个请求的延迟悄悄翻倍,慢得像”节点坏了”,其实是排队经济学在照常运行。
再多给两个常被踩的坑。其一,scantxoutset 这类全集合扫描会长时间占住一个线程,它跑着的时候其他钱包 RPC 全部排队,看起来像整机卡死——调度这类任务前先确认同一端点上没有人在等响应。其二,脚本里对同一份数据反复轮询是隐性大头:把”每秒抓一次内存池”改成事件驱动(ZMQ 推送或区块回调触发),同一份信息的获取成本能降一个量级,队列压力随之消失。容量问题的解法排序几乎永远是”少问、合并问、错峰问”,最后才是”加线程”。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。