代码:github.com/ethereum/go-ethereum
版本:96157a897be2032be5fdb87f947fbe5df8a53bd4
一、官方介绍
node包实现了一个可以包含多种协议的以太坊节点。
节点是服务得集合,这些服务使用共享资源提供的RPC API。服务还可以提供Devp2p协议,当启动节点实例时,这些协议会连接到Devp2p网络。
1、节点生命周期
节点对象得生命周期分为三个状态,INITIALIZING,RUNNING,CLOSED。状态流转如下:
●───────┐
New()
│
▼
INITIALIZING ────Start()─┐
│ │
│ ▼
Close() RUNNING
│ │
▼ │
CLOSED ◀──────Close()─┘
当创建一个对象的时候,我们会分配基础资源,如数据目录等,以及将节点状态重制为INITIALIZING状态。Lifecycle对象,RPC API和p2p网络协议可以在这个状态注册。初始化得时候可以进行打开k-v数据库等基础操作。
当我们注册完想要注册的服务之后,我们就可以启动节点,这会将节点的状态变为RUNNING。启动节点将会启动所有已经注册的Lifecycle对象,启动RPC和p2p网络。注意,节点状态为RUNNING得时候,Lifecycle对象,APIS和p2p网络都不被允许注册。
关闭节点将会释放所有持有的资源。关闭操作会根据节点出于得状态来执行相应的操作:
-
当节点处于
INITIALIZING状态时,我们关闭节点,将会释放数据目录相关的资源; -
当节点处于
RUNNING状态时,我们关闭节点,还会停止所有Lifecycle对象,并且关闭RPC和p2p网络。
我们总是应该关闭节点,尽管节点可能没有被启动。
2、节点资源
节点实例使用的所有文件系统资源都位于称为数据目录的目录中。可以通过配置来覆盖每个资源的位置。数据目录是可以选得,如果没有设置,本地资源的位置也没有指定,节点将会在内存中创建资源。
要访问devp2p网络,节点需要进行配置并启动p2p服务。devp2p网络上得每个主机都有一个唯一标识符–node key。节点实例在重新启动之后会保留这个标识符。节点还会加在静态以及受信任得节点列表,并确保关于其他主机的相关数据被保存。
节点可以启动基于HTTP,WebSocket或着IPC得JSON_RPC服务器。RPC模块会被提供在这些注册服务得地址上。用户可以将任何地址注册为一个RPC地址。节点内置提供了"debug",“admin”,"web3"模块。
服务实现可以通过服务上下文来打开levelDB数据库。节点选择每个数据库运行的文件系统位置。如果节点没有配置数据目录,数据库将会创建在内存中。
节点还创建了加密的以太坊账户秘钥得共享存储。服务可以通过上下文获取到账户管理器,并使用它。
3、资源共享
多个节点实例可以共享一个数据目录,如果他们的实例名称不同(通过设置“名称”配置)。共享行为基于资源类型。
与devp2p相关的资源(如节点标识、静态/受信节点列表、已知主机数据库)会被存储在跟实例名相同的目录中。因此,多个实例使用相同的数据目录时,会将信息存储在数据目录不同的子目录下。
LevelDB数据库也存储在实例子目录中。如果多个节点实例,使用同一个数据目录,打开相同名称的数据库将会为每个实例创建一个数据库。
账户秘钥会被使用了相同数据目录得节点实例共享,除非使用KeyStoreDir选项更改了配置。
二、源码分析
1、个人认知
从上面官方的介绍中,我们可以发现节点的角色更加倾向于一个容器。
- 拥有内置得服务
- 通过注册接口来注册新的服务
- 所有服务都在容器被启动得时候启动,在容器关闭的时候被关闭
- 拥有自己的数据目录(无论是在内存还是文件系统中)
- 容器得表现很大程度取决于配置
- 有自己的标识符且唯一
- 可与其他容器共享数据目录(文件系统)
因此,我们只需要从看待容器得角度来学习节点,就会相对简单。
2、源码解析
a、目录结构
api.go:实现了内置得服务,如admin,debug,web3。config.go:包含了node相关的配置,外部注册的服务也可以进一步得扩展这些值。defaults.go:配置的默认值。endpoints.go:启动HTTP RPC服务。errors.go:格式化错误输出。lifecycle.go:Lifecycle接口。node.go:实现了node的相关功能,比如注册服务等。rpcstack.go:http及rpc服务得相关实现。
b、生命周期流程解析
为了方便理解,我们采用自顶而下得方式来解析:
1、以太坊入口中node相关
本文使用客户端是go得实现,所以我们从cmd/geth/main.go说起。
在main.go中入口为main函数,同时,在main.go中有init函数,init函数将app变量封装好,等待main函数执行app.Run函数。
func init() {
// Initialize the CLI app and start Geth
app.Action = geth //执行的动作
app.HideVersion = true // we have a command to print the version
app.Copyright = "Copyright 2013-2021 The go-ethereum Authors"
app.Commands = []cli.Command{
//子命令
// See chaincmd.go:
initCommand,
importCommand,
exportCommand,
importPreimagesCommand,
exportPreimagesCommand,
copydbCommand,
removedbCommand,
dumpCommand,
dumpGenesisCommand,
inspectCommand,
// See accountcmd.go:
accountCommand,
walletCommand,
// See consolecmd.go:
consoleCommand,
attachCommand,
javascriptCommand,
// See misccmd.go:
makecacheCommand,
makedagCommand,
versionCommand,
versionCheckCommand,
licenseCommand,
// See config.go
dumpConfigCommand,
// See cmd/utils/flags_legacy.go
utils.ShowDeprecated,
}
sort.Sort(cli.CommandsByName(app.Commands))
app.Flags = append(app.Flags, nodeFlags...)
app.Flags = append(app.Flags, rpcFlags...)
app.Flags = append(app.Flags, consoleFlags...)
app.Flags = append(app.Flags, debug.Flags...)
app.Flags = append(app.Flags, debug.DeprecatedFlags...)
app.Flags = append(app.Flags, whisperFlags...)
app.Flags = append(app.Flags, metricsFlags...)
app.Before = func(ctx *cli.Context) error {
return debug.Setup(ctx)
}
app.After = func(ctx *cli.Context) error {
debug.Exit()
prompt.Stdin.Close() // Resets terminal mode.
return nil
}
}
func main() {
if err := app.Run(os.Args); err != nil {
//执行
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
我们从上面看到geth命令执行的动作是geth函数,geth基于命令行参数创建一个默认节点,并运行服务。
// geth is the main entry point into the system if no special subcommand is ran.
// It creates a default node based on the command line arguments and runs it in
// blocking mode, waiting for it to be shut down.
func geth(ctx *cli.Context) error {
if args := ctx.Args(); len(args) > 0 {
//geth是一个单独命令,如果附加了其他的就不能调用geth函数了。
return fmt.Errorf("invalid command: %q", args[0])
}
prepare(ctx) //进行一些检测,并启动一些监控
stack, backend := makeFullNode(ctx) //创建full节点
defer stack.Close() //总是要关闭,注意上面官方的要求
startNode(ctx, stack, backend) //开始节点
stack.Wait() //等待节点关闭,阻塞
return nil
}
因为prepare函数与我们要讲解得node流程无关,所以只讲解makeFullNode以及startNode两个函数。
a、makeFullNode函数
makeFullNode函数载入配置,并创建eth服务。
// makeFullNode loads geth configuration and creates the Ethereum backend.
func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
stack, cfg := makeConfigNode(ctx) //1、应用配置,并创建node
backend := utils.RegisterEthService(stack, &cfg.Eth) //2、注册eth服务
checkWhisper(ctx)
// Configure GraphQL if requested
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
ut

本文详细介绍了以太坊节点的生命周期,包括官方介绍和源码分析。节点分为初始化、启动和关闭三个状态,资源管理涉及数据目录、网络和账户管理。节点通过注册服务启动和关闭。源码解析部分探讨了目录结构、生命周期流程,强调了节点初始化、启动和关闭的函数作用。
最低0.47元/天 解锁文章
425

被折叠的 条评论
为什么被折叠?



