github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/dashboard/dashboard.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 //版权所有2017 Go Ethereum作者 10 //此文件是Go以太坊库的一部分。 11 // 12 //Go-Ethereum库是免费软件:您可以重新分发它和/或修改 13 //根据GNU发布的较低通用公共许可证的条款 14 //自由软件基金会,或者许可证的第3版,或者 15 //(由您选择)任何更高版本。 16 // 17 //Go以太坊图书馆的发行目的是希望它会有用, 18 //但没有任何保证;甚至没有 19 //适销性或特定用途的适用性。见 20 //GNU较低的通用公共许可证,了解更多详细信息。 21 // 22 //你应该收到一份GNU较低级别的公共许可证副本 23 //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。 24 25 package dashboard 26 27 //执行:生成纱线--cwd./资产安装 28 //go:生成纱线——cwd./资产构建 29 //go:生成go bindata-nometadata-o assets.go-prefix assets-nocompress-pkg dashboard assets/index.html assets/bundle.js 30 //go:generate sh-c“sed's var bundlejs//nolint:misspell\\n&assets.go>assets.go.tmp&&mv assets.go.tmp assets.go” 31 //go:generate sh-c“sed's var indexhtml//nolint:misspell\\n&assets.go>assets.go.tmp&&mv assets.go.tmp assets.go” 32 //go:生成gofmt-w-s资产。 33 34 import ( 35 "fmt" 36 "net" 37 "net/http" 38 "runtime" 39 "sync" 40 "sync/atomic" 41 "time" 42 43 "io" 44 45 "github.com/elastic/gosigar" 46 "github.com/ethereum/go-ethereum/log" 47 "github.com/ethereum/go-ethereum/metrics" 48 "github.com/ethereum/go-ethereum/p2p" 49 "github.com/ethereum/go-ethereum/params" 50 "github.com/ethereum/go-ethereum/rpc" 51 "github.com/mohae/deepcopy" 52 "golang.org/x/net/websocket" 53 ) 54 55 const ( 56 activeMemorySampleLimit = 200 //活动内存数据样本的最大数目 57 virtualMemorySampleLimit = 200 //虚拟内存数据示例的最大数目 58 networkIngressSampleLimit = 200 //最大网络入口数据样本数 59 networkEgressSampleLimit = 200 //网络出口数据样本的最大数目 60 processCPUSampleLimit = 200 //最大进程CPU数据样本数 61 systemCPUSampleLimit = 200 //系统CPU数据样本的最大数目 62 diskReadSampleLimit = 200 //磁盘读取数据样本的最大数目 63 diskWriteSampleLimit = 200 //磁盘写入数据示例的最大数目 64 ) 65 66 var nextID uint32 //下一个连接ID 67 68 //仪表板包含仪表板内部。 69 type Dashboard struct { 70 config *Config 71 72 listener net.Listener 73 conns map[uint32]*client //当前活动的WebSocket连接 74 history *Message 75 lock sync.RWMutex //锁定保护仪表板内部 76 77 logdir string 78 79 quit chan chan error //用于优美出口的通道 80 wg sync.WaitGroup 81 } 82 83 //客户端表示与远程浏览器的活动WebSocket连接。 84 type client struct { 85 conn *websocket.Conn //特定的Live WebSocket连接 86 msg chan *Message //更新消息的消息队列 87 logger log.Logger //特定活动WebSocket连接的记录器 88 } 89 90 //新建创建具有给定配置的新仪表板实例。 91 func New(config *Config, commit string, logdir string) *Dashboard { 92 now := time.Now() 93 versionMeta := "" 94 if len(params.VersionMeta) > 0 { 95 versionMeta = fmt.Sprintf(" (%s)", params.VersionMeta) 96 } 97 return &Dashboard{ 98 conns: make(map[uint32]*client), 99 config: config, 100 quit: make(chan chan error), 101 history: &Message{ 102 General: &GeneralMessage{ 103 Commit: commit, 104 Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta), 105 }, 106 System: &SystemMessage{ 107 ActiveMemory: emptyChartEntries(now, activeMemorySampleLimit, config.Refresh), 108 VirtualMemory: emptyChartEntries(now, virtualMemorySampleLimit, config.Refresh), 109 NetworkIngress: emptyChartEntries(now, networkIngressSampleLimit, config.Refresh), 110 NetworkEgress: emptyChartEntries(now, networkEgressSampleLimit, config.Refresh), 111 ProcessCPU: emptyChartEntries(now, processCPUSampleLimit, config.Refresh), 112 SystemCPU: emptyChartEntries(now, systemCPUSampleLimit, config.Refresh), 113 DiskRead: emptyChartEntries(now, diskReadSampleLimit, config.Refresh), 114 DiskWrite: emptyChartEntries(now, diskWriteSampleLimit, config.Refresh), 115 }, 116 }, 117 logdir: logdir, 118 } 119 } 120 121 //EmptyChartEntries返回一个包含空样本数量限制的ChartEntry数组。 122 func emptyChartEntries(t time.Time, limit int, refresh time.Duration) ChartEntries { 123 ce := make(ChartEntries, limit) 124 for i := 0; i < limit; i++ { 125 ce[i] = &ChartEntry{ 126 Time: t.Add(-time.Duration(i) * refresh), 127 } 128 } 129 return ce 130 } 131 132 //协议实现了node.service接口。 133 func (db *Dashboard) Protocols() []p2p.Protocol { return nil } 134 135 //API实现了node.service接口。 136 func (db *Dashboard) APIs() []rpc.API { return nil } 137 138 //Start启动数据收集线程和仪表板的侦听服务器。 139 //实现node.service接口。 140 func (db *Dashboard) Start(server *p2p.Server) error { 141 log.Info("Starting dashboard") 142 143 db.wg.Add(2) 144 go db.collectData() 145 go db.streamLogs() 146 147 http.HandleFunc("/", db.webHandler) 148 http.Handle("/api", websocket.Handler(db.apiHandler)) 149 150 listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", db.config.Host, db.config.Port)) 151 if err != nil { 152 return err 153 } 154 db.listener = listener 155 156 go http.Serve(listener, nil) 157 158 return nil 159 } 160 161 //stop停止数据收集线程和仪表板的连接侦听器。 162 //实现node.service接口。 163 func (db *Dashboard) Stop() error { 164 //关闭连接侦听器。 165 var errs []error 166 if err := db.listener.Close(); err != nil { 167 errs = append(errs, err) 168 } 169 //关闭收集器。 170 errc := make(chan error, 1) 171 for i := 0; i < 2; i++ { 172 db.quit <- errc 173 if err := <-errc; err != nil { 174 errs = append(errs, err) 175 } 176 } 177 //关闭连接。 178 db.lock.Lock() 179 for _, c := range db.conns { 180 if err := c.conn.Close(); err != nil { 181 c.logger.Warn("Failed to close connection", "err", err) 182 } 183 } 184 db.lock.Unlock() 185 186 //等到每一条血路都结束。 187 db.wg.Wait() 188 log.Info("Dashboard stopped") 189 190 var err error 191 if len(errs) > 0 { 192 err = fmt.Errorf("%v", errs) 193 } 194 195 return err 196 } 197 198 //WebHandler处理所有非API请求,只需扁平化并返回仪表板网站。 199 func (db *Dashboard) webHandler(w http.ResponseWriter, r *http.Request) { 200 log.Debug("Request", "URL", r.URL) 201 202 path := r.URL.String() 203 if path == "/" { 204 path = "/index.html" 205 } 206 blob, err := Asset(path[1:]) 207 if err != nil { 208 log.Warn("Failed to load the asset", "path", path, "err", err) 209 http.Error(w, "not found", http.StatusNotFound) 210 return 211 } 212 w.Write(blob) 213 } 214 215 //apiHandler处理仪表板的请求。 216 func (db *Dashboard) apiHandler(conn *websocket.Conn) { 217 id := atomic.AddUint32(&nextID, 1) 218 client := &client{ 219 conn: conn, 220 msg: make(chan *Message, 128), 221 logger: log.New("id", id), 222 } 223 done := make(chan struct{}) 224 225 //开始侦听要发送的消息。 226 db.wg.Add(1) 227 go func() { 228 defer db.wg.Done() 229 230 for { 231 select { 232 case <-done: 233 return 234 case msg := <-client.msg: 235 if err := websocket.JSON.Send(client.conn, msg); err != nil { 236 client.logger.Warn("Failed to send the message", "msg", msg, "err", err) 237 client.conn.Close() 238 return 239 } 240 } 241 } 242 }() 243 244 db.lock.Lock() 245 //发送过去的数据。 246 client.msg <- deepcopy.Copy(db.history).(*Message) 247 //开始跟踪连接并在连接丢失时丢弃。 248 db.conns[id] = client 249 db.lock.Unlock() 250 defer func() { 251 db.lock.Lock() 252 delete(db.conns, id) 253 db.lock.Unlock() 254 }() 255 for { 256 r := new(Request) 257 if err := websocket.JSON.Receive(conn, r); err != nil { 258 if err != io.EOF { 259 client.logger.Warn("Failed to receive request", "err", err) 260 } 261 close(done) 262 return 263 } 264 if r.Logs != nil { 265 db.handleLogRequest(r.Logs, client) 266 } 267 } 268 } 269 270 //MeterCollector返回一个函数,该函数检索特定的仪表。 271 func meterCollector(name string) func() int64 { 272 if metric := metrics.DefaultRegistry.Get(name); metric != nil { 273 m := metric.(metrics.Meter) 274 return func() int64 { 275 return m.Count() 276 } 277 } 278 return func() int64 { 279 return 0 280 } 281 } 282 283 //CollectData收集仪表板上绘图所需的数据。 284 func (db *Dashboard) collectData() { 285 defer db.wg.Done() 286 287 systemCPUUsage := gosigar.Cpu{} 288 systemCPUUsage.Get() 289 var ( 290 mem runtime.MemStats 291 292 collectNetworkIngress = meterCollector("p2p/InboundTraffic") 293 collectNetworkEgress = meterCollector("p2p/OutboundTraffic") 294 collectDiskRead = meterCollector("eth/db/chaindata/disk/read") 295 collectDiskWrite = meterCollector("eth/db/chaindata/disk/write") 296 297 prevNetworkIngress = collectNetworkIngress() 298 prevNetworkEgress = collectNetworkEgress() 299 prevProcessCPUTime = getProcessCPUTime() 300 prevSystemCPUUsage = systemCPUUsage 301 prevDiskRead = collectDiskRead() 302 prevDiskWrite = collectDiskWrite() 303 304 frequency = float64(db.config.Refresh / time.Second) 305 numCPU = float64(runtime.NumCPU()) 306 ) 307 308 for { 309 select { 310 case errc := <-db.quit: 311 errc <- nil 312 return 313 case <-time.After(db.config.Refresh): 314 systemCPUUsage.Get() 315 var ( 316 curNetworkIngress = collectNetworkIngress() 317 curNetworkEgress = collectNetworkEgress() 318 curProcessCPUTime = getProcessCPUTime() 319 curSystemCPUUsage = systemCPUUsage 320 curDiskRead = collectDiskRead() 321 curDiskWrite = collectDiskWrite() 322 323 deltaNetworkIngress = float64(curNetworkIngress - prevNetworkIngress) 324 deltaNetworkEgress = float64(curNetworkEgress - prevNetworkEgress) 325 deltaProcessCPUTime = curProcessCPUTime - prevProcessCPUTime 326 deltaSystemCPUUsage = curSystemCPUUsage.Delta(prevSystemCPUUsage) 327 deltaDiskRead = curDiskRead - prevDiskRead 328 deltaDiskWrite = curDiskWrite - prevDiskWrite 329 ) 330 prevNetworkIngress = curNetworkIngress 331 prevNetworkEgress = curNetworkEgress 332 prevProcessCPUTime = curProcessCPUTime 333 prevSystemCPUUsage = curSystemCPUUsage 334 prevDiskRead = curDiskRead 335 prevDiskWrite = curDiskWrite 336 337 now := time.Now() 338 339 runtime.ReadMemStats(&mem) 340 activeMemory := &ChartEntry{ 341 Time: now, 342 Value: float64(mem.Alloc) / frequency, 343 } 344 virtualMemory := &ChartEntry{ 345 Time: now, 346 Value: float64(mem.Sys) / frequency, 347 } 348 networkIngress := &ChartEntry{ 349 Time: now, 350 Value: deltaNetworkIngress / frequency, 351 } 352 networkEgress := &ChartEntry{ 353 Time: now, 354 Value: deltaNetworkEgress / frequency, 355 } 356 processCPU := &ChartEntry{ 357 Time: now, 358 Value: deltaProcessCPUTime / frequency / numCPU * 100, 359 } 360 systemCPU := &ChartEntry{ 361 Time: now, 362 Value: float64(deltaSystemCPUUsage.Sys+deltaSystemCPUUsage.User) / frequency / numCPU, 363 } 364 diskRead := &ChartEntry{ 365 Time: now, 366 Value: float64(deltaDiskRead) / frequency, 367 } 368 diskWrite := &ChartEntry{ 369 Time: now, 370 Value: float64(deltaDiskWrite) / frequency, 371 } 372 sys := db.history.System 373 db.lock.Lock() 374 sys.ActiveMemory = append(sys.ActiveMemory[1:], activeMemory) 375 sys.VirtualMemory = append(sys.VirtualMemory[1:], virtualMemory) 376 sys.NetworkIngress = append(sys.NetworkIngress[1:], networkIngress) 377 sys.NetworkEgress = append(sys.NetworkEgress[1:], networkEgress) 378 sys.ProcessCPU = append(sys.ProcessCPU[1:], processCPU) 379 sys.SystemCPU = append(sys.SystemCPU[1:], systemCPU) 380 sys.DiskRead = append(sys.DiskRead[1:], diskRead) 381 sys.DiskWrite = append(sys.DiskWrite[1:], diskWrite) 382 db.lock.Unlock() 383 384 db.sendToAll(&Message{ 385 System: &SystemMessage{ 386 ActiveMemory: ChartEntries{activeMemory}, 387 VirtualMemory: ChartEntries{virtualMemory}, 388 NetworkIngress: ChartEntries{networkIngress}, 389 NetworkEgress: ChartEntries{networkEgress}, 390 ProcessCPU: ChartEntries{processCPU}, 391 SystemCPU: ChartEntries{systemCPU}, 392 DiskRead: ChartEntries{diskRead}, 393 DiskWrite: ChartEntries{diskWrite}, 394 }, 395 }) 396 } 397 } 398 } 399 400 //sendToAll将给定消息发送到活动仪表板。 401 func (db *Dashboard) sendToAll(msg *Message) { 402 db.lock.Lock() 403 for _, c := range db.conns { 404 select { 405 case c.msg <- msg: 406 default: 407 c.conn.Close() 408 } 409 } 410 db.lock.Unlock() 411 }