节点内存占用曲线突然多出一个尖峰,很多时候不是被大消息打爆,而是两道每连接内存水位线在工作:v31.0 源码参数表里有 -maxreceivebuffer=<n> 与 -maxsendbuffer=<n> 两行,说明都写成 <n>*1000 bytes,默认值分别取自 src/net.h 的两个常量——接收侧等于五百万字节,发送侧等于一百万字节。名字里的 buffer 容易让人以为是网速旋钮,读一遍源码语义就能纠正。
一、接收线:先囤内存,再暂停读 socket
节点与邻居的每条连接都先把收到的完整消息排进一个待处理队列,再交给处理线程。src/net.cpp 的入队与出队函数里各有一行同样的判断:队列累计的内存用量一旦超过接收水位线,这条连接就进入”暂停接收”状态——不再从 socket 读取新数据,等待队列消化后水位回落才恢复。这道机制防的是典型的内存放大器:对端用高速链路把消息砸进来,而本机处理(验签、查重、更新索引)慢半拍,队列就会无节制膨胀。水位线让反压沿 TCP 天然回传给发送方,谁灌得太快谁自己排队。协议还有一条硬顶:单条消息超过四百万字节直接按错误头处理并断连,水位线管的是”合法消息的洪水”,硬顶管的是”超限的怪物”。
二、发送线:待发内存超限就改期
发送侧的账记在每条连接的待发送内存上。src/net.cpp 在两条路径上更新同一判断:消息入队时先累加其内存用量,一旦”待发内存加传输层缓冲”越过水位线就把这条连接标记为暂停;每轮消息处理结束时再按同一公式重算,水位回落则标记自动解除。暂停期间这条连接的推送直接跳过,留到下一轮调度再试。它保证一个慢邻居不能把本机的内存和带宽无限占住——内存记在你账上,链路被你堵着,对越线的连接自然要降级处理。默认一百万字节的发送水位,对应的是”愿意为每个对端预先囤多少待发消息”的意愿值,而不是这台机器总共能发多少。
三、调它之前先分清三本账
第一,这两个参数都不是带宽整形器:它们不承诺吞吐、不限制速率,只在”每台对端占多少待处理内存”上做上限。想给一天的上传总量封顶,用 maxuploadtarget 那道二十四小时预算闸门,两者互补而不是替代:前者管”某一瞬间每个对端占多少内存”,后者管”一天累计发出去多少字节”,一个防内存尖峰、一个防流量超额。第二,调大接收线等于允许每条连接囤更多在途消息——极端拥塞或攻击时,全部连接的缓冲之和可能比默认高出一个数量级,几十条连接各囤满五兆就是数百兆的常驻内存;调小则容易在慢机器上频繁触发暂停,表现为同步变慢、交易传播延迟。第三,正常家用带宽几乎碰不到这两道线:默认五百万字节的接收水位远超常见链路的合理在途量,真正需要动的场景是特殊慢速网络(超长延迟链路)或专门的资源上限实验。
源码里两线都是同一句判断的多次落点。接收线在消息入队与出队处各重算一次暂停标记,水位一回落、暂停自动解除;发送线在推入消息时置位、在每轮消息处理末尾按”待发内存加传输层缓冲”重算,超线期间这条连接的推送直接跳过,等下一轮再试。还有一条更早生效的硬顶:单条消息声明的尺寸超过 400 万字节,会在消息头解析阶段被判错误并断连——源码注释保留了这道检查的来历:2024 年 7 月披露过一个接收缓冲区的内存问题,正是这道检查防止对端用超大尺寸声明挤爆每连接内存。
一个实用的排障口径:先区分现象发生在哪一侧——“节点看起来变慢了”多半在接收线(处理跟不上入队),“对端收不到我们转发东西”可能碰发送线(我们暂缓了发送)。把日志里连接暂停的痕迹与系统内存曲线对照,再决定是否修改。两个参数均只在启动时读取,改完重启才生效,并保留回退余地;本文只讲机制,不构成任何性能承诺或投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。