Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

bytom源码分析-启动与停止

蔡健雅的高跟鞋
133 0 0
作者使用MacOS操作系统,其他平台也大同小异7 M2 c+ i4 }& {2 [
Golang Version: 1.8
2 o7 ~; n- K- z: ^9 ^$ [! n. P
# V* v! m* J) y# d+ k7 ?
预备工作) [7 O- E! s3 ^2 Y) I
编译安装! F1 v+ G* A, p  `
详细步骤见官方 bytom install  F' D$ x! H- ]% w9 ~
设置debug日志输出
+ s  t, g! @" p3 _& h- L8 H, F开启debug输出文件、函数、行号等详细信息3 p4 V" _5 J3 x. _) `3 h
  1. export BYTOM_DEBUG=debug
复制代码
- C9 Y- g4 t! N5 y2 j( s6 V
初始化并启动bytomd+ `  ^& m: q6 A, _, e" {
初始化
) o% e; f( w/ l  U- ~" |
  1. ./bytomd init --chain_id testnet
复制代码
6 a/ s- P; ~! V8 x; b
bytomd目前支持两种网络,这里我们使用测试网/ j& Q' y5 W* L' A' r  \
mainnet:主网& |" {! }& Q; J4 s$ ^% o
testnet:测试网
7 y4 J/ f$ t: U* v' r6 L' A启动bytomd; b/ G! R) O; q! R" o, C/ J3 s
  1. ./bytomd node --mining --prof_laddr=":8011"
    : n' V! Y! [0 m) `$ _
  2. –prof_laddr=":8080"  // 开启pprof输出性能指标
复制代码
; Q/ v/ Y4 w2 G- y7 [
访问:http://127.0.0.1:8080/debug/pprof/
6 }" `4 M, w' v) x2 W( ~& e. w" B: \bytomd init初始化
( e. X2 W5 N+ C& ^9 I2 _' ?9 [! z入口函数( ]! I% |& A3 x* y$ d% b
  1. ** cmd/bytomd/main.go **
    5 M3 O: K" G9 A" B" T
  2. func init() {) X- h) V8 R# [& p' ~& P+ d
  3.         log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableColors: true})1 E1 Q9 F0 h" h/ u+ G
  4.         // If environment variable BYTOM_DEBUG is not empty,
    $ i* {* }9 Y1 a- D4 H1 ]4 |  V) N% ^
  5.         // then add the hook to logrus and set the log level to DEBUG
    7 x; {  W$ g* g4 B6 ?; G" _
  6.         if os.Getenv("BYTOM_DEBUG") != "" {8 B! ^  X5 o3 f1 k8 O- `4 A* K$ m
  7.                 log.AddHook(ContextHook{})
    9 G$ p" C( c: _, K
  8.                 log.SetLevel(log.DebugLevel). y7 K( H# p3 `6 y+ j
  9.         }
    * j" W& S4 f$ s# r6 N
  10. }
    + E1 W. d6 G1 q
  11. func main() {
    3 L8 m; P( e' T' ^
  12.         cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv(config.DefaultDataDir()))
    . {+ R% x) }6 U. X$ _5 [
  13.         cmd.Execute()
    7 N& ]* j% @% z6 F
  14. }
复制代码
# w' {3 d% @8 ]& {7 j+ @' S
init函数会在main执行之前做初始化操作,可以看到init中bytomd加载BYTOM_DEBUG变量来设置debug日志输出
0 q: U" k( J% r% Ccommand cli传参初始化/ u" X5 k  N) Q  G# Q* [
bytomd的cli解析使用cobra库, r) K5 X' _1 s+ I' E3 ]( V- P
** cmd/bytomd/commands **0 G. C' v% u" Y: R: Y1 q
cmd/bytomd/commands/root.go
0 S  I. ?0 X! E9 b: E0 ]; [% J初始化–root传参。bytomd存储配置、keystore、数据的root目录。在MacOS下,默认路径是~/Library/Bytom/cmd/bytomd/commands/init.go* I% D! _. b5 F* g1 P+ L
初始化–chain_id传参。选择网络类型,在启动bytomd时我们选择了testnet也就是测试网络cmd/bytomd/commands/version.go% o: Z: O; `# n! [8 N8 I6 ^
初始化version传参
cmd/bytomd/commands/run_node.go7 b" J! S8 H4 J, o9 N, A
初始化node节点运行时所需要的传参
! y- M+ s3 z; _" C3 u; O
* J) I  n9 }; f: |( ~. A) F
初始化默认配置$ m% P! W0 Y. N/ G* f3 r% T  B
用户传参只有一部分参数,那节点所需的其他参数需要从默认配置中加载。& g) i. p0 [; Q/ M1 B0 }) n: w
  1. ** cmd/bytomd/commands/root.go **2 Y; e  C, w  d
  2. var (( A: d) m4 E: k4 X
  3.         config = cfg.DefaultConfig()" c" C0 E0 }8 q; Q! z4 j# G* ~
  4. )
    2 y1 E' s: p, p, ^. {1 T
  5. 在root.go中有一个config全局变量加载了node所需的所有默认参数) S- \$ T: I# ~! J+ z1 t% d
  6. // Default configurable parameters.
    5 f% Z- s' G. g; w3 h
  7. func DefaultConfig() *Config {8 u/ _7 Z$ ]! x3 {
  8.         return &Config{) w, r8 {2 m" U( y3 _! V. @7 a
  9.                 BaseConfig: DefaultBaseConfig(),  // node基础相关配置) }' |2 V- l  m6 ~3 B
  10.                 P2P:        DefaultP2PConfig(),   // p2p网络相关配置2 N1 H+ U. n& G9 A! r
  11.                 Wallet:     DefaultWalletConfig(),   // 钱包相关配置1 p7 K" p! E0 c; I0 v
  12.                 Auth:       DefaultRPCAuthConfig(),  // 验证相关配置
    ) O% i4 _' M6 ~& [
  13.                 Web:        DefaultWebConfig(),   // web相关配置  h2 Z4 f/ A2 C  K6 X3 q: f
  14.         }/ }( D+ G+ B2 i8 W' t
  15. }
复制代码

8 ?4 ]5 b3 d  |: X( J& I后面的文章会一一介绍每个配置的作用  D9 Z& z7 g# E2 N% n4 d
bytomd 守护进程启动与退出# H: g* ~, h! F# F4 c
  1. ** cmd/bytomd/commands/run_node.go **, Y, p1 q+ {5 I8 H2 C  t0 q. ?; b  l
  2. func runNode(cmd *cobra.Command, args []string) error {
    3 {8 B9 S2 |5 C( H: t' z
  3.         // Create & start node
    4 i  d- G& p$ r7 }/ v
  4.         n := node.NewNode(config)
    # O5 l: t5 m0 \$ `
  5.         if _, err := n.Start(); err != nil {1 ~" A8 M. t3 S! `$ H' g. n* z' }, Z
  6.                 return fmt.Errorf("Failed to start node: %v", err)
    + z. `. |& F' i+ L: Z/ S" F
  7.         } else {" \$ z3 @9 q1 A9 Y
  8.                 log.WithField("nodeInfo", n.SyncManager().Switch().NodeInfo()).Info("Started node")2 M4 x/ A6 a! ?. l3 c9 y
  9.         }
    & ~; U! b9 X7 G( A4 c# R+ l
  10.         // Trap signal, run forever.. S4 I! `4 ^# U( s
  11.         n.RunForever()' m3 Q0 [* O( t# ~
  12.         return nil4 s* R- ]& s) x" s! A2 Z
  13. }
复制代码

9 V9 Z1 p+ {; NrunNode函数有三步操作:
6 Z$ I: i( K7 p3 n- O  D/ K' L, znode.NewNode:初始化node运行环境
6 v5 `, }9 N2 {& z  Vn.Start:启动node
' q, c9 w: v" u+ B0 g/ F' ?, t1 Tn.RunForever:监听退出信号,收到ctrl+c操作则退出node。在linux中守进程一般监听SIGTERM信号(ctrl+c)作为退出守护进程的信号
1 L2 D+ y6 b$ V1 c+ o" Q2 T初始化node运行环境# C7 R' B1 D! ]# D) W6 Z
在bytomd中有五个db数据库存储在–root参数下的data目录
( r# B: p, @* D$ g* M& P, H8 J4 g- S/ jaccesstoken.db    // 存储token相关信息(钱包访问控制权限)trusthistory.db   // 存储p2p网络同步相关信息txdb.db           // 存储交易相关信息txfeeds.db        //wallet.db         // 存储钱包相关信息
* C1 `( [7 {: U* v7 v% J1 Z3 H

  1. $ G. _" N8 f! D
  2. ** node/node.go **
    + h2 @, P/ w) j) F3 M. @$ v# \
  3. func NewNode(config *cfg.Config) *Node {2 E1 X! s1 R: H
  4.         ctx := context.Background()
    - X, a: F, @2 J/ B" _! |6 c
  5.         initActiveNetParams(config)5 p% X2 A! k2 z3 z: I  t) b! A9 v
  6.         // Get store 初始化txdb数据库, S! P" B1 s" Q+ w6 J$ n9 T# Y) f
  7.         txDB := dbm.NewDB("txdb", config.DBBackend, config.DBDir())
    - Z/ j9 G' x6 y/ v2 I9 [8 w8 t3 K
  8.         store := leveldb.NewStore(txDB)/ j1 w' Y% [* B7 y& V
  9.   // 初始化accesstoken数据库1 E5 \/ V/ C9 C( o
  10.         tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir()). L0 A- V5 M$ ]/ [
  11.         accessTokens := accesstoken.NewStore(tokenDB)
    . S  s  ^5 j# a- O$ G) K
  12.   // 初始化event事件调度器,也叫任务调度器。一个任务可以被多次调用
    / h( \6 N( s( D' o
  13.         // Make event switch* p8 I4 i6 K4 x: S
  14.         eventSwitch := types.NewEventSwitch()# `3 H# \8 V2 x. d, o
  15.         _, err := eventSwitch.Start()7 O1 N/ ]8 W9 W9 W# q) t
  16.         if err != nil {
    & g  F+ l1 T7 T# x
  17.                 cmn.Exit(cmn.Fmt("Failed to start switch: %v", err))
    ; d5 G7 d1 x$ n% @2 Y# \
  18.         }
    4 w( U8 u' A$ m" h) f) z& F+ K
  19.   // 初始化交易池
      u4 f( i: ^, k# E, w  o
  20.         txPool := protocol.NewTxPool()& V" i" i9 `5 r
  21.         chain, err := protocol.NewChain(store, txPool)
    . h& E0 O7 _$ q0 h- S
  22.         if err != nil {) c/ N1 H1 a) F# d
  23.                 cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))/ R; w* d) S/ x- `: E
  24.         }' Z9 k9 z# x0 h* d( ]1 R
  25.         var accounts *account.Manager = nil
    . F6 c$ y- O0 L- M+ I  v* y  v6 M
  26.         var assets *asset.Registry = nil( [% d8 i* `& s6 l
  27.         var wallet *w.Wallet = nil
    / |  q0 ~2 M) C. H
  28.         var txFeed *txfeed.Tracker = nil
    & K8 p; R- v6 P+ m; r$ _6 j4 Q
  29.   // 初始化txfeeds数据库1 g1 F! Z! R8 L
  30.         txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())' y. _  U3 p$ l* e5 ~- {
  31.         txFeed = txfeed.NewTracker(txFeedDB, chain)
    / p( M( X5 y8 T9 f
  32.         if err = txFeed.Prepare(ctx); err != nil {  E, \- @3 O% L+ E! L- E
  33.                 log.WithField("error", err).Error("start txfeed")
    7 F* b% U6 ^2 M$ U% F# V) x+ X
  34.                 return nil
    4 z* s" I5 m+ ?. d! k
  35.         }  d( o4 ]% b. T! L4 H" X6 W$ a, X
  36.   // 初始化keystore8 G2 u- L) [8 r: ?) H0 ?' U
  37.         hsm, err := pseudohsm.New(config.KeysDir())
    , ~" C% P; B5 Z' Q
  38.         if err != nil {
    ! q' Z3 f- x' u' m/ j8 b6 N, a
  39.                 cmn.Exit(cmn.Fmt("initialize HSM failed: %v", err))
    4 W, N* Y  {2 t4 C
  40.         }
    . e' z8 [7 G$ s* [4 s9 n' b
  41.   // 初始化钱包,默认wallet是开启状态
    + w: g8 w) s5 y( }' D: v
  42.         if !config.Wallet.Disable {- R+ ]! v4 C8 y5 Q5 a
  43.                 walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir()). l2 y9 s5 I8 \) _
  44.                 accounts = account.NewManager(walletDB, chain); ]  \: Z6 i  b5 A( t& J: z4 w( V
  45.                 assets = asset.NewRegistry(walletDB, chain)
    1 s3 ]+ p) H2 h% \1 P1 J
  46.                 wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain)& {! w' `+ U5 @3 E" ]
  47.                 if err != nil {
    ! F3 `7 r0 m% \/ W
  48.                         log.WithField("error", err).Error("init NewWallet")4 e1 ?% c: ?! W4 M) W6 J! E) i
  49.                 }
    0 Y, |1 E6 i- D7 F
  50.                 // Clean up expired UTXO reservations periodically.8 e3 z8 H7 g4 A2 E9 ^  ]9 ]9 y0 f9 w
  51.                 go accounts.ExpireReservations(ctx, expireReservationsPeriod)' x& a1 {& \" ^% Q0 w/ v
  52.         }
    ( v/ C9 b6 d1 I5 i8 C( g3 `5 h
  53.         newBlockCh := make(chan *bc.Hash, maxNewBlockChSize)' b$ K7 H/ p. b# J
  54.   // 初始化网络节点同步管理
    & i: G# {0 c" t; W8 [! a
  55.         syncManager, _ := netsync.NewSyncManager(config, chain, txPool, newBlockCh)% i, s6 w( s! d+ l+ O* G
  56.   // 初始化pprof,pprof用于输出性能指标,需要制定--prof_laddr参数来开启,在文章开头我们已经开启该功能
    " d5 H" E: B) N2 k
  57.         // run the profile server" ]/ _+ m, k7 |4 }$ ]& ]) m, i5 }4 d& {
  58.         profileHost := config.ProfListenAddress
    - [5 c3 a5 _. k; ]) }8 |
  59.         if profileHost != "" {
    6 l# q" {9 G/ n2 G- B
  60.                 // Profiling bytomd programs.see (<a href="https://blog.golang.org/profiling-go-programs" target="_blank">https://blog.golang.org/profiling-go-programs</a>)2 ^& a/ w& d( Y8 G9 [3 g
  61.                 // go tool pprof http://profileHose/debug/pprof/heap
    . T+ e( R4 x( Y7 W5 v! I  c7 @
  62.                 go func() {
    5 z" f6 Z1 w5 {3 {* [% ~' M* o+ K
  63.                         http.ListenAndServe(profileHost, nil)& p" K! x) s0 A$ Q
  64.                 }()
    ( U1 J# D! f9 q' ^0 w( |
  65.         }
    5 z0 Y. W2 W/ p/ }4 z! D
  66.   // 初始化节点,填充节点所需的所有参数环境
    % R- e! ~0 r$ _+ k
  67.         node := &Node{4 H) f/ n7 h) C" x, N
  68.                 config:       config,
    + w) B+ q* p1 U+ p+ s
  69.                 syncManager:  syncManager,$ Q8 A( @5 n! v4 y
  70.                 evsw:         eventSwitch,
    5 r7 n$ m4 k. A% }+ J7 a$ |; z* }3 L
  71.                 accessTokens: accessTokens,3 u, w0 w- g( `
  72.                 wallet:       wallet,9 K$ p% {  R( U4 |; P% `+ N1 `: ^) M8 b0 I
  73.                 chain:        chain,
    ( Z, H! E* h1 b7 T+ N1 Q0 z# ]
  74.                 txfeed:       txFeed,& v, |& u; U; c& ~9 X  h( E4 w- O
  75.                 miningEnable: config.Mining,
    ( n9 }/ o& B; l6 G  o4 n
  76.         }
    + |3 Z8 @0 [- [* ]1 b, I- @' {/ w
  77.   // 初始化挖矿
    ; F! S5 g" N6 X2 X
  78.         node.cpuMiner = cpuminer.NewCPUMiner(chain, accounts, txPool, newBlockCh)
    & d/ ]) p5 E' ^/ E
  79.         node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, newBlockCh)
    $ R& @- p! V. A1 z3 N+ o; ~4 M; q
  80.         node.BaseService = *cmn.NewBaseService(nil, "Node", node)
    + H- [& w4 R/ p
  81.         return node2 m! h( l1 x$ G3 j2 i
  82. }
复制代码

3 b/ Q) q1 e  w: w目前bytomd只支持cpu挖矿,所以在代码中只有cpuminer的初始化信息- N! }9 |+ c* ?, D2 E
启动node" E) s9 k( r, V9 ^6 a# l5 N
  1. ** node/node.go **/ s# F( |! T  h4 J  M4 ?
  2. // Lanch web broser or not
    + s4 P  ]  h* D3 R
  3. func lanchWebBroser() {
    : j" {* E( c  I5 V. ]) X
  4.         log.Info("Launching System Browser with :", webAddress)+ Z2 {) k" A" m5 g9 w3 @
  5.         if err := browser.Open(webAddress); err != nil {+ h; e8 R2 j; A" b* I' G5 s9 E
  6.                 log.Error(err.Error())
    8 ^6 Z! t  G9 b5 y
  7.                 return
    / H7 G, h& T' ~# }$ I6 t
  8.         }/ q. @: e/ G) w% }% N- W3 F
  9. }' o8 b& @& ?/ I) ]1 j
  10. func (n *Node) initAndstartApiServer() {# D" W) j* `6 F1 D# Z% ]" y4 V
  11.         n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)9 c  D% L) \8 s; Y( d6 Q( z
  12.         listenAddr := env.String("LISTEN", n.config.ApiAddress)
    ' g" j$ }: g8 y) ~5 T( S+ e  ^; o6 d) C
  13.         env.Parse()
    . y% G0 R3 F6 d
  14.         n.api.StartServer(*listenAddr)5 }& O6 i, \+ u; F2 Z6 j
  15. }( ~6 v2 C3 H- l
  16. func (n *Node) OnStart() error {' x; h$ m9 O7 V2 o# w
  17.         if n.miningEnable {
    ; }; ~! M4 j5 G* m5 q+ x9 A
  18.                 n.cpuMiner.Start()
    : }# Y0 a4 U; h0 {; f. Z
  19.         }9 `  V% q2 C% O5 n2 i% j
  20.         n.syncManager.Start()
    6 ]4 ]& z  d+ J9 e- H$ s5 \
  21.         n.initAndstartApiServer()/ ?/ \+ Y; h- g
  22.         if !n.config.Web.Closed {
    . k& P! F# j9 O- U0 d2 Z* Y
  23.                 lanchWebBroser()& F- ^" g8 R5 [- Z
  24.         }
    & r# }- p. r/ i! u: I
  25.         return nil3 m$ i: U0 ?0 l1 q
  26. }
复制代码
7 m+ O+ b6 D; j- @$ U
OnStart() 启动node进程如下:
# G2 C8 U7 ~" m% H2 t! ~启动挖矿功能启动p2p网络同步启动http协议的apiserver服务打开浏览器访问bytond的交易页面( J; g9 ]! Y4 a8 G, b4 J
5 i7 i. k, N( Y3 g4 S0 u
停止node
! b, ^* a" g  Y9 p) D( hbytomd在启动时执行了n.RunForever()函数,该函数是由tendermint框架启动了监听信号的功能:& O. B/ ^4 M! P4 W3 ^
  1. ** vendor/github.com/tendermint/tmlibs/common/os.go **& n8 x2 X  z6 u
  2. func TrapSignal(cb func()) {
    6 x) L3 Y2 k4 ^; X& r5 d- P/ N
  3.         c := make(chan os.Signal, 1)3 p  [0 y% h+ ^7 w' ]
  4.         signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    8 @8 c' L1 D6 F! a5 D9 H
  5.         go func() {5 U# Z9 K3 X# @# U. F
  6.                 for sig := range c {
    8 `8 v# k# \9 }$ c
  7.                         fmt.Printf("captured %v, exiting...\n", sig)7 U3 G: Q. x5 T; D$ ^% @) I
  8.                         if cb != nil {
    ) P3 K# y2 V: Y
  9.                                 cb()
    ) w- k" q, v4 z+ B
  10.                         }. s* A! N6 Z& R
  11.                         os.Exit(1)5 ~) A4 O5 {- o
  12.                 }5 a1 o6 q2 I- O
  13.         }()4 @+ R( S) L5 b
  14.         select {}
    % B. w6 c$ x& r6 g! \
  15. }
复制代码

5 b: k. ?$ E0 K" BTrapSignal函数监听了SIGTERM信号,bytomd才能成为不退出的守护进程。只有当触发了ctrl+c或kill bytomd_pid才能终止bytomd进程退出。退出时bytomd执行如下操作
' p, s' o! s/ H5 T
  1. ** node/node.go **
    * v) s- u* ?6 ^- d9 c  Q
  2. func (n *Node) OnStop() {
    6 g1 R' u7 N# Y3 v
  3.         n.BaseService.OnStop()% e  q* d6 c4 K6 p& k, m
  4.         if n.miningEnable {1 o6 A# R9 f% A# e
  5.                 n.cpuMiner.Stop()' A' A/ |9 ?7 F. R7 m
  6.         }) J6 H. w) u& E9 B8 n# i
  7.         n.syncManager.Stop()% d0 s" ~) Z4 U* I, A7 g
  8.         log.Info("Stopping Node")
    7 B& G. q; @& m7 V$ i8 G
  9.         // TODO: gracefully disconnect from peers.
    ( ~( }) @% C, l* h
  10. }
复制代码
+ t0 S, z6 g+ W  U
bytomd会将挖矿功能停止,p2p网络停止等操作。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    2

  • 主题

    4