区块链技术架构研究在路上

以太坊go-ethereum源码解析(一)p2p网络通信协议(1

2018-04-17  本文已影响759人  PONPON__

这段时间在看以太坊开源在github的go-ethereum代码,go语言实现的区块链技术,后续会根据各个模块,推出一系列go-ethereum源码解析文章。

开头

打开工程第一眼看到的就是p2p的package。没办法,谁让我在上家公司做的就是p2p协议的分布式缓存加速,一下子就对p2p来兴趣了。p2p协议也不是啥新鲜玩意儿了,注意这里的p2p是网络上的peer-to-peer,可不是互联网借贷的p2p。

p2p协议

经常下片,特别是岛国爱情动作片的男同胞们都知道BT种子或者磁力链接,而BT下载所用协议BitTorrent就是最经典的公有p2p通信协议。我们平时看电影、看剧、看网综用到的爱奇艺客户端腾讯客户端PPLive客户端(注意,是客户端,不是web网页端),听歌时用到的酷狗QQ音乐,以及最常用的下载软件迅雷,用的都是基于BitTorrent简化后的私有p2p协议。

作为处女座的我,就是爱钻牛角尖。个人认为,严格意义上说,p2p协议应分为广义p2p狭义p2p

非真正的去中心化的广义p2p网络协议。

p2p网络

所谓p2p(peer-to-peer)就是网络上节点到节点之间的连接,这是一种网络拓扑类型。这里的节点指的就是你的计算机或者移动设备上跑着的某个实现有一致p2p通信协议的应用。如上图所示,当你的计算机或者移动设备(如 peer 7)第一次要加入这个p2p网络,没有介绍人,上哪去找这个p2p网络的入口?这里一般有两种方式,用于加入到p2p网络中:

例如DHT(Distributed Hash Table)就采用的第一种接入方式(以太坊采用的Kademlia-like算法就是DHT的一种实现算法),而大部分p2p协议应用都会有类tracker服务器的存在,tracker本身就是一个已知的网络中心。tracker宕机不可用,或者已知的peer节点已经下线inactive了,那也就无从进入这个p2p网络了。
因此,我个人认为只有当peer加入到p2p网络(我将这种不考虑接入方式的p2p网络,理解为狭义p2p网络)后,才是真正的去中心化。但p2p网络的接入也是p2p通信协议的重要环节之一,所以.....

狭义p2p网络协议的去中心化

相比传统的C/S网络架构,p2p架构中最重要的特点在于:其网络中的peer之间在地位和功能上是平等的。虽然每个peer可能处理不同的请求,实际提供的资源在具体量化后也会有所差异,但它们都能同时既消耗资源又提供资源。如果把整个p2p网络中的所有资源(包括但不限于运算能力、存储空间、网络带宽等)视为一个总量,那么p2p网络中的资源分布,是分散于各个peer中的。从这一点粗发,p2p网络架构天然是去中心化的、分布式的。

在p2p网络中,peer间联系可分为无结构化的和结构化的。这里简单介绍下,无结构化的p2p网络优点在于组建方便,对网络无结构要求,即使有大量peer同时加入到网络或者下线离开,整个网络也会很稳定。而结构化的优点在于数据查询效率高(这里可以去研究下DHT相关的知识,捞个DHT爬虫的代码看看就有点B数了),缺点是当大量peer同时加入到网络或者下线离开,网络整体稳定性差,性能受影响(emma 不过多展开解释,依旧出门左转 google下DHT,了解下Kademlia,bucket等原理)。

以太坊的p2p协议

以太坊的p2p网络特征:

  1. 网络中随时可能存在一些个体加入和离开网络的情况,但同一时间内大量新旧个体同时发生加入或离开的概率很低(除非那些大矿场同时断电了,哈哈哈)。
  2. 理想状态下每个区块存储的数据应该是相同的。但实际情况会因为广播同步的延迟,导致区块的数据不一致。设计通信机制当然是希望尽可能的高度一致。所以在以太坊p2p网络中,查找数据时,不需要针对某些特定区域以提高效率,更不需要向整个网络猛发送请求。

p2p通信的协议管理模块ProtocolManager

直接上代码了,eth/handler.go

eth/handler.go
以太坊中,管理p2p通信的顶层结构体叫eth.ProtocolManager,它也是eth.Ethereum的核心成员变量之一。UML关系如下图:
图片来自:https://img-blog.csdn.net/20171108141219288?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGVhc3ByaW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
UML图

ProtocolManager主要成员包括:

peertSet来表示缓存相邻peer列表,peerSet map 中的peer表示网络中的一个远端节点。

peertSet
ProtocolManager通过各种通道(newPeerChtxsyncChquitSyncnoMorePeers)和事件订阅(eventMuxtxSubminedBlockSub)的方式,接收和发送包括交易和区块在内的数据更新。

Fetcher存储所有其他peer发送来的有关新数据的Announce消息,并在自身对照后,处理相应的获取请求。

Fetcher
Downloader负责所有向相邻peer主动发起的同步流程。
以太坊的p2p网络中,所有进行通信的两个peer都必须率先经过相互的注册(register),并被添加到各自缓存的peer列表,也就是peerSet对象中,这样的两个peers,就可以称为“相邻”。所以,只要两个peer,如果处于可通信状态,则必定已经“相邻”。
peerSet Register / Unregister

Start(maxPeers int):开启p2p网络

Start(maxPeers int)函数是ProtocolManager的启动函数,它会在eth.Ethereum.Start(maxPeers)中被主动调用。ProtocolManager.Start(maxPeers)会启用4个goroutine去分别执行4个函数。

Start(maxPeers int)
来看下这四个goroutine都在做些什么

pm.txBroadcastLoop()

pm.minedBroadcastLoop()

pm.syncer()


这里的最优peer(BestPeer)指的是什么呢


pm.txsyncLoop()

以上4个goroutine是ProtocolManager向相邻peer主动发起信息的过程。可以看得出,向其他peer主动发起的通信中,按照数据类型可分两类:交易tx和区块block;而按照通信方式划分,亦可分为广播新的单个数据和同步一组同类型数据,这样简单的两两配对,便可组成上述四段流程。

handle(p *peer):相邻peer的回调函数

对于peer间通信而言,除了主动向对方peer发起通信(比如Start()中启动的四个goroutine)之外,还需要一种由对方peer主动调用的数据传输,这种传输不仅仅是由对方peer发给己方,更多的用法是对方peer主动调用一个函数让己方发给它们某些特定数据。这种通信方式,在代码实现上适合用回调来实现。

ProtocolManager.handle()就是这样一个函数,它会在ProtocolManager对象创建时,以回调函数的方式埋入每个p2p.Protocol对象中(实现了Protocol.Run()方法)。之后每当有新peer要与己方建立通信时,如果对方能够支持该Protocol,那么双方就可以顺利的建立并开始通信。


handleMsg的代码比较长,这里就不过多介绍了,有兴趣可以找源码看一下,逻辑简单就是根据不同的消息类型,进行相应的处理。

关于以太坊p2p协议第一篇文章先写到这,内容还有很多,时间有限,只能拆开一部分一部分来写~

参考文献:
https://blog.csdn.net/teaspring/article/details/78455046

上一篇下一篇

猜你喜欢

热点阅读