Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

bytom源码分析-启动与停止

蔡健雅的高跟鞋
180 0 0
作者使用MacOS操作系统,其他平台也大同小异6 A4 b6 r: a* ~5 C, f1 t: d
Golang Version: 1.8
$ j+ P# ]2 d6 D1 X& x0 l
7 J6 f/ j) }% T4 d
预备工作5 I1 F3 D; z& I3 k
编译安装
( d; t  B1 A4 q  S详细步骤见官方 bytom install
" q5 ?( O4 q7 O6 R4 X7 [% t设置debug日志输出
3 q( A! [& m3 G4 P! S9 S6 n开启debug输出文件、函数、行号等详细信息' R  |! d+ H/ W" V
  1. export BYTOM_DEBUG=debug
复制代码
. J" o1 Y7 \7 H5 _; d+ w" e
初始化并启动bytomd
* l  s& _* y. T  }- R初始化
: A) U5 D7 w; H( }. j  U% ]8 h+ ]
  1. ./bytomd init --chain_id testnet
复制代码

. }& |5 V8 Q- `; K. X; ?1 y' q. Ybytomd目前支持两种网络,这里我们使用测试网1 e1 f6 V0 I, n2 v8 o. V
mainnet:主网
6 l/ m6 a7 {# T( {testnet:测试网
5 H, o1 w: I# {2 u( }启动bytomd
7 L& C  |: R5 Q) z) m4 E* z! o6 B# L
  1. ./bytomd node --mining --prof_laddr=":8011"! R% q7 u1 ^3 E: o& i' N! G
  2. –prof_laddr=":8080"  // 开启pprof输出性能指标
复制代码

: ~8 r1 P7 j5 H* C2 h; m2 Y访问:http://127.0.0.1:8080/debug/pprof/
2 ]( K/ q: H% Mbytomd init初始化
& `/ Z! e# R3 W" s6 j0 X入口函数
* l7 x3 x& ~/ s( h
  1. ** cmd/bytomd/main.go **
    # `1 a) k" R" v1 k9 F6 H
  2. func init() {
    6 q% y5 F/ l# e& ]
  3.         log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableColors: true})
    : c1 L/ g2 T1 b3 n7 c% q8 f
  4.         // If environment variable BYTOM_DEBUG is not empty,1 K  f  e, o, v6 c5 L' V9 f
  5.         // then add the hook to logrus and set the log level to DEBUG3 M# X% @. l) V5 D2 z4 m7 O+ A
  6.         if os.Getenv("BYTOM_DEBUG") != "" {
    4 R. Z9 d4 ^3 {0 J. E9 j8 {( u
  7.                 log.AddHook(ContextHook{})! v' Q% L5 F) z3 g
  8.                 log.SetLevel(log.DebugLevel)
    : ^0 c7 |- r, |- S
  9.         }* x8 L% n3 c: }
  10. }5 \$ \9 v' k8 U: E7 l3 O$ o
  11. func main() {' K. b5 W8 v& ?# W$ E
  12.         cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv(config.DefaultDataDir()))
    . x0 ]2 v) V( Z' U  N
  13.         cmd.Execute()0 Q+ [- f  G) a& j" u' t5 e
  14. }
复制代码

: Z% r3 S3 n- L6 d& yinit函数会在main执行之前做初始化操作,可以看到init中bytomd加载BYTOM_DEBUG变量来设置debug日志输出
% H3 d+ |' Q% `  O4 Qcommand cli传参初始化
3 k) R+ F' B& r/ k& ]2 r( ^bytomd的cli解析使用cobra库
# i. y8 C% B- s( {+ M3 ~; H$ N** cmd/bytomd/commands **4 }" s, C: x; Q1 V" G
cmd/bytomd/commands/root.go( H0 V2 X; r" N4 V- T& E
初始化–root传参。bytomd存储配置、keystore、数据的root目录。在MacOS下,默认路径是~/Library/Bytom/cmd/bytomd/commands/init.go  @5 A( ^# H; p! s2 t
初始化–chain_id传参。选择网络类型,在启动bytomd时我们选择了testnet也就是测试网络cmd/bytomd/commands/version.go8 M" d* s( r8 d
初始化version传参
cmd/bytomd/commands/run_node.go
4 a; _. b' z  u  d0 a6 ]3 Z初始化node节点运行时所需要的传参
- ]) r7 b; I. F5 z
+ e; o4 ~: M" `/ U5 ?+ W
初始化默认配置' X' t& X( G1 C* r% f0 I6 V9 d
用户传参只有一部分参数,那节点所需的其他参数需要从默认配置中加载。6 l7 _7 \/ l( [% e# ~4 N* m: {
  1. ** cmd/bytomd/commands/root.go **
    9 F: X4 o. J0 w) ?' t- i
  2. var (
    , {' |8 A3 u( V& _
  3.         config = cfg.DefaultConfig()
    8 ~% r$ P6 X( o* X* t. ?
  4. )9 w& y# x1 z+ M& P& l8 c
  5. 在root.go中有一个config全局变量加载了node所需的所有默认参数
    # Q" E+ ?( d5 T% {6 P1 ?7 S
  6. // Default configurable parameters., ^! O, |( t. N, Z/ D+ t+ \
  7. func DefaultConfig() *Config {
    5 N! Y2 t% b; k3 K8 Q
  8.         return &Config{) h* I) J- `& s+ A1 G% K
  9.                 BaseConfig: DefaultBaseConfig(),  // node基础相关配置
    2 o9 H( {- w+ O8 V
  10.                 P2P:        DefaultP2PConfig(),   // p2p网络相关配置* @" D: L8 ]; S4 o) J
  11.                 Wallet:     DefaultWalletConfig(),   // 钱包相关配置
    ) v4 @  c2 J: F3 q8 _5 ^4 D" b
  12.                 Auth:       DefaultRPCAuthConfig(),  // 验证相关配置
    3 O" |( b7 Q* m& q, R" L- j
  13.                 Web:        DefaultWebConfig(),   // web相关配置7 N1 i$ m4 d! U, q7 @
  14.         }
    * t' j9 |" z( ]5 g- G: \% K( g9 l
  15. }
复制代码

$ I! u3 Z) [$ j1 P后面的文章会一一介绍每个配置的作用
8 b$ x* j: o! T- g9 Wbytomd 守护进程启动与退出, O! j' j8 M: r1 \% s2 |) M# \
  1. ** cmd/bytomd/commands/run_node.go **
    7 z/ j$ ]# Y' w5 e
  2. func runNode(cmd *cobra.Command, args []string) error {
    8 H/ y1 R# ^  r" h# ^# U
  3.         // Create & start node: l+ s4 M$ T! ]. V
  4.         n := node.NewNode(config)
    2 ]: b8 S9 p$ H4 N2 ^8 o+ m: J0 b& W8 [
  5.         if _, err := n.Start(); err != nil {7 s% P2 J1 F/ U% w6 w6 e( Z
  6.                 return fmt.Errorf("Failed to start node: %v", err)
    , W1 K& L2 q( s& J
  7.         } else {
    ' ~0 y$ o: G5 e9 i3 l' n4 H
  8.                 log.WithField("nodeInfo", n.SyncManager().Switch().NodeInfo()).Info("Started node")0 |4 y2 L/ h+ x9 Q) y- }
  9.         }
    3 |% w3 R! z6 e/ @0 d) U
  10.         // Trap signal, run forever.7 @$ D" e: u: x6 G
  11.         n.RunForever()
    ; Z+ n2 F& V5 T
  12.         return nil
    4 _5 v" P7 G7 ^1 a4 O. W
  13. }
复制代码

- N. w& p2 x4 rrunNode函数有三步操作:/ F' c! v2 T) k8 f; `& n
node.NewNode:初始化node运行环境$ @+ f  E  T7 j) L
n.Start:启动node/ @! A' p( b: f8 y6 [$ }
n.RunForever:监听退出信号,收到ctrl+c操作则退出node。在linux中守进程一般监听SIGTERM信号(ctrl+c)作为退出守护进程的信号8 m6 G' `' \. Y( C
初始化node运行环境
% r9 Z% a2 f/ x  K6 K  L在bytomd中有五个db数据库存储在–root参数下的data目录4 W7 t: h) b3 L8 m/ Y: U$ R
accesstoken.db    // 存储token相关信息(钱包访问控制权限)trusthistory.db   // 存储p2p网络同步相关信息txdb.db           // 存储交易相关信息txfeeds.db        //wallet.db         // 存储钱包相关信息
8 [( Q; Y% X1 L3 P. z

  1. 8 G7 w) Q% R/ V+ k$ J2 ]2 [
  2. ** node/node.go **
    8 d/ K& E3 j' C; ?0 f
  3. func NewNode(config *cfg.Config) *Node {
    & c# J: w5 E+ x/ m1 e
  4.         ctx := context.Background()2 N  @) ]% G3 K$ ?+ z; I, n
  5.         initActiveNetParams(config)& k6 w1 R: f2 I5 F
  6.         // Get store 初始化txdb数据库
    : E7 q+ y- N! h8 }3 I$ {( K
  7.         txDB := dbm.NewDB("txdb", config.DBBackend, config.DBDir())
    & v3 y$ s; ^* X3 `) j( n
  8.         store := leveldb.NewStore(txDB)
      A' F( g, _* Z
  9.   // 初始化accesstoken数据库% B" ~/ ^; {2 c! N0 r& ]+ Y
  10.         tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir())) ]* w, f2 j; O5 P# D1 W7 c
  11.         accessTokens := accesstoken.NewStore(tokenDB)3 q( Y8 w* y- ^! R/ \: `! Q% K1 b1 p- F
  12.   // 初始化event事件调度器,也叫任务调度器。一个任务可以被多次调用* d# `0 _: g& Q* w/ I7 j
  13.         // Make event switch7 K0 t( o. Y3 w! J2 ^
  14.         eventSwitch := types.NewEventSwitch(), n2 B+ h9 z* f- r& H
  15.         _, err := eventSwitch.Start()
    8 g" u9 o- u+ b3 k9 D6 x3 ?
  16.         if err != nil {0 w1 w# a. d1 R5 C. O% m
  17.                 cmn.Exit(cmn.Fmt("Failed to start switch: %v", err))# n1 w8 o# U$ s7 h# b$ `9 t3 G
  18.         }
    ' K( h$ \4 M2 Q( N. [# r& k
  19.   // 初始化交易池4 ?' l* u. t; I: N- ~! X
  20.         txPool := protocol.NewTxPool()
    ! q, |1 t% X- ]0 @& d5 R
  21.         chain, err := protocol.NewChain(store, txPool)7 w  F9 ^/ u) G0 ^
  22.         if err != nil {
    6 g! n1 U7 k$ t# r* W
  23.                 cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))
    + K! c8 g5 W, s) U) {, g
  24.         }/ C( a, A" |) E7 D3 Z
  25.         var accounts *account.Manager = nil
    6 Q, a0 [5 a4 T: `9 H% o! b$ B
  26.         var assets *asset.Registry = nil
    2 c' t: }  d4 c2 [( B/ g, f. t
  27.         var wallet *w.Wallet = nil6 _. x5 V' \2 Y6 k
  28.         var txFeed *txfeed.Tracker = nil% h* l5 g1 `* G- A. ]2 P
  29.   // 初始化txfeeds数据库
    + `1 U# F. S1 N& ]! P$ R2 {
  30.         txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())1 i: z9 U" y8 k7 }1 u8 g. a9 \
  31.         txFeed = txfeed.NewTracker(txFeedDB, chain)
    5 |- P5 H2 N' p. S) f6 A
  32.         if err = txFeed.Prepare(ctx); err != nil {
    ! _& j& }& o7 _. L8 I
  33.                 log.WithField("error", err).Error("start txfeed")
    0 j- j# F/ ]' m6 Z! r) x/ l7 }
  34.                 return nil4 N3 m: t: w; g5 D, v, U# I, D
  35.         }
    6 E# v) U: G) L
  36.   // 初始化keystore
    # p& i2 I; S! t5 `- K4 n2 _
  37.         hsm, err := pseudohsm.New(config.KeysDir())1 u6 N! l! y+ E% _3 p4 u
  38.         if err != nil {
    + {* C8 {# \! x& z
  39.                 cmn.Exit(cmn.Fmt("initialize HSM failed: %v", err))9 P1 i$ l. C# Z; A3 }7 n
  40.         }+ b2 L( a5 \8 W$ G0 M5 s1 d2 `
  41.   // 初始化钱包,默认wallet是开启状态( u) t. c4 o4 m# D( }9 b. y+ `
  42.         if !config.Wallet.Disable {
    1 g) H% k& `) j6 K' c% o
  43.                 walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())
    . g5 r5 l# C3 C1 e& r. p! U
  44.                 accounts = account.NewManager(walletDB, chain)) _" S  G1 j/ {1 h1 w6 M
  45.                 assets = asset.NewRegistry(walletDB, chain)
    ! }/ _0 ~* ^& d) P
  46.                 wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain)
    " t& n1 w6 _1 ^- ~2 n
  47.                 if err != nil {$ q* J8 y& ^$ \, Y4 M5 V
  48.                         log.WithField("error", err).Error("init NewWallet")
    ! q- ]" \; |- q9 ]7 Q
  49.                 }
    2 c+ z) v9 f* Y& g0 B0 ~
  50.                 // Clean up expired UTXO reservations periodically.  t1 v; x" x. S% F2 U: o2 q/ q
  51.                 go accounts.ExpireReservations(ctx, expireReservationsPeriod)
    % C% i" K% i* @6 I, q
  52.         }6 p% P# v# `! U$ Y2 }$ x9 b: d
  53.         newBlockCh := make(chan *bc.Hash, maxNewBlockChSize)) N; J4 S  x, a  G+ B' n* o
  54.   // 初始化网络节点同步管理6 V6 L; p1 {4 f" u, P1 [- a
  55.         syncManager, _ := netsync.NewSyncManager(config, chain, txPool, newBlockCh). _% m; J( j& a, X
  56.   // 初始化pprof,pprof用于输出性能指标,需要制定--prof_laddr参数来开启,在文章开头我们已经开启该功能
    ( p- \/ H# j  G/ [" J( D+ l
  57.         // run the profile server4 j7 F' `7 A7 x$ g( \! u
  58.         profileHost := config.ProfListenAddress4 W% I- P3 f5 l. _/ k' c
  59.         if profileHost != "" {
    5 f; g9 O9 A0 T; @1 S5 ~9 l$ v
  60.                 // Profiling bytomd programs.see (<a href="https://blog.golang.org/profiling-go-programs" target="_blank">https://blog.golang.org/profiling-go-programs</a>)4 [  _; g7 g- {0 J8 V- F3 Q% h, A
  61.                 // go tool pprof http://profileHose/debug/pprof/heap
    ' M3 @1 y% Y+ N1 Z! d8 {- N% x
  62.                 go func() {
    : R+ B; [5 N$ I, o/ w$ I
  63.                         http.ListenAndServe(profileHost, nil)& F/ C/ J+ C6 ], ], x2 J
  64.                 }()+ S3 |7 t7 v4 |8 h6 N- e& s9 n8 c
  65.         }
    % @, t- D% @- v  G) D$ b
  66.   // 初始化节点,填充节点所需的所有参数环境! `( ~! H& a* O- A7 S
  67.         node := &Node{2 j- _1 }" b  W3 p1 d
  68.                 config:       config,
    & A* v3 }5 |' b% ]% I
  69.                 syncManager:  syncManager,& P* m' o9 k6 L
  70.                 evsw:         eventSwitch,
    - B5 g3 j- ]+ M) Z
  71.                 accessTokens: accessTokens,2 E5 f7 x9 v& R7 F) ~
  72.                 wallet:       wallet,& Y7 f- J# y( ^
  73.                 chain:        chain,
    ( L- v: P8 f: d* F5 ?9 ~% T+ M
  74.                 txfeed:       txFeed,
    % a, }: b3 }$ ~4 N, C
  75.                 miningEnable: config.Mining,. f, ?( z! H, j" e7 }
  76.         }1 g5 N. L8 D: ^
  77.   // 初始化挖矿
    % q- q, ^, o  W, |
  78.         node.cpuMiner = cpuminer.NewCPUMiner(chain, accounts, txPool, newBlockCh)
    4 b* W; n8 x& b* N, K# t2 c- @4 Q
  79.         node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, newBlockCh)/ D3 B$ I4 [4 A. b5 A, v
  80.         node.BaseService = *cmn.NewBaseService(nil, "Node", node)
    $ S9 q6 y! E% f3 t! x
  81.         return node
    # ]# ?$ n# l* A9 ^8 ~5 i( V5 x  J
  82. }
复制代码

6 X2 W: f+ z" a目前bytomd只支持cpu挖矿,所以在代码中只有cpuminer的初始化信息) s8 N# Z" `5 E+ s! f
启动node
2 y) f2 ?% _/ K3 o  C8 m2 [
  1. ** node/node.go **' O' D& {2 Z5 N
  2. // Lanch web broser or not2 q* n/ V' k& G' a6 q
  3. func lanchWebBroser() {
    : K7 W: w" }' G% G" _
  4.         log.Info("Launching System Browser with :", webAddress)
    ( H. r! u1 ]8 U# X9 G
  5.         if err := browser.Open(webAddress); err != nil {7 z+ B4 r0 F+ B1 C
  6.                 log.Error(err.Error())
    1 J! ~' m/ Y" g8 B! [
  7.                 return
    $ P, F7 m- v0 \3 S; `/ V
  8.         }, r% o0 |' Y1 `5 J
  9. }
    * w" T  L1 |4 v: @, t  D- {; r& i
  10. func (n *Node) initAndstartApiServer() {" R5 B* N4 }8 U; v
  11.         n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)
    / ^. f2 H6 A; a+ u5 M
  12.         listenAddr := env.String("LISTEN", n.config.ApiAddress)
    ( k' ?$ ~+ s8 b1 a# ~
  13.         env.Parse()1 H4 v6 r3 K; u7 ]! r5 m
  14.         n.api.StartServer(*listenAddr)& \, _: E, ^& ]; F
  15. }
    * _3 C& `' A3 }9 E5 r, q0 F
  16. func (n *Node) OnStart() error {
    * C+ W+ ~/ T" n' ]* s4 N- p
  17.         if n.miningEnable {
    1 O6 E% }, o) n5 J% g/ V! H
  18.                 n.cpuMiner.Start()
    4 L' P; A; t6 _" R6 l
  19.         }6 P. _# h' ?& G$ `, ?) b5 |
  20.         n.syncManager.Start()) Q+ U! P) l$ D' k
  21.         n.initAndstartApiServer()
    8 V. T$ f- Q! P# Q
  22.         if !n.config.Web.Closed {
    5 Y$ q: ?* h5 ?! o+ V4 W$ p
  23.                 lanchWebBroser()
    6 h7 `3 w1 i. o6 @0 R
  24.         }
    4 T! w9 i( K, ~5 H* ^( R
  25.         return nil- M. ]. m% y: R& t: p1 S
  26. }
复制代码

5 S7 H8 {2 E$ w; T; R( ~5 cOnStart() 启动node进程如下:0 i8 b' H5 _" U2 k" l) P
启动挖矿功能启动p2p网络同步启动http协议的apiserver服务打开浏览器访问bytond的交易页面
+ `: r) Y8 b# \. o

- |0 d! E4 H% j9 b% {( N停止node4 d$ a4 `! C# E6 {" d& w6 F
bytomd在启动时执行了n.RunForever()函数,该函数是由tendermint框架启动了监听信号的功能:  E% g7 S/ W! w) x9 p9 a4 F
  1. ** vendor/github.com/tendermint/tmlibs/common/os.go **5 f/ l1 P  Y- j( ]9 T! Z
  2. func TrapSignal(cb func()) {
    + D) V" F1 u; W, s0 H: o9 X) {
  3.         c := make(chan os.Signal, 1)/ n% ]# y& l: `9 H, s
  4.         signal.Notify(c, os.Interrupt, syscall.SIGTERM); q  r$ F: ^" Q, X
  5.         go func() {/ q/ H( z) g4 i& P) g
  6.                 for sig := range c {
    6 D! Z  d0 n" h+ f# }1 p6 ~/ w4 ~0 V
  7.                         fmt.Printf("captured %v, exiting...\n", sig)
    6 B5 y, p; r, Y& L% G
  8.                         if cb != nil {
    7 D# ?' Q' T. D1 s0 h" K
  9.                                 cb(), Y+ u" Q7 U) z- u' m3 N4 z
  10.                         }5 Y8 [, @2 }0 S+ b; A( O
  11.                         os.Exit(1)7 b2 h, `3 N+ s  @* n6 C
  12.                 }
    $ h9 z) ~/ U& _+ p9 b
  13.         }(); M- Y7 F0 Y2 c
  14.         select {}" m8 i' s9 a& D4 N4 I3 X  A
  15. }
复制代码
# Q1 i0 D' D+ N0 L' \
TrapSignal函数监听了SIGTERM信号,bytomd才能成为不退出的守护进程。只有当触发了ctrl+c或kill bytomd_pid才能终止bytomd进程退出。退出时bytomd执行如下操作
: ]! k% a- K8 k( S7 U: L6 j, [8 g
  1. ** node/node.go **# H+ O( e7 a! A) |
  2. func (n *Node) OnStop() {& k# ]; y3 d1 _2 q0 c! L8 [" Q
  3.         n.BaseService.OnStop()$ {7 j( ?; y; r4 o( v/ z
  4.         if n.miningEnable {. y6 C" w+ C) M; @- Y
  5.                 n.cpuMiner.Stop()
    2 ~2 |( l: Z. O. b+ U2 b
  6.         }3 P0 k5 z+ z% T. S8 W6 g" J8 r
  7.         n.syncManager.Stop(); X3 a. L2 g! ?6 |  `- R
  8.         log.Info("Stopping Node")
    ( v, R3 s4 @8 a3 m4 G
  9.         // TODO: gracefully disconnect from peers.
    " G# ~6 w- Y+ E5 ~0 N  Y
  10. }
复制代码

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

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    2

  • 主题

    4