一条 getdata 最多点一千件货:MAX_GETDATA_SZ 的分批逻辑 图 1
一条 getdata 最多点一千件货:MAX_GETDATA_SZ 的分批逻辑 · 图 1

节点之间要货,靠的是 getdata 消息:我在 inv 里看到你宣告了某批交易或区块,就用 getdata 点单,把这些具体条目要过来。很多人以为点单可以无限长,其实协议实现给一次 getdata 能列的条目数卡了一个硬上限——在 Bitcoin Core 里这个数字是 1000。定义在 src/net_processing.cpp 顶部,落地在同文件的消息处理里。

上限在哪里生效

源码里有个常量把单次 getdata 的清单大小定死在一千。当节点在处理某条 inv 宣告、把里面自己还没有、且允许请求的存货逐条塞进待发清单时,一旦清单长度触到这个阈值就停止继续追加。换句话说,即便邻居在一条 inv 里一次性宣告了成千上万条存货,节点这一轮最多也只点前一千条,剩下的要么在后续 inv 或后续轮次里再点,要么靠别的同步机制补齐。这是一种防御性的节制:防止单条 getdata 无节制变长,给发起方和应答方都造成内存与带宽上的突发压力。

一条 getdata 最多点一千件货:MAX_GETDATA_SZ 的分批逻辑 图 2
一条 getdata 最多点一千件货:MAX_GETDATA_SZ 的分批逻辑 · 图 2

为什么要卡这一刀

getdata 的应答(真正的 txblock)可能很大,尤其涉及区块时。如果允许一次点单无限多,攻击面就出现了:一个对端用一条超长的 getdata 把节点拖进海量、并发的大对象搬运,耗尽缓冲或连接队列。把单条清单限到一千,等于把”一次要多少货”这件事分段,让内存占用和排程都可控。这个上限约束的是一条消息里的条目数量,不是这些条目的总字节大小;总大小层面另有单消息体积、消息头等其他护栏,各管一层。

它和周边限制怎么配合

getdata 是问货动作,本身在中继对话里通常很短,所以单条长度限制更像是”别把它当成批量下载器滥用”的护栏。它和地址宣告、存货宣告的速率限制是不同的东西:那些限制管的是单位时间内能发多少 inv/addr 条消息,而这个一千的限制管的是单条 getdata 内部装多少个 inventory 向量。要拉大量交易,正常做法不是塞一条巨长的 getdata,而是让节点在多条 inv、多轮请求里分批点单;区块下载则走另一套按区块头先行、按并行度分块的调度,不靠一条 getdata 吃满。

运维视角

对日常节点,这个数字基本是”背景设定”,你不需要主动去调它,源码里它就是一个写死的编译常量,不是命令行可调参数。它的价值在于解释一类现象:当你在日志或抓包里看到某个对端反复、分批地发 getdata,那不是异常,而是受单条清单上限约束后正常的分批点单行为。理解这一点,也能帮你在读 P2P 抓包时不被”为什么这么多条 getdata”迷惑——它反映的正是协议刻意把大批量拆成受控小批的设计取向。

源码里的分批形态

在交易下载路径上能看到这个上限的具体工作方式:节点每轮从交易下载管理器取出本轮该发给某个对端的请求,逐条压进待发清单,一旦清单条目数触到上限,就立刻把当前清单打包成一条 getdata 发送并清空,再从剩余请求继续填;区块下载路径类似,只是队列来源不同。所谓”一条最多一千件”,落到运行时是被切成一串不超过一千条目的消息依次发出,而不是攒成一条巨无霸。这个上限是编译期常量,命令行没有任何开关可以调整它;它和单条消息的字节大小限制、消息头约束属于不同层的护栏,各管各的维度,不能混为一谈。对普通运维,这条限制几乎永远不需要关心,它的意义集中在两处:解释日志与抓包里连发多条较短 getdata 的正常现象,以及帮助理解节点为何对单连接的请求吞吐天然设防。本文讨论 P2P 协议实现细节,不构成任何投资建议。