Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

bytom源码分析-启动与停止

蔡健雅的高跟鞋
132 0 0
作者使用MacOS操作系统,其他平台也大同小异
" [5 ^* {2 e( kGolang Version: 1.8
3 S" ^& y3 l' I0 a& y$ N

6 g5 Z( _6 {. @& X- ^预备工作  N8 v5 C6 K8 q1 h0 `
编译安装0 }; [! J+ v3 Z' N
详细步骤见官方 bytom install
* a  a9 ?+ U% u9 ]# ^  R- [设置debug日志输出: B' _" ?0 d; B1 k) X
开启debug输出文件、函数、行号等详细信息( l0 {# C/ O9 a0 x; b; k& k( U2 s; O
  1. export BYTOM_DEBUG=debug
复制代码
" S! C2 Q6 l! Q5 I
初始化并启动bytomd
1 a, N+ d( N8 Q; c* H初始化
! t8 R% @7 d7 L
  1. ./bytomd init --chain_id testnet
复制代码

. |* A1 M8 \) \% R) @) Cbytomd目前支持两种网络,这里我们使用测试网
2 z4 N* W' ?4 O1 a$ V% i8 dmainnet:主网
1 g3 @6 l8 M7 ^& j, c7 o7 Ltestnet:测试网! P: i; M1 D) M* F' L" X
启动bytomd
+ b* \$ b  ?' D" o, ^' ]% n
  1. ./bytomd node --mining --prof_laddr=":8011"
    ( l8 b6 v. O# \7 J
  2. –prof_laddr=":8080"  // 开启pprof输出性能指标
复制代码

4 `/ T$ u* P' ^& V访问:http://127.0.0.1:8080/debug/pprof/
- M" x+ G$ M1 Vbytomd init初始化8 S5 ^% t$ }0 i7 g" b
入口函数& b1 ^1 r% k* s2 S
  1. ** cmd/bytomd/main.go **  v* o1 F2 G% C7 v
  2. func init() {
    / J& [( a' Z0 Q6 o& M
  3.         log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableColors: true})
    - K8 \7 |- q3 N. S- i5 `# r. ^
  4.         // If environment variable BYTOM_DEBUG is not empty,( @  a; y6 b0 K" o2 l
  5.         // then add the hook to logrus and set the log level to DEBUG
    * l8 H" I, p4 `! ?5 C
  6.         if os.Getenv("BYTOM_DEBUG") != "" {
    0 _5 {' ?2 ^4 U3 Y$ L
  7.                 log.AddHook(ContextHook{})5 D+ y- V0 a' t% E* z
  8.                 log.SetLevel(log.DebugLevel)
    ) D/ U- ?/ Q5 _; a3 u
  9.         }* i: n( s4 d5 h  }6 G' M4 g- N; W
  10. }
    1 J' w! c% _2 o8 u
  11. func main() {
      M# [* B- }$ }0 M' D  i
  12.         cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv(config.DefaultDataDir()))+ v  A9 l) A. @$ {0 @
  13.         cmd.Execute()
    2 E% n' A: o3 g' `( ?& G
  14. }
复制代码
" G+ P2 |8 m7 n4 c2 v& `8 U
init函数会在main执行之前做初始化操作,可以看到init中bytomd加载BYTOM_DEBUG变量来设置debug日志输出
. k0 Z& h  z( S! |# \* V! \, Dcommand cli传参初始化1 V* t+ E/ j6 R4 l( K* k9 D
bytomd的cli解析使用cobra库
) N$ h6 C: c, X* ~2 R3 b' E** cmd/bytomd/commands **3 `0 ~8 @% c! A$ T$ I4 I+ L
cmd/bytomd/commands/root.go9 Z  ?) @3 {; ?$ Z
初始化–root传参。bytomd存储配置、keystore、数据的root目录。在MacOS下,默认路径是~/Library/Bytom/cmd/bytomd/commands/init.go
# `* s$ ~( i1 k$ ~4 V初始化–chain_id传参。选择网络类型,在启动bytomd时我们选择了testnet也就是测试网络cmd/bytomd/commands/version.go+ |" ]6 M. o9 y! B1 D
初始化version传参
cmd/bytomd/commands/run_node.go$ K7 B1 U! z0 K5 H& z
初始化node节点运行时所需要的传参
: R) v9 ]1 B* X7 t9 \! s2 F$ r
6 `: X- D) M) }! J# |- ^; }
初始化默认配置
0 p  |& R2 ^. n* k3 m2 `用户传参只有一部分参数,那节点所需的其他参数需要从默认配置中加载。
; L, F. L  B8 C1 Q
  1. ** cmd/bytomd/commands/root.go **
    ' S2 s+ n7 M6 M0 i! Y1 M
  2. var (, ^: L; T- y: E# R
  3.         config = cfg.DefaultConfig()
    # _+ K1 s4 q6 X4 S" i  o
  4. )3 W. q. g6 `7 F9 E; Y
  5. 在root.go中有一个config全局变量加载了node所需的所有默认参数5 k9 F* u. y2 i2 S& ?% Q! n
  6. // Default configurable parameters.
    & u: l% g* g! ^
  7. func DefaultConfig() *Config {
    % V0 j6 w; c5 k7 v
  8.         return &Config{
    6 E+ Z! U  _1 S! W1 x
  9.                 BaseConfig: DefaultBaseConfig(),  // node基础相关配置
    % [: f+ o$ q3 \8 d) m
  10.                 P2P:        DefaultP2PConfig(),   // p2p网络相关配置
    ( s1 z  ?4 ^+ }' k: A. |- I
  11.                 Wallet:     DefaultWalletConfig(),   // 钱包相关配置3 Y  w4 R8 Y" K  h! p4 ~  N& N
  12.                 Auth:       DefaultRPCAuthConfig(),  // 验证相关配置2 j  {  F- ~$ x
  13.                 Web:        DefaultWebConfig(),   // web相关配置
    7 |; O; F( E! }, M( N& w+ ~
  14.         }/ B/ _, }" ^/ L1 w. q
  15. }
复制代码

' A4 Y+ L" G- k1 W! B; b; S/ _后面的文章会一一介绍每个配置的作用, C  i7 C, W. r. `2 ?) l9 c
bytomd 守护进程启动与退出7 q  Q6 d# o1 t, V# _
  1. ** cmd/bytomd/commands/run_node.go **
    # R$ v3 a, n$ o6 d
  2. func runNode(cmd *cobra.Command, args []string) error {# ^8 d" N8 v0 f% n  c, j
  3.         // Create & start node
    1 S  M) G( u  m: |7 n
  4.         n := node.NewNode(config)
    4 s1 l( ^$ ~0 l8 l) L& p& ]
  5.         if _, err := n.Start(); err != nil {
    $ R* n6 d! e! B& n% m
  6.                 return fmt.Errorf("Failed to start node: %v", err)
    & N& f$ o, t- w) K: ~  L
  7.         } else {
    ) w8 }6 J; M  Y  g$ t  s/ C1 A& r; [
  8.                 log.WithField("nodeInfo", n.SyncManager().Switch().NodeInfo()).Info("Started node")6 I- H6 R" y  j1 o6 e" o' j+ P1 ]
  9.         }( t  r; u" K1 v/ N  B$ A
  10.         // Trap signal, run forever.
    ( X1 T7 b# l9 S) \! N8 E! ^" ^
  11.         n.RunForever()$ \' F2 E& Y2 m
  12.         return nil
    4 Q, j7 ?  J; `2 F; Y% X
  13. }
复制代码
/ Q- g6 S' L- u' a/ j: A
runNode函数有三步操作:
0 Z/ ?1 I& ?8 \- x: m" mnode.NewNode:初始化node运行环境% J) d" z8 Z' \* t0 D8 I
n.Start:启动node1 [' |5 l+ N7 ?' ?  X
n.RunForever:监听退出信号,收到ctrl+c操作则退出node。在linux中守进程一般监听SIGTERM信号(ctrl+c)作为退出守护进程的信号
: w5 p  _" h# a初始化node运行环境
0 L6 A2 d+ P# x0 I在bytomd中有五个db数据库存储在–root参数下的data目录
# {/ p  F& Q2 I3 F  E  Aaccesstoken.db    // 存储token相关信息(钱包访问控制权限)trusthistory.db   // 存储p2p网络同步相关信息txdb.db           // 存储交易相关信息txfeeds.db        //wallet.db         // 存储钱包相关信息6 Q' s& p5 u& Q, x' l" x* g6 B8 i

  1. 6 w+ ]* q: F  u
  2. ** node/node.go **+ w2 A$ P' h* k
  3. func NewNode(config *cfg.Config) *Node {6 Q' D5 \* Z" h; v2 g9 H1 Y
  4.         ctx := context.Background()6 U5 |# O) G: [' f8 a$ ]
  5.         initActiveNetParams(config)' @) P3 _, G; u* Q2 K5 l
  6.         // Get store 初始化txdb数据库+ @( w% m9 x9 d+ N
  7.         txDB := dbm.NewDB("txdb", config.DBBackend, config.DBDir())! Z& y+ X& R. n. F/ q* }! ?
  8.         store := leveldb.NewStore(txDB)
    $ i. e8 R$ a2 p! U& X
  9.   // 初始化accesstoken数据库
    , |" E9 K0 I" B! d# c' \* T! _/ z
  10.         tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir())
    " i: g! l# o& Q8 S& }1 f
  11.         accessTokens := accesstoken.NewStore(tokenDB)
    5 a% S/ O. N, B
  12.   // 初始化event事件调度器,也叫任务调度器。一个任务可以被多次调用- r5 ?. Y7 e0 _1 o5 ^* ^/ \* n
  13.         // Make event switch
    " I( [7 O$ }# L( I! E6 r
  14.         eventSwitch := types.NewEventSwitch()
    4 `; F' M. u) {
  15.         _, err := eventSwitch.Start()
    9 ?8 o& b: E! Q; K% r0 h
  16.         if err != nil {- Z6 Z# G; K& ]$ n. t" S9 \
  17.                 cmn.Exit(cmn.Fmt("Failed to start switch: %v", err))7 r4 U$ T+ h: ^6 \6 ^6 x) ~+ N/ L, p
  18.         }
    . g, C  I  p5 I1 E2 K, ?7 k
  19.   // 初始化交易池
    1 M: J3 R4 I. m% M
  20.         txPool := protocol.NewTxPool()
    3 m& b0 N& c; M; J6 Z' @. W
  21.         chain, err := protocol.NewChain(store, txPool)
    9 z( L1 x+ I+ d9 B; F6 E# V4 l3 h
  22.         if err != nil {
    - ?' @/ f! B) ?3 \; u- e
  23.                 cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))
    / o- v: c) ^" P6 R
  24.         }
      ~6 b$ E' T" x: f5 V/ V4 {9 R
  25.         var accounts *account.Manager = nil1 z- ~2 J6 f8 G/ W
  26.         var assets *asset.Registry = nil( `7 X1 V4 f( z7 v8 v9 C
  27.         var wallet *w.Wallet = nil) z8 s1 Q3 Q8 I' v3 [+ y3 n. f1 E
  28.         var txFeed *txfeed.Tracker = nil- I4 o6 U. A  h8 Q5 u3 _
  29.   // 初始化txfeeds数据库  M/ p- }2 e  w0 q! r  A, j: e
  30.         txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())" L; R' S( v2 s0 q5 Z
  31.         txFeed = txfeed.NewTracker(txFeedDB, chain)
    6 j. o& n- U& ]1 e6 o
  32.         if err = txFeed.Prepare(ctx); err != nil {1 r' Q0 i: R* G: O4 l
  33.                 log.WithField("error", err).Error("start txfeed")' D* m/ ]! P/ ^' p4 S# {
  34.                 return nil
    / g) g. n9 g" S+ k# r8 T
  35.         }9 D0 k: \0 b9 I: p# l9 `
  36.   // 初始化keystore9 I, K3 ^$ @: {, b+ ^
  37.         hsm, err := pseudohsm.New(config.KeysDir())  L# [; i8 I' p: U. P, |4 H$ \! |
  38.         if err != nil {0 \  g# O% W) H- {
  39.                 cmn.Exit(cmn.Fmt("initialize HSM failed: %v", err))
    # u# G( [  P) M+ f6 I% L7 P' U
  40.         }3 b" |6 R7 x- x
  41.   // 初始化钱包,默认wallet是开启状态0 b+ F% V+ X3 I5 K( d6 v
  42.         if !config.Wallet.Disable {
    1 q  T  h8 n6 S
  43.                 walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())* Z! W7 l& ?# }% Y
  44.                 accounts = account.NewManager(walletDB, chain)  R6 Q3 C3 ?7 F1 I
  45.                 assets = asset.NewRegistry(walletDB, chain)
    5 k" N' [7 T, `% i. W
  46.                 wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain)9 |$ z& i8 b3 o
  47.                 if err != nil {
    $ J7 Z" q/ b: L7 B* h- \1 A! z3 N3 d
  48.                         log.WithField("error", err).Error("init NewWallet")- T* @: r1 e% q* A4 J
  49.                 }
    # l8 _1 H+ P7 ^
  50.                 // Clean up expired UTXO reservations periodically.
    ) R) z7 H: A" Y4 B+ \+ ~
  51.                 go accounts.ExpireReservations(ctx, expireReservationsPeriod)
    ; Y; Y4 g. D) L' f7 r7 _0 _5 j5 G6 n# Z
  52.         }9 I4 a$ T: z6 ]( q
  53.         newBlockCh := make(chan *bc.Hash, maxNewBlockChSize)' v3 K6 ~2 v7 c1 M1 E7 q3 Z
  54.   // 初始化网络节点同步管理
    2 R! g7 V1 A; n* P/ }. ^; K
  55.         syncManager, _ := netsync.NewSyncManager(config, chain, txPool, newBlockCh)
    - v5 G# {$ n/ r; u
  56.   // 初始化pprof,pprof用于输出性能指标,需要制定--prof_laddr参数来开启,在文章开头我们已经开启该功能( A2 f% J5 I; }2 L
  57.         // run the profile server; j) S/ u3 D+ N6 T( @3 c# o$ g7 ?
  58.         profileHost := config.ProfListenAddress& Y" w, M, G5 Y8 o9 Y
  59.         if profileHost != "" {
      G+ e+ ?) Y8 I' ?3 J
  60.                 // Profiling bytomd programs.see (<a href="https://blog.golang.org/profiling-go-programs" target="_blank">https://blog.golang.org/profiling-go-programs</a>)
    ! c! l: u9 J( }$ u$ k$ y, X8 S5 ]
  61.                 // go tool pprof http://profileHose/debug/pprof/heap
    ! w* f1 s: D5 R5 r8 u
  62.                 go func() {# X% \* P$ V; G" m" b6 }
  63.                         http.ListenAndServe(profileHost, nil)
    $ V  Y0 F3 ^9 S" c8 E- N  ]
  64.                 }()
    + c' E% M( d2 v) f- ^9 y( j6 p) K
  65.         }
    0 f3 I0 @/ g. S5 D3 B+ W4 a
  66.   // 初始化节点,填充节点所需的所有参数环境
    * j8 c" M/ ^  \" ~
  67.         node := &Node{: h& E' d5 R. _  C
  68.                 config:       config,
    ( {) E0 u( V4 Z* a( V8 q/ `
  69.                 syncManager:  syncManager,; ?3 g5 `# y9 {% d- I
  70.                 evsw:         eventSwitch,& {) u3 h+ k0 r' E; ^# \
  71.                 accessTokens: accessTokens,
    2 r# H& b% G1 x7 Q. @0 X0 l+ e3 C( G
  72.                 wallet:       wallet,
    + d, q. a! b3 U4 U% B9 y" k
  73.                 chain:        chain,
    ) d/ S, H9 @- V- E& y
  74.                 txfeed:       txFeed,: u- H! l) }; a/ R8 v
  75.                 miningEnable: config.Mining,
    . f5 n+ M7 f$ k6 e( y
  76.         }
    + V4 C: n8 }# _# g- ^
  77.   // 初始化挖矿
    : X2 G2 V5 l* r' h
  78.         node.cpuMiner = cpuminer.NewCPUMiner(chain, accounts, txPool, newBlockCh)3 h0 {# \/ K" S
  79.         node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, newBlockCh)  T  F/ y8 }  S% B/ K
  80.         node.BaseService = *cmn.NewBaseService(nil, "Node", node)$ ~8 F" P5 N* F" Z
  81.         return node9 \0 N7 l0 X: T! H4 w7 V* N
  82. }
复制代码
! W" r2 c+ i9 H5 Z# ?% u8 ]7 l
目前bytomd只支持cpu挖矿,所以在代码中只有cpuminer的初始化信息
0 q7 A0 c" @# Z6 j启动node
, }# n/ ?) E; e* q" j2 [/ m
  1. ** node/node.go **
    3 k) ?0 x) h1 q! v7 b- j/ M
  2. // Lanch web broser or not& x/ j% Y% j! m$ N; b
  3. func lanchWebBroser() {& _( O1 A! C6 h' b# G
  4.         log.Info("Launching System Browser with :", webAddress)4 b' j- {+ e/ d6 W* d
  5.         if err := browser.Open(webAddress); err != nil {) R6 E1 j. [0 K' }8 O$ L
  6.                 log.Error(err.Error())
    + R( g6 \+ r) R5 [$ f
  7.                 return6 U2 ^8 d; \% A& u( {3 t; [
  8.         }* \' U7 O3 ]9 q6 ?4 Z9 j
  9. }9 ?" ~  F" J1 @* t. f! D' L7 w% P
  10. func (n *Node) initAndstartApiServer() {
    2 F3 S5 r' g' u6 d% G$ D3 r
  11.         n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)& s5 c; i( C( K' T7 \4 ?7 T
  12.         listenAddr := env.String("LISTEN", n.config.ApiAddress): G  n$ k# O6 ?0 p7 T
  13.         env.Parse()+ y- t0 M8 ?; w; V/ g! q7 n$ g
  14.         n.api.StartServer(*listenAddr)2 r9 {$ S( x5 g; N4 j& y2 F
  15. }
    ) K* @7 U# x% \7 o/ ]1 k  z2 K
  16. func (n *Node) OnStart() error {3 q, b4 T9 t3 W" Z3 k. Z$ P
  17.         if n.miningEnable {/ K$ K1 g8 l5 @& |0 J
  18.                 n.cpuMiner.Start()
    5 L. T8 Y: X5 B8 q# _( c, [
  19.         }$ ^8 Z8 ?9 Y2 E+ f* I
  20.         n.syncManager.Start()0 B- A, T% L4 r
  21.         n.initAndstartApiServer()! G+ _' [1 C4 e
  22.         if !n.config.Web.Closed {- T7 e, g4 I3 ?: [
  23.                 lanchWebBroser()
    - R& M% ?# K, j1 Z! W' G! g
  24.         }
    8 y* J$ N0 q/ a$ b
  25.         return nil; L* Z  l) l$ X# b( }& G. I* b7 M9 f
  26. }
复制代码

0 Z5 Q) @2 e8 t1 N0 v  y% gOnStart() 启动node进程如下:9 p& ^1 J/ P4 ?
启动挖矿功能启动p2p网络同步启动http协议的apiserver服务打开浏览器访问bytond的交易页面
# M( \  R; c4 I9 n
$ _6 y) |( `( t* f1 p
停止node* r. _7 H1 a, g
bytomd在启动时执行了n.RunForever()函数,该函数是由tendermint框架启动了监听信号的功能:
" T/ P* j, y3 ]
  1. ** vendor/github.com/tendermint/tmlibs/common/os.go **
    & v. I  a0 z' R' ~. |& z4 u% q
  2. func TrapSignal(cb func()) {5 I$ d: K# Y% L
  3.         c := make(chan os.Signal, 1)+ o0 W+ t# R% {0 ^# u
  4.         signal.Notify(c, os.Interrupt, syscall.SIGTERM), {+ Y( C- Z# K
  5.         go func() {
    * J5 w+ e9 m; p% M, H) k+ H0 D
  6.                 for sig := range c {
    2 e" l" t% j- Y9 B) m; D8 T
  7.                         fmt.Printf("captured %v, exiting...\n", sig)8 ^6 E3 G; u' \6 ~% F
  8.                         if cb != nil {* u4 |  o9 s( I  h) g* n) X
  9.                                 cb()5 B7 r3 R- C% n
  10.                         }* W5 b% c' S& A3 p0 Z5 r
  11.                         os.Exit(1)
    8 b# u* |1 f$ \) w8 D! l& v
  12.                 }
    # X8 _/ h8 o$ |+ E# K8 z0 {* `' ]
  13.         }()
    6 E7 V9 f; g5 f6 `9 N
  14.         select {}' U: o9 z4 V) @6 C# w$ P7 H0 i
  15. }
复制代码
3 _3 r0 ~3 ^9 |$ a
TrapSignal函数监听了SIGTERM信号,bytomd才能成为不退出的守护进程。只有当触发了ctrl+c或kill bytomd_pid才能终止bytomd进程退出。退出时bytomd执行如下操作, P4 B$ V# X6 ~3 Z6 H
  1. ** node/node.go **
    1 o* }, j3 D4 G, e( r8 V
  2. func (n *Node) OnStop() {. H! [% \, _( p/ _9 ?) ]* q
  3.         n.BaseService.OnStop()4 o  z4 C, f$ ^' v! D
  4.         if n.miningEnable {% B& S0 q' y( D4 L% ~
  5.                 n.cpuMiner.Stop()# r' _+ K5 J3 j3 I9 q0 M  {
  6.         }* F6 f9 T% w9 r+ z% D. t( ~
  7.         n.syncManager.Stop()
    / J" j( n6 K$ g0 h5 I1 D. J5 B3 D
  8.         log.Info("Stopping Node")
    ' _8 F2 V. ?; a$ B% ~. i
  9.         // TODO: gracefully disconnect from peers.
    ! l; {9 f" C" |, u2 ~+ ?- R
  10. }
复制代码
& i. F" r, X7 @' A4 g2 g
bytomd会将挖矿功能停止,p2p网络停止等操作。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    2

  • 主题

    4