序言
梁川,资深架构师,拥有10年以上一线技术从业经验。梁先生精通Erlang、python、Golang、java; 擅长各种平台高并发大流量架构设计与实施; 精通Ethereum区块链以及相关技术; 通读Go-Ethereum源码。
我们开辟《星际源码》专栏,请梁先生为我们讲讲高深的架构技术。梁先生将会不定期的随时分享他在工作中的感想与新知,希望与感兴趣的开发者朋友们一同学习成长!
go-libp2p-host Connect 源码分析
Connect 过程解析
• go-libp2p-host中定义了Host接口,它有几个实现都在go-libp2p包中,我们关注basic包中的BasicHost实现,因为IPFS用了这个实现
• Connect主要是dial peer并完成握手,再去交换Identify信息,Identify信息后文有提到,文中反复提到的ID是指Identify协议的名称
• 发起连接一端我们称为 from ,被连接一端成为 to ,则有如下过程在两端建立连接,这个时序图只为理解交互过程,对于阅读代码并无实际参考价值;
1、2是指from和to要先启动libp2p的host再做后续操作,在这一步已经执行了Swarm.Listen并启动了handlencoming线程来accept连接,并且为ID协议注册了StreamHandler,为通道上的连接注册了ConnHandler;
3 from端通过Connect来dial to端;
4、to端的newConnHandler被触发,这个方法调用了IdentifyConn;
5、于此同时from端发起连接请求成功后会去调用IdentifyConn;
6~14双方行为一致,握手成功后激活ID协议的StreamHandler触发requestHandler来向对方发送Identify消息,两端用各自的responseHandler来处理Identify消息,并将Identify信息放入peerstore中。
看代码,简单看一下 Connect 过程
首先看看接口怎么定义的,因为所有的逻辑都要建立在 Connect 的基础上,所以以 Connect 为入口来欣赏 BasicHost 的实现过程
Host 是什么以及 Connect 要做的事情通过注视都能看出来,只是看到 TODO 时感到有些遗憾,这个 Relay 足足耽误我几天时间,看来读代码应该先读接口.
From 端 Connect 的实现过程
注视跟接口描述的差不多,如果没有可用连接就会去尝试 dial 这个 peer 并且把它加入到 peerstore 中
dialPeer虽然很复杂但最终是调用到IdentifyConn方法上,我们直接看重点
To 端 Listen 的实现过程
·BasicHost.NewHost
·Swarm.Listen
创建BasicHost时的net参数是Network接口的Swarm实现,那么启动过程中会调用Listen方法,下面代码贴出了关键部分,AddListenAddr方法的list.Accept()对上文提到的握手信息进行了处理,然后要upgrade这个连接,再去触发BasicHost中指定的net.SetConnHandler(h.newConnHandler)
Accept是Listener接口的方法
接口定义在go-libp2p-transport/transport.go中
实现定义在go-libp2p-transport-upgrader/listener.go中,我们看看如何实现
这个实现太简单了,只是在读incoming channel,所以线索是谁在往incoming chainnel中写数据,于是找到了handleIncoming()方法,以TcpTransport实现为例,可以看到是在UpgradeListener时启动的handleIncoming
以上已经把关键点都列出来了,略去了细节,如果想去扩展 go-libp2p 包调整握手协议组建私网等掌握这些内容应该已经够了。