区块下载窗口 1024 与那条会自己变宽的超时线 图 1
区块下载窗口 1024 与那条会自己变宽的超时线 · 图 1

同步卡住时,日志里那行 Timeout downloading block 是少数会直接进信息级日志的下载事件。它背后有两个数字:一个决定”往多远的地方要块”,一个决定”要了多久还没来就算对方卡住”。两者都写死在 v31.0 的网络处理代码里。

下载窗口:一千零二十四个高度

第一个常量叫 BLOCK_DOWNLOAD_WINDOW,取值 1024,注释称其为”区块下载窗口”:我们的当前高度往前提多远之内去取块。

这个窗口不是从链顶算的,而是从”我和这个对端最后一个能对上的块”算起。代码在分配下载任务时先定位这个共同块,然后把窗口末端设为它的高度加上一千零二十四,并且注释强调了一句细节:永远不要取超过这个对端已知的最佳块,也提到那句”+1”的用意——多留一个位置,是为了能检测出卡死:如果窗口本可以再多覆盖一个块而那个块恰好是唯一缺的,节点就能观察到”本可以下但没下到”的状态。

窗口存在的理由写在注释里:更大的窗口能容忍对端之间下载速度的差异,但会让区块在磁盘上的落盘顺序更乱,进而让重建索引和修剪变得更难。代码里还留了一句未完成的打算,说这个值以后大概应该做成按对端自适应,目前仍是统一常量。

顺带对齐一下相邻的数字,避免混用:修剪模式的节点愿意对外的历史块数下限是 288,链顶切换时一次最多向邻居公告 8 个区块,紧凑区块的补交窗口只覆盖最近 10 个块。这四个数字管的是完全不同的事。

超时线:随并发下载的对端数放宽

第二个数字更值得运维注意。判断某个对端是否”拿了太久”时,代码用的是一个以出块间隔为单位的公式:一个基准值加上一部分与并发下载对端数成正比的补偿,再乘以出块间隔。

具体取值是:基准为 1,也就是十分钟;每个其他正在下载已验证区块的对端再加零点五,即五分钟。注释解释了为什么要加这一项:如果不补偿,自家下行带宽被多个对端分摊时,会把自己的正常慢误判成对方的错,把无辜的对端踢掉。补偿只统计”已验证的在途区块”,注释说明这是为了防止对端靠吹一堆并不存在的块哈希来无理地拉长我们的耐心。

于是实际效果是:除了当前这个对端之外,如果还有三个对端同时在给你送块,超时线就放宽到出块间隔的 2.5 倍,按主网十分钟一档算就是二十五分钟。这解释了为什么同一个对端在你网络空闲时被踢、在你带宽饱和时却能活很久——不是它变快了,是你的判定门槛变了。

卡死检测:默认只有两秒

除了硬超时,还有一条更敏感的判定,管的是”下载窗口推不动”。它对端级的计时字段叫 m_stalling_since:当节点在给别的对端派活时发现窗口被某个对端堵住——那个对端手上有已验证的在途区块、却没在为我们推进进度——就给它记下起始时刻。等到这个计时超过当前阈值,节点直接断开,日志是那句 Peer is stalling block download。

阈值本身小得出乎意料:默认值只有两秒,上限六十四秒,都不到一次正常出块的零头。但它的行为是自适应的。每断开一个”卡死”的对端,这个阈值临时翻倍,最多涨到六十四秒;而每当链顶正常向前推进一次,阈值又按零点八五的比例慢慢往回压,最终回到两秒。注释里写明了这个设计意图:避免在自己带宽不够时把对端一批批全部踢掉。

注释还有一句重要的限定:卡死只在下载窗口无法移动时才触发。稳态运行下,一千零二十四的窗口远大于待下载的区块集合,所以这类断开几乎只发生在初始同步期间。看到这条日志时,正确的心智模型是”我在补历史,某个对端挡住了路”,而不是”网络出问题了”。

每对端的在途区块数也有一道闸,取值是 16,窗口内的任务分配会受它约束。两套判定合起来看:硬超时管”我下了订单你不交货”,按出块间隔的倍数计算;卡死管”窗口推不动”,以秒计并随链顶推进自我调节。

为什么你的节点看起来”踢人很勤”

把上面几条合起来,能解释几个常见现象。

第一,刚连上一个带宽有限的机器时,节点会在几分钟内踢掉若干对端。这是超时线还没被并发补偿撑开的结果——当时只有一个下载对端,门槛就是最短的十分钟线。

第二,家庭宽带上游窄的节点更容易反复换对端。断开之后重新挑选下载对端,新的对端又要从头计时,形成循环。这种情况提高下载并发或者延长卡死阈值比加带宽立竿见影。

第三,“日志里 Timeout downloading block 后面那个哈希”值得记下来。它通常是节点已经定位到具体高度、且这个块在途超时的证据。如果你正在同步且这个哈希反复出现,多半是能提供该段的对端质量差,而不是链本身有问题。

第四,这些行为都不是配置错了。窗口和超时都是硬编码的常量,配置文件里没有对应开关;能调的只有连接数、卡死阈值这类外围参数。想理解同步速度为什么这样,看清这些常量比改配置更有用。

风险提示:本文依据比特币核心 v31.0 源码撰写,常量取值与断开策略可能随版本调整;本文只讨论节点同步行为,不构成任何投资建议。