github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/protocols/reporter.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:41</date> 10 //</624450106082463744> 11 12 13 package protocols 14 15 import ( 16 "encoding/binary" 17 "time" 18 19 "github.com/ethereum/go-ethereum/log" 20 "github.com/ethereum/go-ethereum/metrics" 21 22 "github.com/syndtr/goleveldb/leveldb" 23 ) 24 25 //AccountMetrics提取了度量数据库,并 26 //报告者坚持标准 27 type AccountingMetrics struct { 28 reporter *reporter 29 } 30 31 //关闭节点时将调用Close。 32 //为了优雅的清理 33 func (am *AccountingMetrics) Close() { 34 close(am.reporter.quit) 35 am.reporter.db.Close() 36 } 37 38 //Reporter是一种内部结构,用于编写与P2P会计相关的 39 //指标达到了B级。它将定期向数据库写入应计指标。 40 type reporter struct { 41 reg metrics.Registry //这些度量的注册表(独立于其他度量) 42 interval time.Duration //报告者将保持度量的持续时间 43 db *leveldb.DB //实际数据库 44 quit chan struct{} //退出Reporter循环 45 } 46 47 //NewMetricsDB创建一个新的LevelDB实例,用于持久化定义的度量 48 //在p2p/protocols/accounting.go中 49 func NewAccountingMetrics(r metrics.Registry, d time.Duration, path string) *AccountingMetrics { 50 var val = make([]byte, 8) 51 var err error 52 53 //创建级别数据库 54 db, err := leveldb.OpenFile(path, nil) 55 if err != nil { 56 log.Error(err.Error()) 57 return nil 58 } 59 60 //检查数据库中是否存在值的所有已定义度量 61 //如果存在,请将其分配给度量。这意味着节点 62 //以前一直在运行,并且该度量值已被持久化。 63 metricsMap := map[string]metrics.Counter{ 64 "account.balance.credit": mBalanceCredit, 65 "account.balance.debit": mBalanceDebit, 66 "account.bytes.credit": mBytesCredit, 67 "account.bytes.debit": mBytesDebit, 68 "account.msg.credit": mMsgCredit, 69 "account.msg.debit": mMsgDebit, 70 "account.peerdrops": mPeerDrops, 71 "account.selfdrops": mSelfDrops, 72 } 73 //迭代映射并获取值 74 for key, metric := range metricsMap { 75 val, err = db.Get([]byte(key), nil) 76 //直到第一次写入值, 77 //这将返回一个错误。 78 //尽管以后记录错误是有益的, 79 //但这需要一个不同的逻辑 80 if err == nil { 81 metric.Inc(int64(binary.BigEndian.Uint64(val))) 82 } 83 } 84 85 //创建报告人 86 rep := &reporter{ 87 reg: r, 88 interval: d, 89 db: db, 90 quit: make(chan struct{}), 91 } 92 93 //执行执行例行程序 94 go rep.run() 95 96 m := &AccountingMetrics{ 97 reporter: rep, 98 } 99 100 return m 101 } 102 103 //运行是一个goroutine,它定期将度量发送到配置的级别db 104 func (r *reporter) run() { 105 intervalTicker := time.NewTicker(r.interval) 106 107 for { 108 select { 109 case <-intervalTicker.C: 110 //在每个勾选处发送指标 111 if err := r.save(); err != nil { 112 log.Error("unable to send metrics to LevelDB", "err", err) 113 //如果在写入过程中出现错误,请退出该例程;我们在此假定错误为 114 //严重,不要再尝试写入。 115 //此外,这应该可以防止节点停止时发生泄漏。 116 return 117 } 118 case <-r.quit: 119 //正常关机 120 return 121 } 122 } 123 } 124 125 //将指标发送到数据库 126 func (r *reporter) save() error { 127 //创建一个级别数据库批处理 128 batch := leveldb.Batch{} 129 //对于注册表中的每个指标(独立的) 130 r.reg.Each(func(name string, i interface{}) { 131 metric, ok := i.(metrics.Counter) 132 if ok { 133 //假设这里的每个度量都是一个计数器(单独的注册表) 134 //…创建快照… 135 ms := metric.Snapshot() 136 byteVal := make([]byte, 8) 137 binary.BigEndian.PutUint64(byteVal, uint64(ms.Count())) 138 //…并将值保存到数据库 139 batch.Put([]byte(name), byteVal) 140 } 141 }) 142 return r.db.Write(&batch, nil) 143 } 144