第四节:节点发现与网络拓扑

好,咱们今天聊点硬核的——分布式交易所里,节点之间到底是怎么找到彼此的?

你想想看,一个去中心化的交易所,背后可能有几千个节点在跑。没有中心服务器,一个新节点上线,它怎么知道该跟谁打招呼?这就是节点发现要解决的问题。

我个人习惯把这个问题拆成三层来看:

  • 底层算法:Kademlia DHT,决定节点怎么组织
  • 路由维护:节点怎么更新自己的“通讯录”
  • 网络穿透:NAT 环境下的节点怎么互相通信

咱们一层一层剥开。

4.1 Kademlia 分布式哈希表

Kademlia,简称 KAD。说白了,它就是一个分布式的“电话本”。

每个节点有一个 160 位的 ID(通常用 SHA-1 算出来的)。节点之间通过这个 ID 的“距离”来决定谁该认识谁。

注意,这里的距离不是地理距离,而是 XOR 异或距离

核心公式:distance(A, B) = A.id XOR B.id

这个距离越小,说明两个节点在逻辑上越“近”。

我在项目中遇到过一个问题:刚开始用 KAD 时,以为距离越小越好,结果发现节点全扎堆了。后来才明白,KAD 的精髓在于 “分桶”——每个节点维护多个 k-bucket,每个桶负责一个距离范围。

举个例子:

  • 桶 0:距离在 [2^0, 2^1) 之间的节点
  • 桶 1:距离在 [2^1, 2^2) 之间的节点
  • 桶 i:距离在 [2^i, 2^(i+1)) 之间的节点

每个桶最多放 k 个节点(k 通常取 20)。这样,整个网络就形成了一种 “越近越密,越远越疏” 的拓扑结构。

嗯,这里要注意:桶的数量是固定的,160 个。因为 ID 是 160 位,所以最多 160 个桶。

4.2 节点路由表维护

路由表,就是每个节点本地维护的那个“通讯录”。

它长什么样?我直接给你看伪代码:

// 路由表结构
type RoutingTable struct {
    buckets [160]*KBucket  // 160个桶
    selfID  NodeID         // 自己的节点ID
}

// 每个桶
type KBucket struct {
    nodes  []Node  // 最多k个节点
    lastSeen time.Time  // 最后一次刷新时间
}

路由表的维护,说白了就三件事:

  1. 节点加入:新节点上线,先找几个种子节点,然后通过 FIND_NODE 请求逐步填充自己的路由表。
  2. 节点更新:收到任何消息,都要检查对应桶,如果节点已存在就刷新时间戳,如果桶没满就加入。
  3. 节点淘汰:桶满了怎么办?我曾经踩过这个坑——直接丢弃新节点。结果导致网络分裂。

避坑指南:我曾经在测试网里发现,桶满了之后直接丢弃新节点,会导致老节点下线后,新节点永远无法加入。正确的做法是:

  • 先 ping 一下桶里最久没联系的节点
  • 如果它活着,就丢弃新节点
  • 如果它死了,就替换掉它

还有一个细节:刷新机制。每个桶如果超过 1 小时没更新,就要主动发起一次刷新。我一般设置成 30 分钟,因为交易所的节点变动比较频繁。

4.3 NAT 穿透与中继节点

好,现在问题来了。大部分节点都在 NAT 后面——家庭网络、云服务器、甚至手机热点。它们没有公网 IP,怎么互相通信?

这就是 NAT 穿透要解决的问题。

常见的穿透方式有三种:

方式 原理 成功率 我的评价
UDP 打洞 双方同时向对方公网地址发 UDP 包 60%-70% 简单,但依赖 NAT 类型
STUN/TURN 通过服务器协调 90%+ 稳定,但需要服务器
中继节点 通过第三方节点转发 100% 最可靠,但延迟高

我个人习惯的做法是:优先尝试 UDP 打洞,如果失败就切换到中继。

中继节点的部署,其实很简单。它就是一个有公网 IP 的节点,专门负责转发消息。但要注意:

  • 中继节点不要参与交易撮合,只做转发
  • 每个中继节点限制连接数,比如 500 个
  • 中继节点之间要互相备份,防止单点故障

小技巧:我在部署中继节点时,会在每个节点上跑一个健康检查脚本,每 5 秒检测一次。如果某个中继节点挂了,其他节点会自动把它的客户端分配到备用节点上。

4.4 整体拓扑结构

说了这么多,咱们用一张图来总结一下整个网络拓扑:

分布式交易所节点网络拓扑 中继节点层 中继1 中继2 中继3 普通节点层(Kademlia 网络) 节点A 节点B 节点C 节点D 节点E NAT 节点(通过中继连接) NAT 节点 1 NAT 节点 2 NAT 节点 3 中继节点 普通节点 NAT 节点 Kademlia 路由 中继连接

从这张图你能看到:

  • 中继节点在最上层,它们之间互相备份
  • 普通节点通过 Kademlia 协议互相连接,形成一个 P2P 网络
  • NAT 节点无法直接连入 P2P 网络,需要通过中继节点转发

说白了,整个网络就是 “中继保底,KAD 为主” 的混合架构。

嗯,最后说一句:节点发现这块,看起来简单,但坑特别多。我当年第一次搭建测试网时,就因为路由表刷新频率设得太低,导致节点分裂成两个孤岛,交易对不上了。后来我加了一个心跳机制,每 10 秒互相 ping 一次,才稳定下来。

所以,别小看节点发现。它是整个分布式交易所的骨架。骨架歪了,上面再好的业务逻辑也白搭。


公众号:蓝海资料掘金营,微信deep3321