本文微信公众号 月牙寂道长 文章链接为: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日
如果你觉得本文对你有帮助,可以转到你的朋友圈,让更多人一起学习。
第一时间获取文章,可以关注本人公众号:月牙寂道长,也可以扫码关注