在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
本文微信公众号 月牙寂道长 文章链接为:https://mp.weixin.qq.com/s/sMZC79DLS3ITTly8cSHwZw 本文图片可能不太清晰,看清晰版本的,可以看原文链接微信公众号链接。
以太坊go-ethereum源码的模块划分非常清晰,所以其各个模块,几乎是相互独立的。
有一个以太坊的架构图: 图片来自:https://blog.csdn.net/s_lisheng/article/details/77990523 (已得到原作者转载许可)
以太坊源码分析---go-ethereum之MPT(Merkle-Patricia Trie) 是讲解其中Trie模块 以太坊源码分析---go-ethereum之event 是讲解event模块的
此次分析的是事件rpc模块 代码结构如下 github.com/ethereum/go-ethereum/rpc 版本为go-ethereum-1.8.9
其分层架构图为:
应用层:rpc server,rpc client 表示层:数据的编码 json 会话层:建立、终止、管理会话链接(http、inproc、ipc、subscription、websocket)
有了这个分层架构图,我们就可以分层进行源码分析了。
server 先从示例代码开始看,主要是看server的主要功能流程 github.com/ethereum/go-ethereum/rpc/server_test.go 上面准备了一个struct,有8个方法。 其中Echo,EchoWitchCtx、Sleep、Rets、Subscription为5个callback 其中Subscription又为subscription 79:创建了一个server 80:创建了一个service 82:关键步骤:将service注册到server中。 RegisterName 86:server中的service是2个,这个后面解释 90:从server中取出service 95:callbacks为5个。说明rpc方法的注册是要遵循标准的,并不是什么方法都是可以注册为callback 99:subscription为1个。
那么下面开始根据流程介绍 github.com/ethereum/go-ethereum/rpc/types.go rpc函数 40:服务的类型 41:rpc方法 42:参数 43:是否有ctx。可以参考test中的函数Echo,EchoWitchCtx 45:标识是否为subscribe。可以参考test中的函数Subscription 66:services的合集 67:callback的合集 68:Subscription callback的合集 50:service的名字 51:服务的类型 52:callback的合集 53:Subscription callback的合集 72:service的合集 74:run标志位
github.com/ethereum/go-ethereum/rpc/server.go 初始化创建server 47-51:创建新的server结构体 55:创建RPCService结构体 56:将rpcservice注册到server中。这是第一个注册的服务,名字为“rpc”。 这也是为什么在test中service数量为2的原因 const MetadataApi = "rpc" RPCService结构体其实就是Server的封装
关键步骤RegisterName 80-82:判断server中的services是否初始化了,没有则初始化 84:初始化一个service 85:获取要注册的service的类型(type) 86:获取要注册的service中的value(其中包含了方法) 95:关键步骤:根据reflect获取到的value,进行遍历过滤出callback和Subscription 96-108:根据service的name,查找是否已经存在在server中,如果存在,则刷新callback和Subscription 111:初始化service的name 112:初始化service的callbacks和Subscriptions 118:将service注册到server中
那么注册流程就是如此的。其中函数suitableCallbacks就不深入分析了。
那rpc服务注册好了,怎么提供服务呢?
这里面就涉及到了应用层与表示层之间的衔接 github.com/ethereum/go-ethereum/rpc/types.go 应用层与表示层的衔接是通过这个interface做到的
github.com/ethereum/go-ethereum/rpc/json.go json提供了两个方法NewCode与NewJsonCodec来构建基于json的ServerCodec
表示层与会话层是如何连接的呢
http、inproc、ipc的衔接过程 以上是websocket的衔接过程
我们以http为例 github.com/ethereum/go-ethereum/rpc/http.go 创建httpserver
以上是httpserver的入口ServerHttp 184-187:ctx的准备 190:创建了一个jsonCodec 194:进入到server的处理流程 ServeSingleRequest
github.com/ethereum/go-ethereum/rpc/server.go 这里提供了两个入口。其中关键的参数为ServerCodec ServeCodec ServeSingleRequest 两者最大的差别在于参数SingleShot为true还是false。表明的是,一次是单个Request还是多个Request。
下面进入到server的入口 131-141:当服务退出后,资源释放。其中139,将codec释放掉
144 :ctx 153-159:将codec放入到set中 继续 163:从codec中读取request 164-173:判断读取过程是否有错误,有,则退出 177-189:判断server是否正常运行,不是,则退出
最终的流程到了这里 191-198:singleshot,指的是单个request。 其中batch为单个request包含了多个req 201-207:是多个requests的处理
exec是执行rpc的最终地方 334:关键步骤:handle执行rpc函数的地方,并返回Response。 337:将Response通过codec写入,返回给调用者 344:subscribe的回调函数执行
execbatch与exec不同的地方就是,一次性有多个request,遍历执行。
最终的执行rpc函数地方handle
最终的地方在这里 296-309:准备rpc call的参数 312:执行rpc函数
看看Response是怎么返回给用户的 github.com/ethereum/go-ethereum/rpc/json.go 调用了encode 而encoder的参数是io.ReadWriteCloser
github.com/ethereum/go-ethereum/rpc/http.go 回到http入口,初始化的时候,将body传入了NewJSONCodec中。
到此server的流程全部打通
client client的流程相对会简单一些 github.com/ethereum/go-ethereum/rpc/client.go 入口为Dial 针对不同类型的,有不同的入口
还是以http为例 github.com/ethereum/go-ethereum/rpc/http.go 构建了一个Client client构建好了,就要调用Call了 199:http模式下调用的sendhttp 301:非http模式下调用send
92:doRequest是关键步骤 99:读取Response 97:解码Response 110:将结果返回
132:将请求用json编码 140:关键步骤:client.Do,这里就是最终构建发送http链接请求的地方 144:状态码返回检查 147:返回resp
client的流程相对要简单的多,就不继续细化分析。
龚浩华 月牙寂道长 QQ 29185807 2018年09月06日 如果你觉得本文对你有帮助,可以转到你的朋友圈,让更多人一起学习。 第一时间获取文章,可以关注本人公众号:月牙寂道长,也可以扫码关注 |
请发表评论