Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

bytom源码分析-启动与停止

蔡健雅的高跟鞋
131 0 0
作者使用MacOS操作系统,其他平台也大同小异3 L5 A7 P8 l  o( z& ^2 Z
Golang Version: 1.8; i: h: ]* p3 y' H2 p
* `. `: b3 @; }
预备工作$ ?4 j8 h! g# t4 [% [3 P
编译安装
- Q" O+ |! u$ [" c. D详细步骤见官方 bytom install& Y$ K0 Z) z! y2 I/ Z! K7 V& I* Z
设置debug日志输出
  }1 E! l% F. g0 ]; M开启debug输出文件、函数、行号等详细信息( n/ U! g5 u; J2 S* e5 x# ]- K6 b
  1. export BYTOM_DEBUG=debug
复制代码

) q4 D! k& y2 ?  i% I初始化并启动bytomd' E; y- [; ?: l# M6 p* x, V
初始化
# v  q; A; d' B0 c/ K/ b, J
  1. ./bytomd init --chain_id testnet
复制代码

3 t6 d# Z4 ]* m. P* i( Kbytomd目前支持两种网络,这里我们使用测试网
' t4 C2 R; r8 |+ b. r: H9 ?9 @mainnet:主网5 L. _( B4 E; k
testnet:测试网
/ x+ _& P% s  a0 l) M3 G8 q启动bytomd9 z+ {. l% k8 F; o! c
  1. ./bytomd node --mining --prof_laddr=":8011"
    4 K9 P% I$ O& V! `7 Q
  2. –prof_laddr=":8080"  // 开启pprof输出性能指标
复制代码

" b4 S/ b) y0 N访问:http://127.0.0.1:8080/debug/pprof/
4 G0 I6 F5 \$ O" W; w$ nbytomd init初始化  D/ E; y1 D+ d- c1 _
入口函数
( U, ~! |/ C* F8 I
  1. ** cmd/bytomd/main.go **7 W; H, S! q9 v# g, J: ^
  2. func init() {" c* }: V% y) R& r4 Q: n& E$ g
  3.         log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableColors: true}), [' `" |, n2 f/ V3 M
  4.         // If environment variable BYTOM_DEBUG is not empty,
    - ?7 I- X! A2 D# N1 r$ `/ K
  5.         // then add the hook to logrus and set the log level to DEBUG
    3 @; B! O. f6 k& k- g6 O" n* ]
  6.         if os.Getenv("BYTOM_DEBUG") != "" {
    & B% o( u# U3 ~5 ^* h
  7.                 log.AddHook(ContextHook{})
    ; A' w5 Z7 ]! A8 Z
  8.                 log.SetLevel(log.DebugLevel)& Q" O" L# {3 H- q9 f4 F
  9.         }
    6 d1 N0 B, t! T' ~5 Z
  10. }
      ~) y0 F# A( D7 M
  11. func main() {" _4 i- T" Q, ?
  12.         cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv(config.DefaultDataDir()))+ w9 J) J+ ]" \, A( p& E
  13.         cmd.Execute()
    / S$ o' z2 N3 J2 }  r: |: @5 {
  14. }
复制代码

% p9 O9 m9 v! S* q6 L9 a, S7 ]init函数会在main执行之前做初始化操作,可以看到init中bytomd加载BYTOM_DEBUG变量来设置debug日志输出( j0 m2 L/ e' I/ g1 H- M8 W( B) p% g
command cli传参初始化
7 G5 b2 L1 Z& A: P% u4 ibytomd的cli解析使用cobra库
" ~( Q7 N  b8 [3 v** cmd/bytomd/commands **( K4 }' |3 l: A: d& C, z
cmd/bytomd/commands/root.go
: @- {; g# e3 A" U$ o$ p+ M初始化–root传参。bytomd存储配置、keystore、数据的root目录。在MacOS下,默认路径是~/Library/Bytom/cmd/bytomd/commands/init.go
) `  T. [0 O% x初始化–chain_id传参。选择网络类型,在启动bytomd时我们选择了testnet也就是测试网络cmd/bytomd/commands/version.go
0 T& }1 b3 C: p8 M* r, K' D初始化version传参
cmd/bytomd/commands/run_node.go
" x  x  P2 C7 e, e7 ^8 f; Y初始化node节点运行时所需要的传参
# k0 i- R% E$ I9 p: u

, @: A# ~' @% ~初始化默认配置% X1 F% U( U6 k5 m
用户传参只有一部分参数,那节点所需的其他参数需要从默认配置中加载。: D% U2 D4 Y% @$ D: N& V5 O
  1. ** cmd/bytomd/commands/root.go **
    * ]( U; K& W0 F8 _4 B
  2. var ($ R* A% t6 B" `3 e% ^6 P
  3.         config = cfg.DefaultConfig()
    ( b& q* \2 n" u, r8 F
  4. )
    2 j0 j8 W) I- `; F( H
  5. 在root.go中有一个config全局变量加载了node所需的所有默认参数
    4 L* k& `, x0 `  D% r& s4 F3 q- {
  6. // Default configurable parameters.2 u  \8 N. Q- x& r
  7. func DefaultConfig() *Config {$ M; Z$ e% S; X4 D; z5 m8 D& o
  8.         return &Config{
    9 A, p7 W1 y7 k0 G7 E
  9.                 BaseConfig: DefaultBaseConfig(),  // node基础相关配置
    5 P. b( Q8 a/ s3 ]
  10.                 P2P:        DefaultP2PConfig(),   // p2p网络相关配置
    0 ?6 e; ~5 N+ X  O. R; p% j
  11.                 Wallet:     DefaultWalletConfig(),   // 钱包相关配置
    ' F- A2 V# [9 W6 C- C. O
  12.                 Auth:       DefaultRPCAuthConfig(),  // 验证相关配置: \1 K8 V( w# H+ W# e
  13.                 Web:        DefaultWebConfig(),   // web相关配置
    8 }' K5 r' N. R  v7 F
  14.         }6 L, b" R4 W$ ?
  15. }
复制代码

& W  b6 c1 N! g5 q5 h0 H后面的文章会一一介绍每个配置的作用. t! k+ j* D, i# F  m
bytomd 守护进程启动与退出
6 V9 n2 E! d3 y/ x
  1. ** cmd/bytomd/commands/run_node.go **
    - y4 l: x$ u, V7 U! Z$ t' [. o5 V
  2. func runNode(cmd *cobra.Command, args []string) error {( V8 `$ e2 Z$ ?$ g$ l! ]
  3.         // Create & start node$ |' O0 r" s% K3 @0 a4 g
  4.         n := node.NewNode(config)
    + O) C- v6 W# _0 ?; M! |
  5.         if _, err := n.Start(); err != nil {
    8 q8 y$ O( J: l0 c8 V7 k
  6.                 return fmt.Errorf("Failed to start node: %v", err)" m/ \# m) ?# |0 h. `5 v* t# T( {
  7.         } else {
    , w7 c7 ]$ p' k' Q
  8.                 log.WithField("nodeInfo", n.SyncManager().Switch().NodeInfo()).Info("Started node")
    3 M  o! ~) G; X2 c
  9.         }
    . M4 S, @' f) n! \
  10.         // Trap signal, run forever.4 q3 O% w& ^( t0 |- k% G( a
  11.         n.RunForever()
    4 S3 j. U) i7 x. G9 d7 m( M
  12.         return nil
    5 ]( m: E- |1 n. Y( P
  13. }
复制代码
: L7 A0 V- i9 H: R
runNode函数有三步操作:
: w; ?( j: ^. }3 z' ~  pnode.NewNode:初始化node运行环境
$ U* F, C! a: q& f7 Y8 _9 y& Mn.Start:启动node
; h: J) F2 x9 ^$ e7 }  en.RunForever:监听退出信号,收到ctrl+c操作则退出node。在linux中守进程一般监听SIGTERM信号(ctrl+c)作为退出守护进程的信号
) ~) ]  Z7 e" a, O初始化node运行环境
. j. ?& B+ }1 Y& E8 C. D1 O! v$ U在bytomd中有五个db数据库存储在–root参数下的data目录1 b1 O; X+ j3 v& b" ^1 _% ^0 h8 |
accesstoken.db    // 存储token相关信息(钱包访问控制权限)trusthistory.db   // 存储p2p网络同步相关信息txdb.db           // 存储交易相关信息txfeeds.db        //wallet.db         // 存储钱包相关信息
/ M1 s+ ^5 F$ {9 V( ^* z0 W
  1. 4 B* r6 g4 O! d: f
  2. ** node/node.go **
    " |! o6 U3 t, Q! [
  3. func NewNode(config *cfg.Config) *Node {4 m7 z0 M& |0 K" E" `- ?" z, N
  4.         ctx := context.Background()
    ! G# X; {, q; q7 C
  5.         initActiveNetParams(config)
    8 ^3 S9 @+ A& s& T1 v  [
  6.         // Get store 初始化txdb数据库
    ; h( y7 E6 x0 c# m+ |
  7.         txDB := dbm.NewDB("txdb", config.DBBackend, config.DBDir())* O" ~+ Q& H5 J* K
  8.         store := leveldb.NewStore(txDB)
    8 G, p- @. J( _4 @4 f8 W* s
  9.   // 初始化accesstoken数据库9 ?% o% U( y; r/ y' Y: E# U
  10.         tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir())
    1 |) m: R( M9 S& y- @9 Y+ F4 |& i6 r8 V
  11.         accessTokens := accesstoken.NewStore(tokenDB)( g. T. n9 \8 y& ?7 w0 a: a0 f3 h
  12.   // 初始化event事件调度器,也叫任务调度器。一个任务可以被多次调用+ q3 g. }! a# B1 t: x; q
  13.         // Make event switch8 V# X& z# E/ A' B2 F& t
  14.         eventSwitch := types.NewEventSwitch()
    . L$ X( _" |9 ~5 {6 r
  15.         _, err := eventSwitch.Start()1 ]) o3 i% w1 O5 O! R- y9 b6 g
  16.         if err != nil {
    " J1 `- \! z  N5 J7 r
  17.                 cmn.Exit(cmn.Fmt("Failed to start switch: %v", err))
    & L2 X3 S: Z; A3 c: B
  18.         }
    ) j4 q5 q: |' [8 ]- x
  19.   // 初始化交易池5 h$ Q3 }7 f8 |) h  N
  20.         txPool := protocol.NewTxPool()# k% Y' H2 x9 `  A% b6 S1 P
  21.         chain, err := protocol.NewChain(store, txPool)5 K1 Z3 b/ L0 F' e
  22.         if err != nil {
    ; N  H" }7 N# b& N# w/ \8 g4 _6 f; ^
  23.                 cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))8 R  p# p3 T' a6 I1 A0 {/ d
  24.         }2 ?& }; ]' j; d: t1 y; `) N
  25.         var accounts *account.Manager = nil
    6 u- F, D- b8 ]6 O
  26.         var assets *asset.Registry = nil, j$ b& \: k  b- Z5 w
  27.         var wallet *w.Wallet = nil8 c# X* E, A1 [/ ?
  28.         var txFeed *txfeed.Tracker = nil
    . R6 }  Y* D, P8 J- f2 w: W& m
  29.   // 初始化txfeeds数据库
    ' H3 ]' d9 x7 w% x9 A
  30.         txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())1 G! V* x, Y1 k# d% Y
  31.         txFeed = txfeed.NewTracker(txFeedDB, chain): A# v  b" W. T( C8 k5 s
  32.         if err = txFeed.Prepare(ctx); err != nil {. j6 T' f* W: B/ }
  33.                 log.WithField("error", err).Error("start txfeed")* Q- e& ~2 e% ?9 N4 Q
  34.                 return nil
    5 |! O6 U8 J1 L, ]
  35.         }4 S) r, @- Z8 E1 x/ X/ B. A( W
  36.   // 初始化keystore
    : t& [3 u; Q9 l2 g. {
  37.         hsm, err := pseudohsm.New(config.KeysDir())% u) X7 x& v2 S: y8 A
  38.         if err != nil {
      g! T& W( m$ l# i0 {6 X1 g
  39.                 cmn.Exit(cmn.Fmt("initialize HSM failed: %v", err))/ d$ |; W3 l4 X) H0 V( m3 H
  40.         }
    " L* X% p8 U! q; N$ U0 G/ H$ R
  41.   // 初始化钱包,默认wallet是开启状态
    9 ]! J, R  p1 I$ i' |' g
  42.         if !config.Wallet.Disable {5 }4 f" ?' [6 c, [4 g# f, c4 s
  43.                 walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())6 I, i* ?( x4 x, v$ }+ l' j
  44.                 accounts = account.NewManager(walletDB, chain)
    0 p% O: r  P3 |
  45.                 assets = asset.NewRegistry(walletDB, chain)7 l  s" x' D7 W; Q7 y$ {- O
  46.                 wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain)% f; H6 C1 ]8 W7 q
  47.                 if err != nil {
    4 Q( n3 w$ p" t& n: v
  48.                         log.WithField("error", err).Error("init NewWallet")
    - S* Q) R6 h: L/ X. R4 }
  49.                 }
    8 D) I9 y: T; ^5 X
  50.                 // Clean up expired UTXO reservations periodically.5 y. {- n. ^* k' i, H7 G
  51.                 go accounts.ExpireReservations(ctx, expireReservationsPeriod)
    8 D7 g' g( P  w& ]2 k4 h* y
  52.         }
    2 B4 h7 N  ?8 t  f, `
  53.         newBlockCh := make(chan *bc.Hash, maxNewBlockChSize)
    . e* v1 N* R% B
  54.   // 初始化网络节点同步管理* a% p5 ^$ o+ V. K8 K' w
  55.         syncManager, _ := netsync.NewSyncManager(config, chain, txPool, newBlockCh)0 B2 B3 ^/ v0 q
  56.   // 初始化pprof,pprof用于输出性能指标,需要制定--prof_laddr参数来开启,在文章开头我们已经开启该功能
      k9 R- H# ~' z- J0 O  q
  57.         // run the profile server
    ) I' J# _/ m4 r! N, j  j/ z  l' z
  58.         profileHost := config.ProfListenAddress9 r. ~5 q+ |% v& S9 b3 d" i+ ?
  59.         if profileHost != "" {
    6 d0 i0 v& }, L/ f  [$ T1 p
  60.                 // Profiling bytomd programs.see (<a href="https://blog.golang.org/profiling-go-programs" target="_blank">https://blog.golang.org/profiling-go-programs</a>)
    0 e, y  ]# ^0 F/ D. ^% b
  61.                 // go tool pprof http://profileHose/debug/pprof/heap
    4 ]  Q1 T, q* Y" s1 g  i8 z% d
  62.                 go func() {
    % I) ?( M. s  J
  63.                         http.ListenAndServe(profileHost, nil)
    ' v5 h& `( k: F9 |0 w
  64.                 }()
    ! N& ]: J1 j  I5 B4 i7 v  X
  65.         }
    6 E6 P# V7 K4 l# b
  66.   // 初始化节点,填充节点所需的所有参数环境
    1 o' H) ]& H. a
  67.         node := &Node{
    / a1 f# v2 T; k5 ~( F, f8 K
  68.                 config:       config,# ]3 E) Y  e8 F) |. j
  69.                 syncManager:  syncManager,
    % V* T: i, p6 D% t1 \0 ?8 A: F3 a/ \
  70.                 evsw:         eventSwitch,
    7 X8 n- C0 k% s3 Y
  71.                 accessTokens: accessTokens,6 s! t) b7 h( E
  72.                 wallet:       wallet,# A; z" z$ s' ?. U# b
  73.                 chain:        chain," v# ~: ^1 s$ N7 k
  74.                 txfeed:       txFeed,
    & o9 \4 F0 h& y# D
  75.                 miningEnable: config.Mining,6 [. ]) y& A( T* ]- c# u) U
  76.         }
    ! L) X/ G9 p* s4 k: z
  77.   // 初始化挖矿3 o# C, i7 D: R/ [& n+ M
  78.         node.cpuMiner = cpuminer.NewCPUMiner(chain, accounts, txPool, newBlockCh)
    0 N9 }+ ~# J0 g+ d) Q, b5 j* l) F$ e
  79.         node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, newBlockCh)
    6 V+ W4 y7 ]- M% _1 `
  80.         node.BaseService = *cmn.NewBaseService(nil, "Node", node)
    ) U& d8 T2 T9 ~. V+ c
  81.         return node4 W9 \. z; B0 r/ r, W8 ]
  82. }
复制代码

1 f9 ]- k  ]9 H- B6 @" W# y. d, c目前bytomd只支持cpu挖矿,所以在代码中只有cpuminer的初始化信息
4 T* }4 @+ F0 }1 _2 `  O, n启动node3 @% _# H; f+ q$ M/ V
  1. ** node/node.go **
    , g4 g' e4 }% i) q3 c$ h8 B' d
  2. // Lanch web broser or not' [% L. F! Y6 O0 R1 J8 j
  3. func lanchWebBroser() {0 |  P) a2 ?% M$ K0 e6 _- F2 Y
  4.         log.Info("Launching System Browser with :", webAddress)
    / u. B9 ~& p4 _3 t+ L
  5.         if err := browser.Open(webAddress); err != nil {
    , ]( K3 C+ O3 X& J8 d' ?
  6.                 log.Error(err.Error())- ^7 {8 {, m. ?9 o2 i
  7.                 return+ L- V' a# ?. O! C8 W
  8.         }
    % [- _1 @$ j' a/ G9 j; f
  9. }3 }3 Q; o: q; s: t: O
  10. func (n *Node) initAndstartApiServer() {0 `/ r0 v/ Y# \4 q) o' H
  11.         n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)* r6 {2 e7 [5 c, _# g5 B$ o" P
  12.         listenAddr := env.String("LISTEN", n.config.ApiAddress)
    5 l2 ~0 x- F2 c+ y7 o5 m, k
  13.         env.Parse()- p* B, S" ]$ B- S3 B
  14.         n.api.StartServer(*listenAddr)
    : u+ S7 ^* o. C, v, |; |1 H" Q0 r
  15. }5 w7 J$ {% Y; @  m
  16. func (n *Node) OnStart() error {- g" u: }* `3 ?4 K& d( C) f) R2 O
  17.         if n.miningEnable {" d' F% g( @( k1 \* @% r* K" }
  18.                 n.cpuMiner.Start(), o/ ]6 y+ u/ n% h
  19.         }! }' ~3 \8 V% Q
  20.         n.syncManager.Start()
    ) T  o* j, q2 G$ O6 r! ^
  21.         n.initAndstartApiServer()
    7 `( {- _3 O- Z
  22.         if !n.config.Web.Closed {
    + G- B0 D# J( Z- R
  23.                 lanchWebBroser()3 f& U  X+ A) G: r1 k( i+ m8 N6 ?
  24.         }; e. c) E% p+ M$ C- Q7 {
  25.         return nil
      E5 I+ V8 @( Y, f. _* z
  26. }
复制代码
/ ^8 r8 H5 A3 K) F4 R9 P
OnStart() 启动node进程如下:, k7 J- _! T; `
启动挖矿功能启动p2p网络同步启动http协议的apiserver服务打开浏览器访问bytond的交易页面
+ v* D+ ?4 [0 D
7 `  Z0 t0 R. b! ^+ v
停止node
+ `9 m8 U* n) L2 _bytomd在启动时执行了n.RunForever()函数,该函数是由tendermint框架启动了监听信号的功能:
8 s; m, R5 C! u0 j/ i2 q1 D' P
  1. ** vendor/github.com/tendermint/tmlibs/common/os.go **: e3 t, ~% ]. P0 A5 ]3 _$ Y
  2. func TrapSignal(cb func()) {, ?& n0 a! F$ a. N6 U
  3.         c := make(chan os.Signal, 1)! T$ N9 L* w/ Q+ v
  4.         signal.Notify(c, os.Interrupt, syscall.SIGTERM)% C7 y% u5 V4 r& c3 P6 D- y$ {
  5.         go func() {, `! O; p( ]9 W# x
  6.                 for sig := range c {  s6 t5 y* e$ o8 y, d' A
  7.                         fmt.Printf("captured %v, exiting...\n", sig); n$ |% K. g  ]; A
  8.                         if cb != nil {# `  E1 h( T" M" D: B9 ^+ x
  9.                                 cb()  T& z. S: a* I7 T. x8 M
  10.                         }! x, v! ]; g% w5 h$ i
  11.                         os.Exit(1)
    : @% G5 G7 `! i3 h" B
  12.                 }
    3 X. g( K/ T2 O2 Q
  13.         }()+ h2 I2 j+ y- j3 r
  14.         select {}6 q+ S: }6 s. b' X
  15. }
复制代码
/ @- C' P7 @$ W6 P% r
TrapSignal函数监听了SIGTERM信号,bytomd才能成为不退出的守护进程。只有当触发了ctrl+c或kill bytomd_pid才能终止bytomd进程退出。退出时bytomd执行如下操作; L% a) O5 r, J4 f
  1. ** node/node.go *** M* ^. d3 a& }* p4 o! t  Y% t
  2. func (n *Node) OnStop() {# V1 R& F, R: U5 y% j
  3.         n.BaseService.OnStop()8 b0 j+ h1 o& e9 T
  4.         if n.miningEnable {& ~  Z2 O) ~' ^3 L! Z* ]
  5.                 n.cpuMiner.Stop()
    + ^2 l0 S" Q7 p( ]% C
  6.         }
    * `$ g: Y, b- z( |3 z0 n  U
  7.         n.syncManager.Stop()5 W6 v$ a3 E3 M
  8.         log.Info("Stopping Node")
    : }, H" ~$ V9 P8 n
  9.         // TODO: gracefully disconnect from peers.
    , S- ~2 m! O# r+ T( J
  10. }
复制代码
1 f. R& k- q8 V% G* y1 h% R8 P
bytomd会将挖矿功能停止,p2p网络停止等操作。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    2

  • 主题

    4