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  }