Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

bytom源码分析-启动与停止

蔡健雅的高跟鞋
129 0 0
作者使用MacOS操作系统,其他平台也大同小异  H, D) D7 _$ D( i9 R  ^* |: c" b
Golang Version: 1.8* l$ g$ |2 ?3 {- X6 `

/ I. a4 Z3 M& @. k预备工作
4 [! V! B6 ?7 J+ n0 |* u编译安装
3 z- Q  T2 M# u" `详细步骤见官方 bytom install5 p2 A; \4 _, a& L8 L$ W+ p2 q
设置debug日志输出
9 {' M$ c( W; G1 \开启debug输出文件、函数、行号等详细信息  Y; e( F) w/ L1 b
  1. export BYTOM_DEBUG=debug
复制代码
: c0 H7 [4 A; T6 A, ^
初始化并启动bytomd
7 V6 C$ t7 X# F: q. J5 h初始化
6 A# ]8 @9 C- r! H2 ]" A% j& f: Y; I
  1. ./bytomd init --chain_id testnet
复制代码

4 K+ X: [- t% P! w7 [bytomd目前支持两种网络,这里我们使用测试网
& V& y1 i) e) e- J# `mainnet:主网3 d! O& A6 x6 e- O: p! H
testnet:测试网
; Q1 e  n$ W1 _; W& v/ }; v启动bytomd, l9 [2 C# a- I. Z
  1. ./bytomd node --mining --prof_laddr=":8011"
    ' w8 @8 y/ A; J6 m: ~( |( q
  2. –prof_laddr=":8080"  // 开启pprof输出性能指标
复制代码
( S% [* {  {5 s7 L3 J8 T/ ~) r2 j
访问:http://127.0.0.1:8080/debug/pprof/2 x% {2 {1 g9 \4 @& r
bytomd init初始化
- {# D* r8 Z/ F; ]# P- L6 H* ]入口函数
$ p+ Z2 j3 B+ }7 Z4 Q
  1. ** cmd/bytomd/main.go **- @# I' S; {6 V: B
  2. func init() {9 o9 y: h+ z$ P# f. n
  3.         log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableColors: true})( k/ o3 [5 a/ L
  4.         // If environment variable BYTOM_DEBUG is not empty,$ Q1 t3 i/ b) c3 K* g) W5 K3 J& ^
  5.         // then add the hook to logrus and set the log level to DEBUG7 K4 x7 l0 E7 ~
  6.         if os.Getenv("BYTOM_DEBUG") != "" {% _7 [. R. c2 u% B3 {- d7 `- C
  7.                 log.AddHook(ContextHook{})" k, B2 `, y- @7 F: E4 O) u' I: x
  8.                 log.SetLevel(log.DebugLevel)
    % v: W( e' t$ j9 ^. J4 T
  9.         }
    3 l' v; g8 m2 e" Y0 I9 r% h! G
  10. }% E7 o7 \  ^* k0 v
  11. func main() {
    7 z& Y. x$ y3 @9 t$ X
  12.         cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv(config.DefaultDataDir()))
    ) _7 V% A  g- j. N; T+ y, \6 @
  13.         cmd.Execute()4 v4 c8 w( I: L, r
  14. }
复制代码

, R9 A1 m  O& Q: \! h; sinit函数会在main执行之前做初始化操作,可以看到init中bytomd加载BYTOM_DEBUG变量来设置debug日志输出6 Y( ^6 k1 L% n0 ^) K& V8 S
command cli传参初始化
5 z2 }( L7 S6 S$ q7 _bytomd的cli解析使用cobra库
0 e. q( {6 N( \2 A3 b% ~** cmd/bytomd/commands **
9 G  D/ o4 y, a" Z/ g+ k! ycmd/bytomd/commands/root.go5 a5 a  j: E( [
初始化–root传参。bytomd存储配置、keystore、数据的root目录。在MacOS下,默认路径是~/Library/Bytom/cmd/bytomd/commands/init.go
  s9 V% q: W5 [9 T& [# b; }3 x初始化–chain_id传参。选择网络类型,在启动bytomd时我们选择了testnet也就是测试网络cmd/bytomd/commands/version.go7 \) n9 J1 [9 x9 Y6 j1 {% P# h
初始化version传参
cmd/bytomd/commands/run_node.go  _2 n3 K# t$ L  v
初始化node节点运行时所需要的传参( O/ V( c0 j$ q; }8 t$ \

5 Q) @2 r7 H0 }7 e初始化默认配置  ~( j# e& F# F1 ~+ s: y" {0 S
用户传参只有一部分参数,那节点所需的其他参数需要从默认配置中加载。
3 r, m9 N9 k8 P6 Y) ?# {9 u! Z% a
  1. ** cmd/bytomd/commands/root.go **/ V& u% r& y5 I/ K3 X# a
  2. var (
    , E8 o8 c% J+ b  f6 f: T
  3.         config = cfg.DefaultConfig()
    + p( W( p/ H9 Z9 h
  4. )
    : R+ W5 G( J. r! b# K
  5. 在root.go中有一个config全局变量加载了node所需的所有默认参数% y4 t. k/ b5 N) J3 i/ F
  6. // Default configurable parameters.
    % i7 ?- v+ W& |7 H1 @! e
  7. func DefaultConfig() *Config {6 I' \# Y  a- }  E5 e5 P
  8.         return &Config{: Q/ j* I/ h9 r3 w2 y+ k" T
  9.                 BaseConfig: DefaultBaseConfig(),  // node基础相关配置
    0 E& l: l6 e- F
  10.                 P2P:        DefaultP2PConfig(),   // p2p网络相关配置
    2 i# N6 j7 b$ `& i
  11.                 Wallet:     DefaultWalletConfig(),   // 钱包相关配置5 W' C. i7 X7 n+ ^1 B' `( }
  12.                 Auth:       DefaultRPCAuthConfig(),  // 验证相关配置! b3 g0 I0 r5 `6 e
  13.                 Web:        DefaultWebConfig(),   // web相关配置+ n: r" Y" J- ^3 |
  14.         }
    7 w, ~5 y2 u/ W! w6 ~5 u( B# _+ |
  15. }
复制代码

; v! W& ?% r4 D$ O6 u# u3 v后面的文章会一一介绍每个配置的作用/ j& e7 D- E# E. @
bytomd 守护进程启动与退出* L9 u* h3 ~: I9 {: G% [, q
  1. ** cmd/bytomd/commands/run_node.go **
    " F5 p. L$ d: z2 S* c0 N) T' ?
  2. func runNode(cmd *cobra.Command, args []string) error {5 y6 t2 \4 N, D6 V! v  C
  3.         // Create & start node) k- |( }! g6 M/ R- K6 u; r, D. k1 {
  4.         n := node.NewNode(config)  E- u3 Y7 Y) N! ^2 B& d
  5.         if _, err := n.Start(); err != nil {
    ( |4 b8 |0 W* w* o- G0 d" u+ n) p9 w
  6.                 return fmt.Errorf("Failed to start node: %v", err)
    : m& {4 J  ~: B  g! z4 {* n- p& J
  7.         } else {% k4 H. e. c& Z  }8 G
  8.                 log.WithField("nodeInfo", n.SyncManager().Switch().NodeInfo()).Info("Started node")
    0 ~2 c# B) M5 y' J
  9.         }
    ( `  u9 z0 Z* v+ C
  10.         // Trap signal, run forever.
    5 F9 f4 b& E, W: [2 b
  11.         n.RunForever()
    0 \0 u/ R3 `% J: R6 P$ T
  12.         return nil% ~2 ^, b( t2 R) \5 H1 n7 L: F, z* D
  13. }
复制代码
. @" T4 i! `. a, b: h- O
runNode函数有三步操作:0 ~8 L4 T& P$ {0 s2 @  I
node.NewNode:初始化node运行环境+ q& l& k$ d7 m! ~
n.Start:启动node
5 f# }5 P6 J( U* r( bn.RunForever:监听退出信号,收到ctrl+c操作则退出node。在linux中守进程一般监听SIGTERM信号(ctrl+c)作为退出守护进程的信号! c  |* U) u/ M1 c
初始化node运行环境
3 U# W* Q7 K3 u9 p3 `6 e1 _在bytomd中有五个db数据库存储在–root参数下的data目录
0 i' Y: D8 |8 R" S: [; H- Naccesstoken.db    // 存储token相关信息(钱包访问控制权限)trusthistory.db   // 存储p2p网络同步相关信息txdb.db           // 存储交易相关信息txfeeds.db        //wallet.db         // 存储钱包相关信息1 y& h& \  S( H6 ]

  1. 4 I: s9 L, P* m8 m$ c# G' t
  2. ** node/node.go **4 D. B$ X# }) z3 `8 U" D- k4 ^
  3. func NewNode(config *cfg.Config) *Node {
    ( m$ F3 B; G0 n
  4.         ctx := context.Background()
    - A6 R& e$ s7 u* L1 l! X4 Z
  5.         initActiveNetParams(config)+ y" `/ A( F; d, R( C# {
  6.         // Get store 初始化txdb数据库
    / j5 b( x6 k' C# q7 ^
  7.         txDB := dbm.NewDB("txdb", config.DBBackend, config.DBDir()): T. `. g6 q5 g% d, Q
  8.         store := leveldb.NewStore(txDB)) k+ Q0 Z7 B) c- z3 n7 A
  9.   // 初始化accesstoken数据库4 `) l4 \) {1 H, o: h1 L. E: S
  10.         tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir())0 p( `$ X$ d& g# |: w( P
  11.         accessTokens := accesstoken.NewStore(tokenDB)% T8 \+ c( S, {* W% w5 u
  12.   // 初始化event事件调度器,也叫任务调度器。一个任务可以被多次调用
    7 \  K, t9 B8 H% y  V
  13.         // Make event switch
    7 e4 U7 W% j9 n) U+ P
  14.         eventSwitch := types.NewEventSwitch()( t: O# G6 t" a( P- I( T8 j
  15.         _, err := eventSwitch.Start()
    6 R, O6 o  H0 s9 y$ J8 s
  16.         if err != nil {; E" X' i0 p* P6 y2 E
  17.                 cmn.Exit(cmn.Fmt("Failed to start switch: %v", err))+ k0 P# c3 \1 O, q. f
  18.         }
    7 v$ q# S3 c0 T$ X0 v1 h, j
  19.   // 初始化交易池$ b! D, V, L4 U+ K2 f3 l9 v4 k
  20.         txPool := protocol.NewTxPool()# d) i0 e! E1 P. R- s$ D9 ]
  21.         chain, err := protocol.NewChain(store, txPool)
    0 C% {9 G7 o( V
  22.         if err != nil {
    % w$ {, j1 }" s8 q; U: I& q0 i& y
  23.                 cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))
    " T! L. e0 r+ T# A/ q
  24.         }# {9 j- N2 w! b+ X( @# Q  j1 U
  25.         var accounts *account.Manager = nil
    . {9 T! r0 m9 r! K6 C7 }
  26.         var assets *asset.Registry = nil
    1 l4 H2 y" T, K! r" Q: i
  27.         var wallet *w.Wallet = nil* m5 K. q; f/ m, z
  28.         var txFeed *txfeed.Tracker = nil
    + q& |" E$ Q0 G$ }
  29.   // 初始化txfeeds数据库
    ) J' e3 I/ D$ z0 `
  30.         txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())! Q$ f: c( U. ^* g1 ]
  31.         txFeed = txfeed.NewTracker(txFeedDB, chain)
    - c8 C6 B! e. t0 r8 U6 `
  32.         if err = txFeed.Prepare(ctx); err != nil {) }/ M, q9 r( l5 c* F# b3 M  ~3 t$ M4 z
  33.                 log.WithField("error", err).Error("start txfeed")
    + p3 F9 ~& S- Q( E$ E" L
  34.                 return nil# |; K0 F5 q5 D7 W5 s) d
  35.         }
    $ i9 {' C2 Z& D' e
  36.   // 初始化keystore! U  w4 x% _% u
  37.         hsm, err := pseudohsm.New(config.KeysDir())* T& ~4 S" i; Y/ O
  38.         if err != nil {
    5 B& I( N% f: |) s/ s3 i5 f+ n
  39.                 cmn.Exit(cmn.Fmt("initialize HSM failed: %v", err))
    8 v" M9 D. U, J
  40.         }1 J% p, |$ H  }9 \
  41.   // 初始化钱包,默认wallet是开启状态
      P+ \) w+ L3 a# A" c( A7 W7 s
  42.         if !config.Wallet.Disable {, h4 A+ ^: f: ^* A
  43.                 walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())+ J' n2 x. l3 |2 Q2 u
  44.                 accounts = account.NewManager(walletDB, chain)- O1 }8 _6 z+ x' w$ C+ L1 c- J, S2 _
  45.                 assets = asset.NewRegistry(walletDB, chain)
    % ]( o( E7 Y" Q# r1 |
  46.                 wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain)& R8 n: {) A2 L$ y, D$ I
  47.                 if err != nil {  s: D; \1 ]. F# e0 @" G8 M: }
  48.                         log.WithField("error", err).Error("init NewWallet")) b/ a; q: Q' y: c0 R
  49.                 }; \4 c, `5 H8 X# o* ]+ }& l9 N: }) M
  50.                 // Clean up expired UTXO reservations periodically.
    - M+ D2 }4 o; m8 U7 y" k" l, ]! ]/ L
  51.                 go accounts.ExpireReservations(ctx, expireReservationsPeriod)7 ?" ?& G7 m( D# l8 e
  52.         }: x/ L) O" t; T& t  P: P/ ~: X
  53.         newBlockCh := make(chan *bc.Hash, maxNewBlockChSize). t' m9 k7 D( s& C$ R
  54.   // 初始化网络节点同步管理
    # o3 p/ H0 o* J7 M
  55.         syncManager, _ := netsync.NewSyncManager(config, chain, txPool, newBlockCh)
    4 b: B2 j6 Q  \! w) t6 [9 _' b2 D
  56.   // 初始化pprof,pprof用于输出性能指标,需要制定--prof_laddr参数来开启,在文章开头我们已经开启该功能
    ) Q& _% ?5 z3 k. j6 w4 m9 b
  57.         // run the profile server
    ' ^7 Q" x& f. T1 R  N9 Z
  58.         profileHost := config.ProfListenAddress$ J2 P# k& F" Z. ]; z) C4 U) ?
  59.         if profileHost != "" {2 l+ S" _' R. M  g
  60.                 // Profiling bytomd programs.see (<a href="https://blog.golang.org/profiling-go-programs" target="_blank">https://blog.golang.org/profiling-go-programs</a>)
    ; X% G( ~% ]. X, ]- K
  61.                 // go tool pprof http://profileHose/debug/pprof/heap0 `' i  a* l5 C* Q! f' q6 j
  62.                 go func() {" Q, H$ ?" u% z) X
  63.                         http.ListenAndServe(profileHost, nil)
    / q; u7 C! T) c
  64.                 }()/ f: ]2 n; p* T7 F$ \4 V
  65.         }' V  x; P! l# d4 m& l
  66.   // 初始化节点,填充节点所需的所有参数环境9 |$ |  j( J5 v4 ?; V  |- o
  67.         node := &Node{* O' j' a1 S3 r$ _& ^
  68.                 config:       config,5 Z3 s4 F) t8 z$ L8 S" R' ~3 V5 M
  69.                 syncManager:  syncManager," h/ ?: p! i/ v7 o& |2 {
  70.                 evsw:         eventSwitch,& z* y' V) a  l! R
  71.                 accessTokens: accessTokens,# P- {6 T, S  x
  72.                 wallet:       wallet,
    . i4 I1 `; s: ]+ K! V
  73.                 chain:        chain,
    , R8 ]' R1 b, y/ _5 n8 X8 |' U
  74.                 txfeed:       txFeed,
    ; |1 m  Z: `4 t  l, m9 z- Z4 n
  75.                 miningEnable: config.Mining,
    ; v3 A  X& q. Z$ ]) F  J: U1 m% Y
  76.         }2 a' o9 s7 k1 Y/ y$ i: Q% r7 s7 P
  77.   // 初始化挖矿) f& j4 R8 @) e3 k! X; N0 v7 m2 y
  78.         node.cpuMiner = cpuminer.NewCPUMiner(chain, accounts, txPool, newBlockCh)0 n. b0 ]% m7 _
  79.         node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, newBlockCh)6 `' m5 l9 ^/ w( E4 s! X- f
  80.         node.BaseService = *cmn.NewBaseService(nil, "Node", node); G" W7 S. Y: v  O5 s& W
  81.         return node& |+ u5 P: n: ~0 v
  82. }
复制代码
3 {- h5 E5 t8 V5 _. V7 v% H
目前bytomd只支持cpu挖矿,所以在代码中只有cpuminer的初始化信息
9 t% E2 S" w/ y启动node
& H* ^4 x4 `, S! a6 {) P0 t: q
  1. ** node/node.go **
    * e' I' S- I& n# j% {9 m$ i! J
  2. // Lanch web broser or not5 d) |4 ?& L$ S# p: T) c( s" q
  3. func lanchWebBroser() {
    * S& L! ]& I2 j
  4.         log.Info("Launching System Browser with :", webAddress)
    & U8 N1 r: a+ W' H# k: |7 ]! X, @4 c
  5.         if err := browser.Open(webAddress); err != nil {! K+ f) }& h8 M0 L6 C4 i: `
  6.                 log.Error(err.Error())
    4 p9 o+ |' o2 a1 c* @6 S
  7.                 return3 W8 z  |; W5 j* u
  8.         }3 v: c1 R% W4 P3 Z5 V" G+ [+ @3 o
  9. }
    / S( w; \5 N* @/ N# _- l# g( D
  10. func (n *Node) initAndstartApiServer() {
    / {; s7 R! j% V/ Y
  11.         n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)6 ^9 Y* P9 N. c; {/ r. ?2 s
  12.         listenAddr := env.String("LISTEN", n.config.ApiAddress)( M. F/ c* q; v) E7 w
  13.         env.Parse(): [7 T8 K1 W: C( g
  14.         n.api.StartServer(*listenAddr); u" Y9 K( ]3 b
  15. }/ `) g, T6 P! h$ ^! Z0 o. h
  16. func (n *Node) OnStart() error {
    . ?% l) f% N* n* ^! s
  17.         if n.miningEnable {
    ' ^) P6 i" I- ?" }
  18.                 n.cpuMiner.Start()$ ?" h% n+ K! P) e+ M; h/ O
  19.         }
    8 B+ X7 `, `1 W" y) Z2 I
  20.         n.syncManager.Start()7 I" w2 Q6 c2 E8 p% N5 u. r# e( n/ b9 Z
  21.         n.initAndstartApiServer()
    + i  `# I! O; u% w+ |0 A8 |- \
  22.         if !n.config.Web.Closed {
    " S6 c; m- |/ j& b9 B( i
  23.                 lanchWebBroser()
    0 F( E3 y5 h# J& _) H5 ]% Y4 v$ z
  24.         }% ~) z' p  L  ?7 g2 ?/ f+ C6 M# [+ m
  25.         return nil1 ~5 y) W/ l6 e9 Y6 k
  26. }
复制代码
4 ]. a( r1 b5 s. c
OnStart() 启动node进程如下:
( Z2 z0 s8 [( `9 I启动挖矿功能启动p2p网络同步启动http协议的apiserver服务打开浏览器访问bytond的交易页面
/ B- t$ X/ ~; ]
3 c6 `8 i9 _8 }: @$ v7 R3 [+ D; ~
停止node
# E- _2 P) n2 @: w+ x6 F! Nbytomd在启动时执行了n.RunForever()函数,该函数是由tendermint框架启动了监听信号的功能:3 W  o. \9 g: e; Q. b
  1. ** vendor/github.com/tendermint/tmlibs/common/os.go **9 B# e3 F, r" J* o1 r
  2. func TrapSignal(cb func()) {, k& ^/ q, x" m0 k  o
  3.         c := make(chan os.Signal, 1)
    ! S. S* _2 P6 o% q$ m2 D9 I, w' [) x
  4.         signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    " X# r5 ^2 `  d. a, N; U: Z
  5.         go func() {- l2 c% {3 ^/ p+ Y5 ]) t
  6.                 for sig := range c {
      n6 j2 o6 B! R+ Z
  7.                         fmt.Printf("captured %v, exiting...\n", sig)% c, y' _3 Q. e& t: o) X+ A9 q
  8.                         if cb != nil {
    + Z0 W$ Q  V' |5 s1 X+ J
  9.                                 cb(), E) n$ ]% E/ u; S9 V
  10.                         }: }9 f- I3 S6 E! K
  11.                         os.Exit(1)
    9 X& v! A* p+ c: \3 Z9 X
  12.                 }0 m' o* [  Q3 L) ?( K: _+ |
  13.         }(), M3 g9 e. O$ f
  14.         select {}& r2 d+ b7 ~" u$ m& _% D" H! o
  15. }
复制代码

0 g% P6 x- P  D) BTrapSignal函数监听了SIGTERM信号,bytomd才能成为不退出的守护进程。只有当触发了ctrl+c或kill bytomd_pid才能终止bytomd进程退出。退出时bytomd执行如下操作
% W4 a- n( J! B1 I8 l+ a
  1. ** node/node.go **' ~+ E% B# o5 B9 G% X7 a
  2. func (n *Node) OnStop() {
    * C& v" [' _" p3 C; z- ?  w
  3.         n.BaseService.OnStop()0 g2 q! @) s$ N: T! Q
  4.         if n.miningEnable {
    ( l1 [- L- F+ l$ Y, S4 k% {
  5.                 n.cpuMiner.Stop()
    " n2 A) F$ D% [
  6.         }5 Q! I, ]# R7 S- J
  7.         n.syncManager.Stop()
    / \: Y3 j2 ~7 g, w$ b/ G3 f; n- N
  8.         log.Info("Stopping Node")
    # Y. F; q3 b" f/ v
  9.         // TODO: gracefully disconnect from peers.0 c. J* E% U6 |% b7 w$ y3 I+ J0 v
  10. }
复制代码

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

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    2

  • 主题

    4