Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

bytom源码分析-启动与停止

蔡健雅的高跟鞋
189 0 0
作者使用MacOS操作系统,其他平台也大同小异; M) O% V9 |9 K* z$ i2 O$ O7 N8 n
Golang Version: 1.8
& S1 e* o; L9 o$ ~

4 U; V, c# O8 u/ E" N$ e- a预备工作
" @& Z  l$ Q2 _. y0 G1 M3 x编译安装
& K+ z$ w+ B: i3 g  S3 f详细步骤见官方 bytom install
2 V7 g' N1 P- c- s$ q设置debug日志输出6 |; [3 d7 l$ X- D/ z
开启debug输出文件、函数、行号等详细信息: x* U& B( o9 ^9 a3 x  O
  1. export BYTOM_DEBUG=debug
复制代码

& Q0 U4 c+ z3 F6 |* W4 D8 m初始化并启动bytomd( U  g! M8 I/ i% C! j
初始化
8 Y# L4 U- U4 a, m
  1. ./bytomd init --chain_id testnet
复制代码
8 a9 R, {! n3 W0 Q* G( h- @* o" Y; f
bytomd目前支持两种网络,这里我们使用测试网  g, |& t8 G5 p9 ]
mainnet:主网
; L6 `- v: ]6 |5 Ntestnet:测试网
$ X2 [2 z. R% k$ R启动bytomd+ `# D5 @1 A. g$ J5 S( e
  1. ./bytomd node --mining --prof_laddr=":8011"
    1 ]9 ?+ w: e+ V
  2. –prof_laddr=":8080"  // 开启pprof输出性能指标
复制代码

1 w2 |" a$ p; R/ h( f, R访问:http://127.0.0.1:8080/debug/pprof/
& H- N( H5 K- d& {: Wbytomd init初始化- i! F4 b- b% B( z2 Z/ r  D
入口函数
# o% \8 j+ b6 m) }
  1. ** cmd/bytomd/main.go **
    + [$ d) `& }: A! _) S
  2. func init() {
    ) k- v# k0 e) M/ z, X8 R: H0 b$ i1 t
  3.         log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableColors: true})5 x  r$ |5 L& z4 E% D: F6 K
  4.         // If environment variable BYTOM_DEBUG is not empty,
    # r! Y4 e) I: m6 E( k) s5 X# o
  5.         // then add the hook to logrus and set the log level to DEBUG: x1 D% f3 }! v, s/ c
  6.         if os.Getenv("BYTOM_DEBUG") != "" {
    3 J/ ]1 D! d& u4 S6 e6 F1 b
  7.                 log.AddHook(ContextHook{})" J& C0 R8 a: Z* B3 i3 b# {
  8.                 log.SetLevel(log.DebugLevel)
    1 y  L2 o8 g3 m! k" M7 y
  9.         }. m* @! z- D+ w" h' a, O' W, }
  10. }
    % X0 l- E1 j7 Q
  11. func main() {
    % {! H1 G: X/ g$ ^: {8 _
  12.         cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv(config.DefaultDataDir()))
    ; x0 P: t- {! I# z- `
  13.         cmd.Execute()9 i6 a4 w3 r4 i4 M) k. c4 w
  14. }
复制代码

7 U8 E; w% W2 o8 Y* L  Pinit函数会在main执行之前做初始化操作,可以看到init中bytomd加载BYTOM_DEBUG变量来设置debug日志输出; s( T/ ~# c6 h* K4 f
command cli传参初始化( k7 L4 Q/ U# u: ^8 l
bytomd的cli解析使用cobra库" g" \6 G* ^8 A2 E& u6 a0 e
** cmd/bytomd/commands **
. t( R1 p1 a# K/ ]8 }' Icmd/bytomd/commands/root.go/ Y5 Q6 u% s2 T9 y2 u9 O
初始化–root传参。bytomd存储配置、keystore、数据的root目录。在MacOS下,默认路径是~/Library/Bytom/cmd/bytomd/commands/init.go  }. f* O2 X2 ]- s# z2 T  h+ j
初始化–chain_id传参。选择网络类型,在启动bytomd时我们选择了testnet也就是测试网络cmd/bytomd/commands/version.go
# X5 O- u% F' n# j7 w+ C- m' ]/ x初始化version传参
cmd/bytomd/commands/run_node.go7 |" h# s& g2 @  k3 ]+ p9 p( h
初始化node节点运行时所需要的传参  x3 Y7 g! R# S1 Y$ }. M

: e9 O% B; `) V; a& C6 m6 v' L初始化默认配置( d) p8 Z, q/ @2 Y& ?
用户传参只有一部分参数,那节点所需的其他参数需要从默认配置中加载。
' v7 U6 z0 B+ Q( D% R3 b
  1. ** cmd/bytomd/commands/root.go **" Q, G) @; e: T! [! R2 z
  2. var (7 |( Y  `8 v, m
  3.         config = cfg.DefaultConfig()
    * x4 a' c' A0 ?( p5 }0 U2 b4 R
  4. )5 n: a. S+ K- J& L! Z% d8 R, |
  5. 在root.go中有一个config全局变量加载了node所需的所有默认参数7 u- H& i9 a1 v9 {8 `* U3 W# S
  6. // Default configurable parameters.; F/ V+ ^2 E+ L8 r. s
  7. func DefaultConfig() *Config {3 e: q- ^, V7 Z+ |6 p( I+ F* O8 F/ {
  8.         return &Config{
    5 j2 l3 A7 ^$ O$ t# |
  9.                 BaseConfig: DefaultBaseConfig(),  // node基础相关配置
    ' R& q/ H* v; ~% h" N
  10.                 P2P:        DefaultP2PConfig(),   // p2p网络相关配置
    # {3 R  j! p$ T! i; ?
  11.                 Wallet:     DefaultWalletConfig(),   // 钱包相关配置2 w) D# }2 h& }0 U& U7 P
  12.                 Auth:       DefaultRPCAuthConfig(),  // 验证相关配置
    + a3 z9 u. U" O: v' \. s6 o
  13.                 Web:        DefaultWebConfig(),   // web相关配置2 Z" W9 V6 b: m, I. S; r' u
  14.         }
    5 G' T4 q& m, V, b. n4 B- [
  15. }
复制代码
% ?* t- k( k' Z; q) q6 p. m
后面的文章会一一介绍每个配置的作用
; W  _6 a% V2 s- |2 w7 r7 bbytomd 守护进程启动与退出
. `0 ~# p2 p( ]5 w2 h# x7 _5 o
  1. ** cmd/bytomd/commands/run_node.go **
    % V7 e3 r1 [& M: {( ^" w
  2. func runNode(cmd *cobra.Command, args []string) error {% ~& y- l( x. t" W, s6 J
  3.         // Create & start node1 ]! P7 ~' z0 t( B; d+ C- Z& Y
  4.         n := node.NewNode(config)" ^- ?& q0 o* \5 ?- g) R" @& Y
  5.         if _, err := n.Start(); err != nil {
    - A( Z' U* H' Q/ b! D: M
  6.                 return fmt.Errorf("Failed to start node: %v", err)+ e6 X5 @+ ?+ I
  7.         } else {: O+ a8 a$ }9 B6 \
  8.                 log.WithField("nodeInfo", n.SyncManager().Switch().NodeInfo()).Info("Started node")3 w7 L" d$ k4 Q7 H# f1 D9 B$ q
  9.         }
    - e8 e/ H( t4 B/ a& a* t
  10.         // Trap signal, run forever.% G7 ^2 }- U' p- ?' m4 L
  11.         n.RunForever()9 O0 P  C& _' K: [' ~
  12.         return nil
    . I5 O+ B1 S5 d% L- I& \
  13. }
复制代码
/ d' N+ G' p  c9 `' f
runNode函数有三步操作:
! v  F& C% G0 z5 U2 knode.NewNode:初始化node运行环境
- U5 N, ]1 H# Y8 {% Z- \% Rn.Start:启动node! \5 G8 E% R7 N1 F0 I8 P3 l
n.RunForever:监听退出信号,收到ctrl+c操作则退出node。在linux中守进程一般监听SIGTERM信号(ctrl+c)作为退出守护进程的信号
; n2 g" ?' m7 z4 {初始化node运行环境8 V, m4 f! }7 g3 h! [$ D9 u+ R, V
在bytomd中有五个db数据库存储在–root参数下的data目录4 m8 |+ _" X. I6 n3 U' A9 r6 I
accesstoken.db    // 存储token相关信息(钱包访问控制权限)trusthistory.db   // 存储p2p网络同步相关信息txdb.db           // 存储交易相关信息txfeeds.db        //wallet.db         // 存储钱包相关信息
. I% }. c$ j, _7 Y4 R2 J: g2 n

  1. ; d! H, _. y# Q: ~1 C% R
  2. ** node/node.go **
    : C, m0 t4 }+ B  y7 P, i& A
  3. func NewNode(config *cfg.Config) *Node {0 h+ z8 k! v! v  n" y# ?& n
  4.         ctx := context.Background()7 V) E0 i. J, C
  5.         initActiveNetParams(config)
    % @% D6 A( D# M- E, n) k9 C
  6.         // Get store 初始化txdb数据库5 D1 D6 d& T/ N5 w
  7.         txDB := dbm.NewDB("txdb", config.DBBackend, config.DBDir())4 P; d% X  ~/ Q
  8.         store := leveldb.NewStore(txDB)2 [9 n' W9 N( m1 Q+ r
  9.   // 初始化accesstoken数据库3 @$ n6 b8 }6 J
  10.         tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir())1 C6 c. [# L2 Z( s
  11.         accessTokens := accesstoken.NewStore(tokenDB)5 A( }; s/ o. t
  12.   // 初始化event事件调度器,也叫任务调度器。一个任务可以被多次调用& L  C$ z' R1 ~8 C% P7 o
  13.         // Make event switch' \& z1 m3 @' l* \- @4 M/ I; N( P
  14.         eventSwitch := types.NewEventSwitch(): \% P2 Q/ g& ~& |  Y
  15.         _, err := eventSwitch.Start()9 P; n$ b- d2 R! M! M
  16.         if err != nil {
    . \  j: @1 G- K) J
  17.                 cmn.Exit(cmn.Fmt("Failed to start switch: %v", err))1 k9 l, B( X7 W* [8 ?+ z7 a
  18.         }! N' s/ i5 Z* e3 [9 \4 l5 O% [
  19.   // 初始化交易池9 X' n& x# ~" \# i5 {
  20.         txPool := protocol.NewTxPool()& h# z! S* F: \3 j1 ]& ~
  21.         chain, err := protocol.NewChain(store, txPool)
    ( K1 C# p0 A- d3 S2 w" ^
  22.         if err != nil {( m, \# k8 D# \$ x- d- S. C
  23.                 cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))) r/ l7 R; S% a* B; Q* p/ H- C
  24.         }' ^) K3 F# p: E  f# J& e/ p, q. [
  25.         var accounts *account.Manager = nil
    ( t5 ^: K8 N/ J
  26.         var assets *asset.Registry = nil
    " w2 ~! v7 H# d' |
  27.         var wallet *w.Wallet = nil
    # O6 a' T) L' C' m2 s# t3 |
  28.         var txFeed *txfeed.Tracker = nil
    # T- E3 q0 w! P3 [; m
  29.   // 初始化txfeeds数据库
    4 ^, P0 S6 t& m8 p4 Y4 O
  30.         txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())0 k. Q% c- c7 b
  31.         txFeed = txfeed.NewTracker(txFeedDB, chain)5 }5 Q2 f5 `0 S7 c  M! W( s5 R1 A
  32.         if err = txFeed.Prepare(ctx); err != nil {
    * ]* ]4 u+ ^) }( g* v
  33.                 log.WithField("error", err).Error("start txfeed")& j5 e" ~4 C7 T: X
  34.                 return nil$ b' b0 m/ `, P) t5 G' P, u/ t2 B
  35.         }
    $ H$ Y; O3 q) O
  36.   // 初始化keystore$ A0 p9 M* S+ L7 U
  37.         hsm, err := pseudohsm.New(config.KeysDir())0 M# H& k2 D9 \; Z5 i
  38.         if err != nil {
    7 o( y! ?* j( Y' o
  39.                 cmn.Exit(cmn.Fmt("initialize HSM failed: %v", err))
    : y8 N9 k/ ~/ L& B0 i8 }% e6 l
  40.         }3 j% S8 u9 r% V- M& F) f
  41.   // 初始化钱包,默认wallet是开启状态
    - T6 I4 h! I$ O" ^9 E: C
  42.         if !config.Wallet.Disable {* O# S/ @6 y# f4 s( r4 d- R
  43.                 walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())" F1 u8 p' i$ |0 c* D6 H+ o' l7 J7 z
  44.                 accounts = account.NewManager(walletDB, chain)
    0 E) j* r6 s4 A4 M
  45.                 assets = asset.NewRegistry(walletDB, chain)
      Y8 D9 i1 J  m, P/ l6 q) O
  46.                 wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain)6 j# i9 H2 a. |
  47.                 if err != nil {
    : r6 g1 Y8 Z7 _% l% `
  48.                         log.WithField("error", err).Error("init NewWallet")- _1 Y3 }7 x7 e
  49.                 }" {9 Z4 N* g# V: f  h
  50.                 // Clean up expired UTXO reservations periodically.
      y6 b" W; m# @+ U! U: I& m7 _
  51.                 go accounts.ExpireReservations(ctx, expireReservationsPeriod): \6 W" ]4 l  G
  52.         }
    ' p7 \. N2 k4 ~* \) a/ P& J
  53.         newBlockCh := make(chan *bc.Hash, maxNewBlockChSize)! _. P* p: a* W0 o! Q0 u( I9 ^
  54.   // 初始化网络节点同步管理
    8 h/ A1 `5 Y( K5 v
  55.         syncManager, _ := netsync.NewSyncManager(config, chain, txPool, newBlockCh)8 `+ s- z+ ]1 K7 @# ]7 t$ \+ F
  56.   // 初始化pprof,pprof用于输出性能指标,需要制定--prof_laddr参数来开启,在文章开头我们已经开启该功能
    ; z9 W9 g8 v+ D, L% R
  57.         // run the profile server$ v6 ?. S' f9 v
  58.         profileHost := config.ProfListenAddress: @- G  p7 s) j6 D" p2 {' F. n5 M
  59.         if profileHost != "" {, I( u* d% Q4 Y/ i& u) P8 u
  60.                 // Profiling bytomd programs.see (<a href="https://blog.golang.org/profiling-go-programs" target="_blank">https://blog.golang.org/profiling-go-programs</a>)
    % y# `0 @* S' F/ C$ U/ z* F# n
  61.                 // go tool pprof http://profileHose/debug/pprof/heap
    " C6 Q; b% b' {" q# t, a3 c1 s
  62.                 go func() {9 v2 p8 Z3 J9 [, t
  63.                         http.ListenAndServe(profileHost, nil)4 B0 R* H3 I! ^& I1 p7 o' I
  64.                 }()
    ' E" R8 u. H& \' Q) U
  65.         }
    2 k; K9 f) u3 |$ k4 ~0 x* I3 b
  66.   // 初始化节点,填充节点所需的所有参数环境) a/ {  m( y9 r. G
  67.         node := &Node{8 J# i! {! I" X6 o
  68.                 config:       config,
    ) z" Z5 c4 ^* y! B3 g8 a, ]
  69.                 syncManager:  syncManager,- j8 `1 _0 u; @+ M! I" n6 [
  70.                 evsw:         eventSwitch,& W( i" U& t" G8 G$ F- r$ A6 x% t) X
  71.                 accessTokens: accessTokens,
    % J4 ?% e& B1 C+ I- `/ k
  72.                 wallet:       wallet,# `& ]" ~, I. J, [
  73.                 chain:        chain,
    ( Q5 j5 a2 o* Z  t+ z1 ~3 e
  74.                 txfeed:       txFeed,4 w  T% [% e/ M
  75.                 miningEnable: config.Mining,( Z; f' `4 Y* c
  76.         }/ N$ O4 ^- c3 M* `! L
  77.   // 初始化挖矿: M/ q; Z. [6 ]! F& ~. i
  78.         node.cpuMiner = cpuminer.NewCPUMiner(chain, accounts, txPool, newBlockCh)7 n! V* [1 }8 H2 N2 o& v
  79.         node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, newBlockCh)9 c- m; J5 u4 H6 v
  80.         node.BaseService = *cmn.NewBaseService(nil, "Node", node)) A' L: J1 ~& Y
  81.         return node* w8 i, X4 ~+ w% N9 \8 l! @8 P) h
  82. }
复制代码
4 G$ O: G) `/ m
目前bytomd只支持cpu挖矿,所以在代码中只有cpuminer的初始化信息8 [! p0 |1 G, m0 K" h
启动node7 f2 T# ~8 z5 X9 V7 ^% H8 l* _
  1. ** node/node.go **
    ! q- k; b4 [& ^# D( L
  2. // Lanch web broser or not# K+ ?2 V1 N4 ?. W
  3. func lanchWebBroser() {/ {/ N$ n3 |3 C/ n! m# U  n6 h$ y
  4.         log.Info("Launching System Browser with :", webAddress)
    - `$ o4 o( B0 H' W# V" q3 v1 P6 e/ e6 w
  5.         if err := browser.Open(webAddress); err != nil {
    7 W* [! f. D4 r8 k7 T
  6.                 log.Error(err.Error())
    6 ~# `( e) u# H! {% i" j7 @
  7.                 return
    + F) M# ~7 B' P* m
  8.         }5 j( B6 m- B0 n( B" {
  9. }, ]1 w; Y* Y- ?5 D& `# X
  10. func (n *Node) initAndstartApiServer() {% N1 d9 i/ C+ w% n/ |2 E2 |
  11.         n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)  P& T9 Q; i8 p& Q/ G1 t
  12.         listenAddr := env.String("LISTEN", n.config.ApiAddress)
    ! D2 u7 p3 ^0 a& w- F2 D- H
  13.         env.Parse()* n  b  A% u, ?7 ~$ g$ C" G! X
  14.         n.api.StartServer(*listenAddr)' T& _0 E+ e5 N) J! I/ P: P+ n  v
  15. }
    : w# c4 z% O- M& G! C$ ?) @
  16. func (n *Node) OnStart() error {( a8 F' Y- s6 _5 B
  17.         if n.miningEnable {
    ! V/ F8 `( u! @* A7 h! |
  18.                 n.cpuMiner.Start()# _# J  z- X' y8 n$ d
  19.         }2 X+ g* Y! L8 r4 |* c4 s) u
  20.         n.syncManager.Start()) ]0 [7 f' v' [' o! b8 X! O, k  `
  21.         n.initAndstartApiServer()
    + s0 W( y7 S4 m8 V) ~7 {7 C1 n
  22.         if !n.config.Web.Closed {
    7 y3 O+ {* V* }- p
  23.                 lanchWebBroser()
    0 s; R# H0 k, D0 ~
  24.         }
    ) K2 N7 u; y% J# D) R7 X* Y
  25.         return nil/ L: Y% x6 g" m
  26. }
复制代码
+ W* R% @* K0 T6 ]1 r
OnStart() 启动node进程如下:9 \5 p5 K8 _2 W% Z* v" H1 j
启动挖矿功能启动p2p网络同步启动http协议的apiserver服务打开浏览器访问bytond的交易页面3 D" W  g* s1 a% X+ d

% }% j* a" C4 @* c停止node" L5 \( v+ ~8 o7 w& a6 z* p6 \
bytomd在启动时执行了n.RunForever()函数,该函数是由tendermint框架启动了监听信号的功能:
6 v; U- z+ R) H  Y! R1 {
  1. ** vendor/github.com/tendermint/tmlibs/common/os.go **
    0 s+ q. K+ G: N
  2. func TrapSignal(cb func()) {" ~1 T& l+ n' X7 K" q6 L
  3.         c := make(chan os.Signal, 1)5 e+ P! Z& {( t+ i+ ^
  4.         signal.Notify(c, os.Interrupt, syscall.SIGTERM)& H9 ^" b; v8 h) n1 {+ e1 ]
  5.         go func() {
    ' C! y3 T* M2 O( g1 ^4 \* a( v
  6.                 for sig := range c {
    9 b) a4 X& Z& G1 [
  7.                         fmt.Printf("captured %v, exiting...\n", sig)
    : I( m- O- C, c* C) {6 l
  8.                         if cb != nil {# q8 u! G6 e* s( Y7 b! ^0 r. j# }
  9.                                 cb()
    4 p: i" D8 m5 P# e8 C
  10.                         }7 b5 V/ o, }! a$ x- ]) X
  11.                         os.Exit(1)
    . E7 P: k% p0 L% k1 b
  12.                 }3 F' M  ^# f* ]4 ~
  13.         }()  o$ G( O. Q6 S. D! f; t' y( ~4 I
  14.         select {}1 F7 X- Y' O' Y" I  C4 H3 G( m$ J
  15. }
复制代码
9 a( i% i) \& t3 }5 G6 L: O2 B8 |1 c
TrapSignal函数监听了SIGTERM信号,bytomd才能成为不退出的守护进程。只有当触发了ctrl+c或kill bytomd_pid才能终止bytomd进程退出。退出时bytomd执行如下操作. A3 z, ]' ?4 S, E8 E; \& X6 U
  1. ** node/node.go **
    + l5 r( m% N: h) d3 F6 b
  2. func (n *Node) OnStop() {+ W5 {! C2 r1 Q" E; j1 L. c
  3.         n.BaseService.OnStop()
    2 K. x4 |4 m/ N8 d
  4.         if n.miningEnable {
    , x4 e; D$ `: z5 u
  5.                 n.cpuMiner.Stop()
    # [" w# G8 U( A& Q
  6.         }
      }& T: k+ f5 A# o! H- F+ Z
  7.         n.syncManager.Stop()
    $ r# k7 C$ l8 c
  8.         log.Info("Stopping Node"), x' e+ p9 {; w- z9 o- \6 B
  9.         // TODO: gracefully disconnect from peers.
    5 A7 d* O: w. H1 }1 J
  10. }
复制代码

' P7 U  Y. G1 |8 r: l* {7 a5 G. Qbytomd会将挖矿功能停止,p2p网络停止等操作。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    2

  • 主题

    4