Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

bytom源码分析-启动与停止

蔡健雅的高跟鞋
181 0 0
作者使用MacOS操作系统,其他平台也大同小异! x/ t& g/ @8 ~# O; O# F( ^; U3 C
Golang Version: 1.80 T. i: f% _/ a

  J" i, F8 w. F% @3 w预备工作
- _0 f. w% h2 N( s6 n" I, e7 ^编译安装; B4 f! O2 ^' n, o- n
详细步骤见官方 bytom install' m2 k7 h. G6 w/ d5 D; L
设置debug日志输出
3 J+ |# N& T* t开启debug输出文件、函数、行号等详细信息
5 d4 G3 ~9 ]8 [3 a! m( T
  1. export BYTOM_DEBUG=debug
复制代码

: X' ?+ O! ]/ T( B初始化并启动bytomd
0 E, H9 V+ T5 }$ m6 s! a初始化
; v' E7 R( e: S
  1. ./bytomd init --chain_id testnet
复制代码

& p; C$ u5 z) h3 M/ Tbytomd目前支持两种网络,这里我们使用测试网# V7 L) T0 F2 P: E
mainnet:主网
% [  ?! r" r: W# v# p  J" l. t- M, Atestnet:测试网
  L% ?% f' L- U启动bytomd
4 I# E4 d+ Q) d" L$ E  L
  1. ./bytomd node --mining --prof_laddr=":8011"- Z$ w% K  j3 F+ ]  ^1 x
  2. –prof_laddr=":8080"  // 开启pprof输出性能指标
复制代码
7 y, `7 K4 {) G: v( n4 `
访问:http://127.0.0.1:8080/debug/pprof/
; k! _0 G5 m3 i3 jbytomd init初始化
8 s/ [; _0 y; i+ w! N3 i: c入口函数
4 j7 l1 @8 v- z4 B
  1. ** cmd/bytomd/main.go **
    5 S' V2 J9 B+ T$ D
  2. func init() {% u( x; @# P/ V' m) n
  3.         log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableColors: true})
    $ E* N4 p2 ]- ]+ [1 o
  4.         // If environment variable BYTOM_DEBUG is not empty,5 z$ p4 K* M% P+ h% M, \2 T0 }+ \
  5.         // then add the hook to logrus and set the log level to DEBUG
    - R: u: a# h3 P, C8 a) b
  6.         if os.Getenv("BYTOM_DEBUG") != "" {$ h( [8 X: v! q* p% r
  7.                 log.AddHook(ContextHook{})
    # x* Z) ^2 ^* J
  8.                 log.SetLevel(log.DebugLevel)
    $ Y. U9 j6 A# I5 a
  9.         }
    4 Y4 r  ~* i9 I0 T1 G& J
  10. }
    7 Z( S  M- [% y% H: T+ ?
  11. func main() {4 X8 \- E% f. U& z" B3 ?8 \
  12.         cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv(config.DefaultDataDir()))
    2 o) K+ k9 d, f2 x4 j
  13.         cmd.Execute()
    ! C$ U3 F: q3 u7 i& T  d  Y% r
  14. }
复制代码
% Q. T9 n  i4 w% w' p% B' \! e
init函数会在main执行之前做初始化操作,可以看到init中bytomd加载BYTOM_DEBUG变量来设置debug日志输出2 G' M, x3 ~; d  C6 R, p0 ?
command cli传参初始化- s% A" A/ v9 J+ ]; ?
bytomd的cli解析使用cobra库  U0 H! B' A! k" s* i
** cmd/bytomd/commands **/ d! H9 d  b5 [& l
cmd/bytomd/commands/root.go6 e4 l, N8 Q2 g1 g
初始化–root传参。bytomd存储配置、keystore、数据的root目录。在MacOS下,默认路径是~/Library/Bytom/cmd/bytomd/commands/init.go
5 g& B0 ?7 u! z- O% N% t3 |% a. _初始化–chain_id传参。选择网络类型,在启动bytomd时我们选择了testnet也就是测试网络cmd/bytomd/commands/version.go
% g0 e: g5 u' b& g& o# x初始化version传参
cmd/bytomd/commands/run_node.go; L) O" F9 h9 q* f: Q1 d
初始化node节点运行时所需要的传参, @% w3 r9 r+ n& T

) f9 D( }, @  u0 t/ v0 ?初始化默认配置
/ L' r# A* v# V8 Z# E: z6 ^+ T3 z用户传参只有一部分参数,那节点所需的其他参数需要从默认配置中加载。/ X+ F& P, `$ m  o, v6 S; `
  1. ** cmd/bytomd/commands/root.go **
    " P1 s9 A& e2 ~
  2. var (
    5 P$ l. {$ B2 g
  3.         config = cfg.DefaultConfig()9 `+ l/ E7 F$ [1 n* y+ g; V
  4. )
    + T# ^- w/ Z2 |2 q# Z7 \5 x
  5. 在root.go中有一个config全局变量加载了node所需的所有默认参数# C% |9 x" _; |7 X* M
  6. // Default configurable parameters.' z: [+ f$ g# y' Y& I
  7. func DefaultConfig() *Config {! l6 R# ~# R) v  K" Y. o
  8.         return &Config{/ _: a9 ]* [7 v) L5 J
  9.                 BaseConfig: DefaultBaseConfig(),  // node基础相关配置9 B2 e2 ]/ e" @- J5 ^6 ~
  10.                 P2P:        DefaultP2PConfig(),   // p2p网络相关配置0 O% A- u: k2 x9 a
  11.                 Wallet:     DefaultWalletConfig(),   // 钱包相关配置) C9 O7 z# e: }) y2 w. _
  12.                 Auth:       DefaultRPCAuthConfig(),  // 验证相关配置, E; B7 q) c3 m% e+ d! E
  13.                 Web:        DefaultWebConfig(),   // web相关配置2 n2 g1 N- R/ o* @8 G2 o$ D2 m
  14.         }
    & E" e3 k( X' N' r
  15. }
复制代码

* T9 X6 R/ y) _3 b6 q4 Y- [后面的文章会一一介绍每个配置的作用
4 r9 o: Z" a) X( g+ J' G, c* }bytomd 守护进程启动与退出3 W& ^  n1 i3 ?$ P
  1. ** cmd/bytomd/commands/run_node.go **
    % T9 `) ^) z- U. e* T! S
  2. func runNode(cmd *cobra.Command, args []string) error {1 X3 D: \; A1 t" z% P! e  q' o
  3.         // Create & start node$ \# U0 M. |) P' G& |: O8 N
  4.         n := node.NewNode(config)3 s% M% N, |! X5 a( @3 n9 |+ D
  5.         if _, err := n.Start(); err != nil {0 f; Q7 s1 n* L
  6.                 return fmt.Errorf("Failed to start node: %v", err)
    & Z- o8 R- {3 y
  7.         } else {' ]: r& r3 n( \7 X9 ]
  8.                 log.WithField("nodeInfo", n.SyncManager().Switch().NodeInfo()).Info("Started node")9 Y' n& w2 V8 q  n
  9.         }
    ! B5 W% ?9 {5 I) X* e
  10.         // Trap signal, run forever.* _% N, O+ W6 m6 M4 h
  11.         n.RunForever()
    2 w6 G$ T( w8 f+ G% e
  12.         return nil. ~9 b, C& t( g( _% r; q# l( s/ R
  13. }
复制代码

0 t) ~+ w% x* \runNode函数有三步操作:
  k; Y9 F- I' q; j1 Xnode.NewNode:初始化node运行环境
0 r7 m! v% ~' Nn.Start:启动node
( [* y8 {' O% X! x, H7 i6 _n.RunForever:监听退出信号,收到ctrl+c操作则退出node。在linux中守进程一般监听SIGTERM信号(ctrl+c)作为退出守护进程的信号* _* P+ y6 C/ G1 |8 A
初始化node运行环境
& O% N% k+ ?1 L在bytomd中有五个db数据库存储在–root参数下的data目录$ v1 X/ M( [0 j* w
accesstoken.db    // 存储token相关信息(钱包访问控制权限)trusthistory.db   // 存储p2p网络同步相关信息txdb.db           // 存储交易相关信息txfeeds.db        //wallet.db         // 存储钱包相关信息
# I7 j) B7 y3 H' [& ?! g
  1. . q8 B" _. V3 \1 h5 Q* Q/ F8 c6 _
  2. ** node/node.go **  g8 g" m8 o! j6 l
  3. func NewNode(config *cfg.Config) *Node {# Y5 r/ @: J" A3 O9 U) f$ F' H  ]8 i) j
  4.         ctx := context.Background()5 i6 C  d1 B1 L6 [, I$ r8 H. m; A5 Y
  5.         initActiveNetParams(config)# }. c* d/ W2 T
  6.         // Get store 初始化txdb数据库' D, @9 }/ ^- @* ]* E: `
  7.         txDB := dbm.NewDB("txdb", config.DBBackend, config.DBDir())
    9 r8 g! m% @! U( H# ]4 R% j
  8.         store := leveldb.NewStore(txDB)
    / R6 c4 y$ q2 V4 F1 s4 X
  9.   // 初始化accesstoken数据库1 n% n! _7 I; |' |
  10.         tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir())
    % B4 U' `6 h4 b. }7 o9 ^6 ]
  11.         accessTokens := accesstoken.NewStore(tokenDB)
    ( ~) L% Q9 N0 \9 j+ d
  12.   // 初始化event事件调度器,也叫任务调度器。一个任务可以被多次调用
    , R# S1 u% N- ~9 Z2 p' ^
  13.         // Make event switch
    5 s. K" [1 D2 x3 J% z5 \
  14.         eventSwitch := types.NewEventSwitch()8 S# t, c6 |. I
  15.         _, err := eventSwitch.Start()' {* \1 Q3 ]  A+ \# d
  16.         if err != nil {
    3 u) |" G2 ~5 g% p! M
  17.                 cmn.Exit(cmn.Fmt("Failed to start switch: %v", err))
    5 z5 V5 p* f9 N5 s/ V6 x
  18.         }
    7 U3 m& D8 {4 u6 v& L" p$ i
  19.   // 初始化交易池
    : d, J+ [, V2 p0 u" w) R
  20.         txPool := protocol.NewTxPool()
    3 v( J  l" D( w: ~5 ]  \
  21.         chain, err := protocol.NewChain(store, txPool)
    " W( j4 [4 a7 A
  22.         if err != nil {
    ; u( @' c' B, r# l* G
  23.                 cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))5 [& ]* }8 U. q. d
  24.         }
    ' z1 L9 K0 c5 N( f! E
  25.         var accounts *account.Manager = nil
    + K$ G3 s; y3 \/ y
  26.         var assets *asset.Registry = nil
    : N  @* O) y! X6 i. j& r
  27.         var wallet *w.Wallet = nil& X% x; ~6 Q  }& T; h+ e* t; n
  28.         var txFeed *txfeed.Tracker = nil
    6 F1 k. R# p. i, C" E
  29.   // 初始化txfeeds数据库
    : S' X# i, j' a  }
  30.         txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())% f: U. Z* ]2 C6 W) a  u9 [' y2 Q
  31.         txFeed = txfeed.NewTracker(txFeedDB, chain)
    6 c7 P  [: Q- h1 w2 l# W
  32.         if err = txFeed.Prepare(ctx); err != nil {! N" U( v4 e" N/ {+ Y
  33.                 log.WithField("error", err).Error("start txfeed")5 c1 u0 K+ @& I0 c
  34.                 return nil
    7 D3 V+ z0 I7 I$ E5 n
  35.         }
    . o6 j) ]. G* H7 a4 A  L7 H
  36.   // 初始化keystore! ]! \: i9 _' c% Y0 g
  37.         hsm, err := pseudohsm.New(config.KeysDir())
    - E1 V. F# Z3 Z& {+ T8 I0 ~
  38.         if err != nil {1 p0 F) g# H% X8 U, m& j
  39.                 cmn.Exit(cmn.Fmt("initialize HSM failed: %v", err))/ P9 p7 \  m  p' m. l
  40.         }
    + d( U' b- f" ?5 U* G
  41.   // 初始化钱包,默认wallet是开启状态+ K& H, h, G$ Q# n/ r6 D+ z
  42.         if !config.Wallet.Disable {
    $ h7 l& ~3 G: d6 R; [2 ~8 F# V* r- [
  43.                 walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())$ G& P% S4 Z( ?- B& P/ X/ W
  44.                 accounts = account.NewManager(walletDB, chain)
    * x; i5 _& e8 @5 j
  45.                 assets = asset.NewRegistry(walletDB, chain)
    ! D8 C0 |) s# U/ q! U' i- t
  46.                 wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain)/ s- x3 E; g+ L; Y: ^
  47.                 if err != nil {5 `( k  \) o$ i! ]
  48.                         log.WithField("error", err).Error("init NewWallet")
    ( }$ p1 b0 t3 N" F/ \
  49.                 }, U" {5 X2 n: f+ x7 o7 v# X* |" x
  50.                 // Clean up expired UTXO reservations periodically., v3 T+ J! e0 S; [6 p3 J7 d
  51.                 go accounts.ExpireReservations(ctx, expireReservationsPeriod)" I/ d9 k% G! P1 O2 Q* Z
  52.         }
    - P3 u: U6 M' g( d4 i; N
  53.         newBlockCh := make(chan *bc.Hash, maxNewBlockChSize)
    0 q  H; j) D# w( D6 v) Q  z
  54.   // 初始化网络节点同步管理
    * f3 n1 q( b' O) P" |1 r. r! R
  55.         syncManager, _ := netsync.NewSyncManager(config, chain, txPool, newBlockCh)
    ; A  Q3 Y- \3 @7 r4 |
  56.   // 初始化pprof,pprof用于输出性能指标,需要制定--prof_laddr参数来开启,在文章开头我们已经开启该功能& ~& d, f+ a! m7 i) W9 `
  57.         // run the profile server
    5 q! S# C4 A1 w, n) K
  58.         profileHost := config.ProfListenAddress
    * I/ g/ d8 F0 w
  59.         if profileHost != "" {3 J* B, D4 B" G2 a4 N- W9 s
  60.                 // Profiling bytomd programs.see (<a href="https://blog.golang.org/profiling-go-programs" target="_blank">https://blog.golang.org/profiling-go-programs</a>)
    ' ^* W7 s6 W7 Y8 N, j* ]' h
  61.                 // go tool pprof http://profileHose/debug/pprof/heap
    + |- H) \, _' V. T; ~* H# q
  62.                 go func() {
    ( B5 ]9 S7 D- \4 p
  63.                         http.ListenAndServe(profileHost, nil)( k# d6 K6 K6 S1 M5 a( s
  64.                 }()/ c* U0 t1 q4 D0 ~) y9 Z
  65.         }
    0 ?2 w3 z( }  c6 O
  66.   // 初始化节点,填充节点所需的所有参数环境
    , m4 h1 y9 e) r, K+ P& O" R3 x& F
  67.         node := &Node{+ ]5 w6 c* ]. G' j0 ~3 o' T+ T
  68.                 config:       config,  G+ Q# a! ~0 \# a8 F3 X
  69.                 syncManager:  syncManager,
    1 H4 [' I  Z5 C; D; g  I8 ]
  70.                 evsw:         eventSwitch,. M5 c( N( _% F  T  J/ S4 J
  71.                 accessTokens: accessTokens,$ d0 Z2 d! f" u/ x- R4 h4 U
  72.                 wallet:       wallet,
    0 ^' a* {* [# W
  73.                 chain:        chain,
    ; M, _2 `2 l8 u4 S1 b9 v& D
  74.                 txfeed:       txFeed,
    3 N" e8 i' q& n. C  |6 W- V
  75.                 miningEnable: config.Mining,5 Q. `8 _$ T6 y: a- U
  76.         }; f) Z- O: b9 O/ X) q
  77.   // 初始化挖矿
    / v' P* Z, o9 ]' ^. W! f, A: M
  78.         node.cpuMiner = cpuminer.NewCPUMiner(chain, accounts, txPool, newBlockCh)
    ! r0 ^' n7 D" K# P$ ^
  79.         node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, newBlockCh)- L8 N3 g  m0 b6 V- A( ^
  80.         node.BaseService = *cmn.NewBaseService(nil, "Node", node)
    ) N0 ^, @. m' {" G( G8 t7 J
  81.         return node; a: H; `( n+ t' B2 J3 Q
  82. }
复制代码
' @) G" I$ Z& _
目前bytomd只支持cpu挖矿,所以在代码中只有cpuminer的初始化信息6 J: n/ y  [: V) v9 O
启动node
; M: I3 ^* ~: X7 O! F
  1. ** node/node.go **
      p1 u- z- G( T% y3 I' y7 j/ r3 C
  2. // Lanch web broser or not: E! R# t( ?( j! Y$ r8 s4 D  c
  3. func lanchWebBroser() {
      ]( W4 E0 L5 k; Q2 z! l
  4.         log.Info("Launching System Browser with :", webAddress)
    # X! t) ~% d( P. N: L6 \7 @& {
  5.         if err := browser.Open(webAddress); err != nil {+ `& z3 [. I' E  X0 S
  6.                 log.Error(err.Error())5 J/ P) m  o! p, g
  7.                 return# @# c" Q/ f+ ]2 m8 p7 ?
  8.         }
    ' O+ C7 k( Z- e5 Y& z( `
  9. }
    3 O2 X! m) p9 N6 W% g
  10. func (n *Node) initAndstartApiServer() {
    $ D: M2 E; u6 ~# T1 q
  11.         n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)5 E0 F  u- z# \
  12.         listenAddr := env.String("LISTEN", n.config.ApiAddress), o3 A6 V' {: a$ L4 o
  13.         env.Parse()& U& k' K; a+ Q
  14.         n.api.StartServer(*listenAddr)4 `8 Y  t' ]# Y) N1 a/ w- }0 C
  15. }
    7 ^" ?6 m: e: ]6 s0 s
  16. func (n *Node) OnStart() error {
    + g' Z, R/ Y, Y+ Q7 D1 y2 S. r1 I
  17.         if n.miningEnable {) I# \0 I* c- }5 A- F& v
  18.                 n.cpuMiner.Start()& o& D) w" T7 f6 M6 Y% L
  19.         }
    # ]1 a0 `* [1 Q" S( `2 \: |
  20.         n.syncManager.Start()
    2 `$ [+ d0 h4 u/ b
  21.         n.initAndstartApiServer()
    5 k6 ~& h& w# a- @
  22.         if !n.config.Web.Closed {
    ' G# e/ z$ N8 T9 ^; ~
  23.                 lanchWebBroser()
    - ~( C+ d* v8 n8 u
  24.         }
    1 ~) U7 ^$ S4 O$ F: D+ O; `
  25.         return nil
    $ }% z' t( J; X1 ]/ v( I
  26. }
复制代码

( R% J% B8 o) a$ G* L6 I' lOnStart() 启动node进程如下:/ G, i" o1 [; j# g
启动挖矿功能启动p2p网络同步启动http协议的apiserver服务打开浏览器访问bytond的交易页面: G3 \& G5 E) V9 a

% c4 Z; j% q: Q9 [- z% v" x2 P停止node5 u0 q1 x: h- n" B, @: u) O$ w1 n
bytomd在启动时执行了n.RunForever()函数,该函数是由tendermint框架启动了监听信号的功能:, Y4 B# `4 g% D4 X( P( u
  1. ** vendor/github.com/tendermint/tmlibs/common/os.go **
    % E8 |# O* c- A
  2. func TrapSignal(cb func()) {) g1 k7 l7 I* J/ J% x
  3.         c := make(chan os.Signal, 1)
    5 R) z5 X8 g2 C/ I7 y  z0 q9 H
  4.         signal.Notify(c, os.Interrupt, syscall.SIGTERM)
      t7 P! w# {& p$ L! H: `  I2 z
  5.         go func() {
    2 V- {/ ^# V2 z0 \4 b7 Y# f9 b
  6.                 for sig := range c {
    # D# s5 T  m- p5 z5 i  ~8 B
  7.                         fmt.Printf("captured %v, exiting...\n", sig)+ I; p( M+ s, v
  8.                         if cb != nil {& q5 J7 y3 e% M2 P6 K+ a( Y
  9.                                 cb()
    $ n. B1 g  t( A) \
  10.                         }8 S5 Q) n5 F0 i. J# Q/ X/ r
  11.                         os.Exit(1)+ ?" w) p4 G5 a9 f! X7 B# y
  12.                 }: D. p9 c% z1 z
  13.         }()
    3 c8 }( }7 F- y# r
  14.         select {}
    * \+ r! c4 h2 S# D7 w; v# O
  15. }
复制代码

2 B% E7 d3 M9 B5 p% S- m. a" STrapSignal函数监听了SIGTERM信号,bytomd才能成为不退出的守护进程。只有当触发了ctrl+c或kill bytomd_pid才能终止bytomd进程退出。退出时bytomd执行如下操作
0 x4 |6 W. v2 w4 J9 w2 N' v
  1. ** node/node.go **" P% E8 F/ B4 g, O9 K/ N
  2. func (n *Node) OnStop() {% d0 j# k) n  }, ?
  3.         n.BaseService.OnStop()
    ( m0 C! W% R: k8 U6 N  R
  4.         if n.miningEnable {
    + j) b) V0 i  x
  5.                 n.cpuMiner.Stop()! e: X& @  _" f% l! Z
  6.         }' Q' O2 G2 ~5 }
  7.         n.syncManager.Stop()9 H- f" O4 M7 i6 y6 r2 C, e
  8.         log.Info("Stopping Node")
    0 q  j4 s" V& U8 x! x. A
  9.         // TODO: gracefully disconnect from peers.! B, x! Q2 s, C. b  C; v
  10. }
复制代码

8 H* \9 s9 A- a4 v8 K. ~bytomd会将挖矿功能停止,p2p网络停止等操作。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

蔡健雅的高跟鞋 小学生
  • 粉丝

    0

  • 关注

    2

  • 主题

    4