以太坊之node生命周期详解

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

代码:github.com/ethereum/go-ethereum
版本:96157a897be2032be5fdb87f947fbe5df8a53bd4

一、官方介绍

node包实现了一个可以包含多种协议的以太坊节点。

节点是服务得集合,这些服务使用共享资源提供的RPC API。服务还可以提供Devp2p协议,当启动节点实例时,这些协议会连接到Devp2p网络。

1、节点生命周期

节点对象得生命周期分为三个状态,INITIALIZINGRUNNINGCLOSED。状态流转如下:

●───────┐
     New()
        │
        ▼
  INITIALIZING ────Start()─┐
        │                  │
        │                  ▼
    Close()             RUNNING
        │                  │
        ▼                  │
     CLOSED ◀──────Close()─┘

当创建一个对象的时候,我们会分配基础资源,如数据目录等,以及将节点状态重制为INITIALIZING状态。Lifecycle对象,RPC APIp2p网络协议可以在这个状态注册。初始化得时候可以进行打开k-v数据库等基础操作。

当我们注册完想要注册的服务之后,我们就可以启动节点,这会将节点的状态变为RUNNING。启动节点将会启动所有已经注册的Lifecycle对象,启动RPCp2p网络。注意,节点状态为RUNNING得时候,Lifecycle对象,APISp2p网络都不被允许注册。

关闭节点将会释放所有持有的资源。关闭操作会根据节点出于得状态来执行相应的操作:

  • 当节点处于INITIALIZING状态时,我们关闭节点,将会释放数据目录相关的资源;

  • 当节点处于RUNNING状态时,我们关闭节点,还会停止所有Lifecycle对象,并且关闭RPCp2p网络。

我们总是应该关闭节点,尽管节点可能没有被启动。

2、节点资源

节点实例使用的所有文件系统资源都位于称为数据目录的目录中。可以通过配置来覆盖每个资源的位置。数据目录是可以选得,如果没有设置,本地资源的位置也没有指定,节点将会在内存中创建资源。

要访问devp2p网络,节点需要进行配置并启动p2p服务。devp2p网络上得每个主机都有一个唯一标识符–node key。节点实例在重新启动之后会保留这个标识符。节点还会加在静态以及受信任得节点列表,并确保关于其他主机的相关数据被保存。

节点可以启动基于HTTPWebSocket或着IPCJSON_RPC服务器。RPC模块会被提供在这些注册服务得地址上。用户可以将任何地址注册为一个RPC地址。节点内置提供了"debug",“admin”,"web3"模块。

服务实现可以通过服务上下文来打开levelDB数据库。节点选择每个数据库运行的文件系统位置。如果节点没有配置数据目录,数据库将会创建在内存中。

节点还创建了加密的以太坊账户秘钥得共享存储。服务可以通过上下文获取到账户管理器,并使用它。

3、资源共享

多个节点实例可以共享一个数据目录,如果他们的实例名称不同(通过设置“名称”配置)。共享行为基于资源类型。

与devp2p相关的资源(如节点标识、静态/受信节点列表、已知主机数据库)会被存储在跟实例名相同的目录中。因此,多个实例使用相同的数据目录时,会将信息存储在数据目录不同的子目录下。

LevelDB数据库也存储在实例子目录中。如果多个节点实例,使用同一个数据目录,打开相同名称的数据库将会为每个实例创建一个数据库。

账户秘钥会被使用了相同数据目录得节点实例共享,除非使用KeyStoreDir选项更改了配置。

二、源码分析

1、个人认知

从上面官方的介绍中,我们可以发现节点的角色更加倾向于一个容器。

  • 拥有内置得服务
  • 通过注册接口来注册新的服务
  • 所有服务都在容器被启动得时候启动,在容器关闭的时候被关闭
  • 拥有自己的数据目录(无论是在内存还是文件系统中)
  • 容器得表现很大程度取决于配置
  • 有自己的标识符且唯一
  • 可与其他容器共享数据目录(文件系统)

因此,我们只需要从看待容器得角度来学习节点,就会相对简单。

2、源码解析

a、目录结构
  • api.go:实现了内置得服务,如admindebugweb3
  • config.go:包含了node相关的配置,外部注册的服务也可以进一步得扩展这些值。
  • defaults.go:配置的默认值。
  • endpoints.go:启动HTTP RPC服务。
  • errors.go:格式化错误输出。
  • lifecycle.goLifecycle接口。
  • node.go:实现了node的相关功能,比如注册服务等。
  • rpcstack.gohttprpc服务得相关实现。
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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值