王继有,PalletOne高级核心开发工程师,8年研发经验,精通C,C++,Go语言。具有丰富的DHCPv6、ND、RUI协议以及高性能服务器和对象存储的设计开发经验;熟悉区块链P2P网络的设计与开发。
一、p2p四种不同的网络模型
P2P 网络不同于传统的客户端/服务端(client/server,C/S)结构,P2P 网络中的每个节点都可以既是客户端也是服务端,因此也不适合使用 HTTP 协议进行节点之间的通信,一般都是直接使用 Socket 进行网络编程。
P2P 主要存在四种不同的网络模型,也代表着 P2P 技术的四个发展阶段:
1、集中式
2、纯分布式
3、混合式
4、结构化模型
不过需要指出的是,这里所说的网络模型主要是指路由查询结构,即不同节点之间如何建立连接通道,两个节点之间一旦建立连接,具体传输什么数据则是两个节点之间的事情。
1、集中式
存在一个中心节点,保存了其他所有节点的索引信息,索引信息一般包括节点 IP 地址、端口、节点资源等。
优点:结构简单、实现容易。
缺点:由于中心节点需要存储所有节点的路由信息,当节点规模扩展时,就很容易出现性能瓶颈;而且也存在单点故障问题。
2、纯分布式
新节点与邻居节点建立连接后,还需要进行全网广播,让整个网络知道该节点的存在。全网广播的方式就是,该节点首先向邻居节点广播,邻居节点收到广播消息后,再继续向自己的邻居节点广播,以此类推,从而广播到整个网络。这种广播方法也称为泛洪机制。
纯分布式结构不存在集中式结构的单点性能瓶颈问题和单点故障问题,具有较好的可扩展性,但泛洪机制引入了新的问题,主要是可控性差的问题,包括两个较大的问题,一是容易形成泛洪循环,比如节点 A 发出的消息经过节点 B 到 节点 C,节点 C 再广播到节点 A,这就形成了一个循环;另一个棘手问题则是响应消息风暴问题,如果节点 A 想请求的资源被很多节点所拥有,那么在很短时间内,会出现大量节点同时向节点 A 发送响应消息,这就可能会让节点 A 瞬间瘫痪。
3、混合式
混合式其实就是混合了集中式和分布式结构,如下图所示,网络中存在多个超级节点组成分布式网络,而每个超级节点则有多个普通节点与它组成局部的集中式网络。一个新的普通节点加入,则先选择一个超级节点进行通信,该超级节点再推送其他超级节点列表给新加入节点,加入节点再根据列表中的超级节点状态决定选择哪个具体的超级节点作为父节点。
这种结构的泛洪广播就只是发生在超级节点之间,就可以避免大规模泛洪存在的问题。在实际应用中,混合式结构是相对灵活并且比较有效的组网架构,实现难度也相对较小,因此目前较多系统基于混合式结构进行开发实现。其实,比特币网络如今也是这种结构。
4、结构化模型
它也是一种分布式网络结构,但与纯分布式结构不同。纯分布式网络就是一个随机网络,而结构化网络则将所有节点按照某种结构进行有序组织,比如形成一个环状网络或树状网络。而结构化网络的具体实现上,普遍都是基于 DHT(Distributed Hash Table,分布式哈希表) 算法思想。DHT 只是提出一种网络模型,并不涉及具体实现,主要想解决如何在分布式环境下快速而又准确地路由、定位数据的问题。具体的实现方案有 Chord、Pastry、CAN、Kademlia 等算法,其中 Kademlia 也是以太坊网络的实现算法,很多常用的 P2P 应用如 BitTorrent、电驴等也是使用 Kademlia。
结构化网络采用一组预先确定的规则来连接结点,有效并高效的解决查询。
DHT还应用于其他很多应用:如分布式数据结构(Distributed Data Structure,DDS)、内容分发系统(Content Distributed Systems,CDS)、域名系统(Domain Name System,DNS)等。
二、Kademlia协议原理简介
参考:https://github.com/ZtesoftCS/go-ethereum-code-analysis/blob/master/references/Kademlia%E5%8D%8F%E8%AE%AE%E5%8E%9F%E7%90%86%E7%AE%80%E4%BB%8B.pdf
1、节点距离
2、K桶
3、Kademlia 协议操作
4、路由查询
5、数据存放
6、节点加入和离开
三、以太坊网络分层
1、Peer管理
2、ETH协议
Peer管理
Server.Start()中启动一个单独线程(listenLoop())去监听某个端口有无主动发来的IP连接;另外一个单独线程启动run()函数,在无限循环里处理接收到的任何新消息新对象。在run()函数中,如果有远端peer发来连接请求(新的p2p.conn{}),则调用Server.newPeer()生成新的peer对象,并把Server.Protocols全交给peer。
广播新出现的交易对象。txBroadcastLoop()会在txCh通道的收端持续等待,一旦接收到有关新交易的事件,会立即调用BroadcastTx()函数广播给那些尚无该交易对象的相邻个体。
广播新挖掘出的区块。minedBroadcastLoop()持续等待本个体的新挖掘出区块事件,然后立即广播给需要的相邻个体。当不再订阅新挖掘区块事件时,这个函数才会结束等待并返回。很有意思的是,在收到新挖掘出区块事件后,minedBroadcastLoop()会连续调用两次BroadcastBlock(),两次调用仅仅一个bool型参数@propagate不一样,当该参数为true时,会将整个新区块依次发给相邻区块中的一小部分;而当其为false时,仅仅将新区块的Hash值和Number发送给所有相邻列表。
定时与相邻个体进行区块全链的强制同步。syncer()首先启动fetcher成员,然后进入一个无限循环,每次循环中都会向相邻peer列表中“最优”的那个peer作一次区块全链同步。发起上述同步的理由分两种:如果有新登记(加入)的相邻个体,则在整个peer列表数目大于5时,发起之;如果没有新peer到达,则以10s为间隔定时的发起之。这里所谓"最优"指的是peer中所维护区块链的TotalDifficulty(td)最高,由于Td是全链中从创世块到最新头块的Difficulty值总和,所以Td值最高就意味着它的区块链是最新的,跟这样的peer作区块全链同步,显然改动量是最小的,此即"最优"。
将新出现的交易对象均匀的同步给相邻个体。txsyncLoop()主体也是一个无限循环,它的逻辑稍微复杂一些:首先有一个数据类型txsync{p, txs},包含peer和tx列表;通道txsyncCh用来接收txsync{}对象;txsyncLoop()每次循环时,如果从通道txsyncCh中收到新数据,则将它存入一个本地map[]结构,k为peer.ID,v为txsync{},并将这组tx对象发送给这个peer;每次向peer发送tx对象的上限数目100*1024,如果txsync{}对象中有剩余tx,则该txsync{}对象继续存入map[]并更新tx数目;如果本次循环没有新到达txsync{},则从map[]结构中随机找出一个txsync对象,将其中的tx组发送给相应的peer,重复以上循环。
四、geth启动流程
五、普通转账交易流程
-----------------------------------------------------------------------------------
PalletOne——区块链世界的IP协议 高性能分布式账本
更多详情关注脉冲资本公众号。
感谢链向财经!