github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/les/serverpool.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:39</date>
    10  //</624450096175517696>
    11  
    12  
    13  //包les实现轻以太坊子协议。
    14  package les
    15  
    16  import (
    17  	"crypto/ecdsa"
    18  	"fmt"
    19  	"io"
    20  	"math"
    21  	"math/rand"
    22  	"net"
    23  	"strconv"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common/mclock"
    28  	"github.com/ethereum/go-ethereum/crypto"
    29  	"github.com/ethereum/go-ethereum/ethdb"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-ethereum/p2p"
    32  	"github.com/ethereum/go-ethereum/p2p/discv5"
    33  	"github.com/ethereum/go-ethereum/p2p/enode"
    34  	"github.com/ethereum/go-ethereum/rlp"
    35  )
    36  
    37  const (
    38  //连接结束或超时后,会有一段等待时间
    39  //才能再次选择连接。
    40  //等待时间=基本延迟*(1+随机(1))
    41  //基本延迟=在a之后的第一个shortretrycnt时间的shortretryplay
    42  //连接成功,在应用longretryplay之后
    43  	shortRetryCnt   = 5
    44  	shortRetryDelay = time.Second * 5
    45  	longRetryDelay  = time.Minute * 10
    46  //MaxNewEntries是新发现(从未连接)节点的最大数目。
    47  //如果达到了这个限度,那么最近发现的最少的一个就会被剔除。
    48  	maxNewEntries = 1000
    49  //MaxKnownEntries是已知(已连接)节点的最大数目。
    50  //如果达到了限制,则会丢弃最近连接的限制。
    51  //(与新条目不同的是,已知条目是持久的)
    52  	maxKnownEntries = 1000
    53  //同时连接的服务器的目标
    54  	targetServerCount = 5
    55  //从已知表中选择的服务器的目标
    56  //(如果有新的,我们留有试用的空间)
    57  	targetKnownSelect = 3
    58  //拨号超时后,考虑服务器不可用并调整统计信息
    59  	dialTimeout = time.Second * 30
    60  //TargetConntime是服务器之前的最小预期连接持续时间
    61  //无任何特定原因删除客户端
    62  	targetConnTime = time.Minute * 10
    63  //基于最新发现时间的新条目选择权重计算:
    64  //unity until discoverExpireStart, then exponential decay with discoverExpireConst
    65  	discoverExpireStart = time.Minute * 20
    66  	discoverExpireConst = time.Minute * 20
    67  //已知条目选择权重在
    68  //每次连接失败(成功后恢复)
    69  	failDropLn = 0.1
    70  //已知节点连接成功和质量统计数据具有长期平均值
    71  //以及一个以指数形式调整的短期值,其系数为
    72  //pstatrecentadjust与每个拨号/连接同时以指数方式返回
    73  //到时间常数pstatornetomantc的平均值
    74  	pstatReturnToMeanTC = time.Hour
    75  //节点地址选择权重在
    76  //每次连接失败(成功后恢复)
    77  	addrFailDropLn = math.Ln2
    78  //响应coretc和delayscoretc是
    79  //根据响应时间和块延迟时间计算选择机会
    80  	responseScoreTC = time.Millisecond * 100
    81  	delayScoreTC    = time.Second * 5
    82  	timeoutPow      = 10
    83  //initstatsweight用于初始化以前未知的具有良好
    84  //统计学给自己一个证明自己的机会
    85  	initStatsWeight = 1
    86  )
    87  
    88  //connreq表示对等连接请求。
    89  type connReq struct {
    90  	p      *peer
    91  	node   *enode.Node
    92  	result chan *poolEntry
    93  }
    94  
    95  //disconnreq表示对等端断开连接的请求。
    96  type disconnReq struct {
    97  	entry   *poolEntry
    98  	stopped bool
    99  	done    chan struct{}
   100  }
   101  
   102  //registerreq表示对等注册请求。
   103  type registerReq struct {
   104  	entry *poolEntry
   105  	done  chan struct{}
   106  }
   107  
   108  //ServerPool实现用于存储和选择新发现的
   109  //已知的轻型服务器节点。它接收发现的节点,存储关于
   110  //已知节点,并始终保持足够高质量的服务器连接。
   111  type serverPool struct {
   112  	db     ethdb.Database
   113  	dbKey  []byte
   114  	server *p2p.Server
   115  	quit   chan struct{}
   116  	wg     *sync.WaitGroup
   117  	connWg sync.WaitGroup
   118  
   119  	topic discv5.Topic
   120  
   121  	discSetPeriod chan time.Duration
   122  	discNodes     chan *enode.Node
   123  	discLookups   chan bool
   124  
   125  	entries              map[enode.ID]*poolEntry
   126  	timeout, enableRetry chan *poolEntry
   127  	adjustStats          chan poolStatAdjust
   128  
   129  	connCh     chan *connReq
   130  	disconnCh  chan *disconnReq
   131  	registerCh chan *registerReq
   132  
   133  	knownQueue, newQueue       poolEntryQueue
   134  	knownSelect, newSelect     *weightedRandomSelect
   135  	knownSelected, newSelected int
   136  	fastDiscover               bool
   137  }
   138  
   139  //NewServerPool创建新的ServerPool实例
   140  func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup) *serverPool {
   141  	pool := &serverPool{
   142  		db:           db,
   143  		quit:         quit,
   144  		wg:           wg,
   145  		entries:      make(map[enode.ID]*poolEntry),
   146  		timeout:      make(chan *poolEntry, 1),
   147  		adjustStats:  make(chan poolStatAdjust, 100),
   148  		enableRetry:  make(chan *poolEntry, 1),
   149  		connCh:       make(chan *connReq),
   150  		disconnCh:    make(chan *disconnReq),
   151  		registerCh:   make(chan *registerReq),
   152  		knownSelect:  newWeightedRandomSelect(),
   153  		newSelect:    newWeightedRandomSelect(),
   154  		fastDiscover: true,
   155  	}
   156  	pool.knownQueue = newPoolEntryQueue(maxKnownEntries, pool.removeEntry)
   157  	pool.newQueue = newPoolEntryQueue(maxNewEntries, pool.removeEntry)
   158  	return pool
   159  }
   160  
   161  func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) {
   162  	pool.server = server
   163  	pool.topic = topic
   164  	pool.dbKey = append([]byte("serverPool/"), []byte(topic)...)
   165  	pool.wg.Add(1)
   166  	pool.loadNodes()
   167  
   168  	if pool.server.DiscV5 != nil {
   169  		pool.discSetPeriod = make(chan time.Duration, 1)
   170  		pool.discNodes = make(chan *enode.Node, 100)
   171  		pool.discLookups = make(chan bool, 100)
   172  		go pool.discoverNodes()
   173  	}
   174  	pool.checkDial()
   175  	go pool.eventLoop()
   176  }
   177  
   178  //discovernodes包装搜索主题,将结果节点转换为enode.node。
   179  func (pool *serverPool) discoverNodes() {
   180  	ch := make(chan *discv5.Node)
   181  	go func() {
   182  		pool.server.DiscV5.SearchTopic(pool.topic, pool.discSetPeriod, ch, pool.discLookups)
   183  		close(ch)
   184  	}()
   185  	for n := range ch {
   186  		pubkey, err := decodePubkey64(n.ID[:])
   187  		if err != nil {
   188  			continue
   189  		}
   190  		pool.discNodes <- enode.NewV4(pubkey, n.IP, int(n.TCP), int(n.UDP))
   191  	}
   192  }
   193  
   194  //任何传入连接都应调用Connect。如果连接
   195  //最近由服务器池拨号,返回相应的池条目。
   196  //否则,应拒绝连接。
   197  //请注意,无论何时接受连接并返回池条目,
   198  //也应始终调用断开连接。
   199  func (pool *serverPool) connect(p *peer, node *enode.Node) *poolEntry {
   200  	log.Debug("Connect new entry", "enode", p.id)
   201  	req := &connReq{p: p, node: node, result: make(chan *poolEntry, 1)}
   202  	select {
   203  	case pool.connCh <- req:
   204  	case <-pool.quit:
   205  		return nil
   206  	}
   207  	return <-req.result
   208  }
   209  
   210  //应在成功握手后调用已注册
   211  func (pool *serverPool) registered(entry *poolEntry) {
   212  	log.Debug("Registered new entry", "enode", entry.node.ID())
   213  	req := &registerReq{entry: entry, done: make(chan struct{})}
   214  	select {
   215  	case pool.registerCh <- req:
   216  	case <-pool.quit:
   217  		return
   218  	}
   219  	<-req.done
   220  }
   221  
   222  //结束连接时应调用Disconnect。服务质量统计
   223  //可以选择更新(在这种情况下,如果没有注册,则不更新
   224  //只更新连接统计信息,就像在超时情况下一样)
   225  func (pool *serverPool) disconnect(entry *poolEntry) {
   226  	stopped := false
   227  	select {
   228  	case <-pool.quit:
   229  		stopped = true
   230  	default:
   231  	}
   232  	log.Debug("Disconnected old entry", "enode", entry.node.ID())
   233  	req := &disconnReq{entry: entry, stopped: stopped, done: make(chan struct{})}
   234  
   235  //阻止,直到断开请求被送达。
   236  	pool.disconnCh <- req
   237  	<-req.done
   238  }
   239  
   240  const (
   241  	pseBlockDelay = iota
   242  	pseResponseTime
   243  	pseResponseTimeout
   244  )
   245  
   246  //poolStatAdjust records are sent to adjust peer block delay/response time statistics
   247  type poolStatAdjust struct {
   248  	adjustType int
   249  	entry      *poolEntry
   250  	time       time.Duration
   251  }
   252  
   253  //AdjustBlockDelay调整节点的块公告延迟统计信息
   254  func (pool *serverPool) adjustBlockDelay(entry *poolEntry, time time.Duration) {
   255  	if entry == nil {
   256  		return
   257  	}
   258  	pool.adjustStats <- poolStatAdjust{pseBlockDelay, entry, time}
   259  }
   260  
   261  //AdjusteResponseTime调整节点的请求响应时间统计信息
   262  func (pool *serverPool) adjustResponseTime(entry *poolEntry, time time.Duration, timeout bool) {
   263  	if entry == nil {
   264  		return
   265  	}
   266  	if timeout {
   267  		pool.adjustStats <- poolStatAdjust{pseResponseTimeout, entry, time}
   268  	} else {
   269  		pool.adjustStats <- poolStatAdjust{pseResponseTime, entry, time}
   270  	}
   271  }
   272  
   273  //事件循环处理池事件和所有内部函数的互斥锁
   274  func (pool *serverPool) eventLoop() {
   275  	lookupCnt := 0
   276  	var convTime mclock.AbsTime
   277  	if pool.discSetPeriod != nil {
   278  		pool.discSetPeriod <- time.Millisecond * 100
   279  	}
   280  
   281  //根据连接时间断开连接更新服务质量统计信息
   282  //以及断开启动器。
   283  	disconnect := func(req *disconnReq, stopped bool) {
   284  //处理对等端断开请求。
   285  		entry := req.entry
   286  		if entry.state == psRegistered {
   287  			connAdjust := float64(mclock.Now()-entry.regTime) / float64(targetConnTime)
   288  			if connAdjust > 1 {
   289  				connAdjust = 1
   290  			}
   291  			if stopped {
   292  //我们要求断开连接。
   293  				entry.connectStats.add(1, connAdjust)
   294  			} else {
   295  //disconnect requested by server side.
   296  				entry.connectStats.add(connAdjust, 1)
   297  			}
   298  		}
   299  		entry.state = psNotConnected
   300  
   301  		if entry.knownSelected {
   302  			pool.knownSelected--
   303  		} else {
   304  			pool.newSelected--
   305  		}
   306  		pool.setRetryDial(entry)
   307  		pool.connWg.Done()
   308  		close(req.done)
   309  	}
   310  
   311  	for {
   312  		select {
   313  		case entry := <-pool.timeout:
   314  			if !entry.removed {
   315  				pool.checkDialTimeout(entry)
   316  			}
   317  
   318  		case entry := <-pool.enableRetry:
   319  			if !entry.removed {
   320  				entry.delayedRetry = false
   321  				pool.updateCheckDial(entry)
   322  			}
   323  
   324  		case adj := <-pool.adjustStats:
   325  			switch adj.adjustType {
   326  			case pseBlockDelay:
   327  				adj.entry.delayStats.add(float64(adj.time), 1)
   328  			case pseResponseTime:
   329  				adj.entry.responseStats.add(float64(adj.time), 1)
   330  				adj.entry.timeoutStats.add(0, 1)
   331  			case pseResponseTimeout:
   332  				adj.entry.timeoutStats.add(1, 1)
   333  			}
   334  
   335  		case node := <-pool.discNodes:
   336  			entry := pool.findOrNewNode(node)
   337  			pool.updateCheckDial(entry)
   338  
   339  		case conv := <-pool.discLookups:
   340  			if conv {
   341  				if lookupCnt == 0 {
   342  					convTime = mclock.Now()
   343  				}
   344  				lookupCnt++
   345  				if pool.fastDiscover && (lookupCnt == 50 || time.Duration(mclock.Now()-convTime) > time.Minute) {
   346  					pool.fastDiscover = false
   347  					if pool.discSetPeriod != nil {
   348  						pool.discSetPeriod <- time.Minute
   349  					}
   350  				}
   351  			}
   352  
   353  		case req := <-pool.connCh:
   354  //处理对等连接请求。
   355  			entry := pool.entries[req.p.ID()]
   356  			if entry == nil {
   357  				entry = pool.findOrNewNode(req.node)
   358  			}
   359  			if entry.state == psConnected || entry.state == psRegistered {
   360  				req.result <- nil
   361  				continue
   362  			}
   363  			pool.connWg.Add(1)
   364  			entry.peer = req.p
   365  			entry.state = psConnected
   366  			addr := &poolEntryAddress{
   367  				ip:       req.node.IP(),
   368  				port:     uint16(req.node.TCP()),
   369  				lastSeen: mclock.Now(),
   370  			}
   371  			entry.lastConnected = addr
   372  			entry.addr = make(map[string]*poolEntryAddress)
   373  			entry.addr[addr.strKey()] = addr
   374  			entry.addrSelect = *newWeightedRandomSelect()
   375  			entry.addrSelect.update(addr)
   376  			req.result <- entry
   377  
   378  		case req := <-pool.registerCh:
   379  //处理对等注册请求。
   380  			entry := req.entry
   381  			entry.state = psRegistered
   382  			entry.regTime = mclock.Now()
   383  			if !entry.known {
   384  				pool.newQueue.remove(entry)
   385  				entry.known = true
   386  			}
   387  			pool.knownQueue.setLatest(entry)
   388  			entry.shortRetry = shortRetryCnt
   389  			close(req.done)
   390  
   391  		case req := <-pool.disconnCh:
   392  //处理对等端断开请求。
   393  			disconnect(req, req.stopped)
   394  
   395  		case <-pool.quit:
   396  			if pool.discSetPeriod != nil {
   397  				close(pool.discSetPeriod)
   398  			}
   399  
   400  //在断开所有连接后,生成一个goroutine以关闭断开连接。
   401  			go func() {
   402  				pool.connWg.Wait()
   403  				close(pool.disconnCh)
   404  			}()
   405  
   406  //退出前处理所有剩余的断开请求。
   407  			for req := range pool.disconnCh {
   408  				disconnect(req, true)
   409  			}
   410  			pool.saveNodes()
   411  			pool.wg.Done()
   412  			return
   413  		}
   414  	}
   415  }
   416  
   417  func (pool *serverPool) findOrNewNode(node *enode.Node) *poolEntry {
   418  	now := mclock.Now()
   419  	entry := pool.entries[node.ID()]
   420  	if entry == nil {
   421  		log.Debug("Discovered new entry", "id", node.ID())
   422  		entry = &poolEntry{
   423  			node:       node,
   424  			addr:       make(map[string]*poolEntryAddress),
   425  			addrSelect: *newWeightedRandomSelect(),
   426  			shortRetry: shortRetryCnt,
   427  		}
   428  		pool.entries[node.ID()] = entry
   429  //用良好的统计数据初始化以前未知的对等点,以提供证明自己的机会
   430  		entry.connectStats.add(1, initStatsWeight)
   431  		entry.delayStats.add(0, initStatsWeight)
   432  		entry.responseStats.add(0, initStatsWeight)
   433  		entry.timeoutStats.add(0, initStatsWeight)
   434  	}
   435  	entry.lastDiscovered = now
   436  	addr := &poolEntryAddress{ip: node.IP(), port: uint16(node.TCP())}
   437  	if a, ok := entry.addr[addr.strKey()]; ok {
   438  		addr = a
   439  	} else {
   440  		entry.addr[addr.strKey()] = addr
   441  	}
   442  	addr.lastSeen = now
   443  	entry.addrSelect.update(addr)
   444  	if !entry.known {
   445  		pool.newQueue.setLatest(entry)
   446  	}
   447  	return entry
   448  }
   449  
   450  //loadNodes从数据库加载已知节点及其统计信息
   451  func (pool *serverPool) loadNodes() {
   452  	enc, err := pool.db.Get(pool.dbKey)
   453  	if err != nil {
   454  		return
   455  	}
   456  	var list []*poolEntry
   457  	err = rlp.DecodeBytes(enc, &list)
   458  	if err != nil {
   459  		log.Debug("Failed to decode node list", "err", err)
   460  		return
   461  	}
   462  	for _, e := range list {
   463  		log.Debug("Loaded server stats", "id", e.node.ID(), "fails", e.lastConnected.fails,
   464  			"conn", fmt.Sprintf("%v/%v", e.connectStats.avg, e.connectStats.weight),
   465  			"delay", fmt.Sprintf("%v/%v", time.Duration(e.delayStats.avg), e.delayStats.weight),
   466  			"response", fmt.Sprintf("%v/%v", time.Duration(e.responseStats.avg), e.responseStats.weight),
   467  			"timeout", fmt.Sprintf("%v/%v", e.timeoutStats.avg, e.timeoutStats.weight))
   468  		pool.entries[e.node.ID()] = e
   469  		pool.knownQueue.setLatest(e)
   470  		pool.knownSelect.update((*knownEntry)(e))
   471  	}
   472  }
   473  
   474  //savenodes将已知节点及其统计信息保存到数据库中。节点是
   475  //从最少订购到最近连接。
   476  func (pool *serverPool) saveNodes() {
   477  	list := make([]*poolEntry, len(pool.knownQueue.queue))
   478  	for i := range list {
   479  		list[i] = pool.knownQueue.fetchOldest()
   480  	}
   481  	enc, err := rlp.EncodeToBytes(list)
   482  	if err == nil {
   483  		pool.db.Put(pool.dbKey, enc)
   484  	}
   485  }
   486  
   487  //当达到项计数限制时,removeentry将删除池项。
   488  //请注意,它是由新的/已知的队列调用的,该条目已经从这些队列中
   489  //已删除,因此不需要将其从队列中删除。
   490  func (pool *serverPool) removeEntry(entry *poolEntry) {
   491  	pool.newSelect.remove((*discoveredEntry)(entry))
   492  	pool.knownSelect.remove((*knownEntry)(entry))
   493  	entry.removed = true
   494  	delete(pool.entries, entry.node.ID())
   495  }
   496  
   497  //setretrydial启动计时器,该计时器将再次启用拨号某个节点
   498  func (pool *serverPool) setRetryDial(entry *poolEntry) {
   499  	delay := longRetryDelay
   500  	if entry.shortRetry > 0 {
   501  		entry.shortRetry--
   502  		delay = shortRetryDelay
   503  	}
   504  	delay += time.Duration(rand.Int63n(int64(delay) + 1))
   505  	entry.delayedRetry = true
   506  	go func() {
   507  		select {
   508  		case <-pool.quit:
   509  		case <-time.After(delay):
   510  			select {
   511  			case <-pool.quit:
   512  			case pool.enableRetry <- entry:
   513  			}
   514  		}
   515  	}()
   516  }
   517  
   518  //当一个条目可能再次拨号时,调用updateCheckDial。资讯科技更新
   519  //它的选择权重和检查是否可以/应该进行新的拨号。
   520  func (pool *serverPool) updateCheckDial(entry *poolEntry) {
   521  	pool.newSelect.update((*discoveredEntry)(entry))
   522  	pool.knownSelect.update((*knownEntry)(entry))
   523  	pool.checkDial()
   524  }
   525  
   526  //checkDial checks if new dials can/should be made. It tries to select servers both
   527  //基于良好的统计数据和最近的发现。
   528  func (pool *serverPool) checkDial() {
   529  	fillWithKnownSelects := !pool.fastDiscover
   530  	for pool.knownSelected < targetKnownSelect {
   531  		entry := pool.knownSelect.choose()
   532  		if entry == nil {
   533  			fillWithKnownSelects = false
   534  			break
   535  		}
   536  		pool.dial((*poolEntry)(entry.(*knownEntry)), true)
   537  	}
   538  	for pool.knownSelected+pool.newSelected < targetServerCount {
   539  		entry := pool.newSelect.choose()
   540  		if entry == nil {
   541  			break
   542  		}
   543  		pool.dial((*poolEntry)(entry.(*discoveredEntry)), false)
   544  	}
   545  	if fillWithKnownSelects {
   546  //没有新发现的节点可供选择,并且自快速发现阶段以来
   547  //结束了,我们可能在不久的将来找不到更多,所以选择更多
   548  //已知条目(如果可能)
   549  		for pool.knownSelected < targetServerCount {
   550  			entry := pool.knownSelect.choose()
   551  			if entry == nil {
   552  				break
   553  			}
   554  			pool.dial((*poolEntry)(entry.(*knownEntry)), true)
   555  		}
   556  	}
   557  }
   558  
   559  //拨号启动新连接
   560  func (pool *serverPool) dial(entry *poolEntry, knownSelected bool) {
   561  	if pool.server == nil || entry.state != psNotConnected {
   562  		return
   563  	}
   564  	entry.state = psDialed
   565  	entry.knownSelected = knownSelected
   566  	if knownSelected {
   567  		pool.knownSelected++
   568  	} else {
   569  		pool.newSelected++
   570  	}
   571  	addr := entry.addrSelect.choose().(*poolEntryAddress)
   572  	log.Debug("Dialing new peer", "lesaddr", entry.node.ID().String()+"@"+addr.strKey(), "set", len(entry.addr), "known", knownSelected)
   573  	entry.dialed = addr
   574  	go func() {
   575  		pool.server.AddPeer(entry.node)
   576  		select {
   577  		case <-pool.quit:
   578  		case <-time.After(dialTimeout):
   579  			select {
   580  			case <-pool.quit:
   581  			case pool.timeout <- entry:
   582  			}
   583  		}
   584  	}()
   585  }
   586  
   587  //CheckDialTimeout检查节点是否仍处于拨号状态,如果仍然处于拨号状态,则将其重置。
   588  //并相应地调整连接统计。
   589  func (pool *serverPool) checkDialTimeout(entry *poolEntry) {
   590  	if entry.state != psDialed {
   591  		return
   592  	}
   593  	log.Debug("Dial timeout", "lesaddr", entry.node.ID().String()+"@"+entry.dialed.strKey())
   594  	entry.state = psNotConnected
   595  	if entry.knownSelected {
   596  		pool.knownSelected--
   597  	} else {
   598  		pool.newSelected--
   599  	}
   600  	entry.connectStats.add(0, 1)
   601  	entry.dialed.fails++
   602  	pool.setRetryDial(entry)
   603  }
   604  
   605  const (
   606  	psNotConnected = iota
   607  	psDialed
   608  	psConnected
   609  	psRegistered
   610  )
   611  
   612  //Poolentry表示服务器节点,并存储其当前状态和统计信息。
   613  type poolEntry struct {
   614  	peer                  *peer
   615  pubkey                [64]byte //secp256k1节点密钥
   616  	addr                  map[string]*poolEntryAddress
   617  	node                  *enode.Node
   618  	lastConnected, dialed *poolEntryAddress
   619  	addrSelect            weightedRandomSelect
   620  
   621  	lastDiscovered              mclock.AbsTime
   622  	known, knownSelected        bool
   623  	connectStats, delayStats    poolStats
   624  	responseStats, timeoutStats poolStats
   625  	state                       int
   626  	regTime                     mclock.AbsTime
   627  	queueIdx                    int
   628  	removed                     bool
   629  
   630  	delayedRetry bool
   631  	shortRetry   int
   632  }
   633  
   634  //poolentryenc是poolentry的rlp编码。
   635  type poolEntryEnc struct {
   636  	Pubkey                     []byte
   637  	IP                         net.IP
   638  	Port                       uint16
   639  	Fails                      uint
   640  	CStat, DStat, RStat, TStat poolStats
   641  }
   642  
   643  func (e *poolEntry) EncodeRLP(w io.Writer) error {
   644  	return rlp.Encode(w, &poolEntryEnc{
   645  		Pubkey: encodePubkey64(e.node.Pubkey()),
   646  		IP:     e.lastConnected.ip,
   647  		Port:   e.lastConnected.port,
   648  		Fails:  e.lastConnected.fails,
   649  		CStat:  e.connectStats,
   650  		DStat:  e.delayStats,
   651  		RStat:  e.responseStats,
   652  		TStat:  e.timeoutStats,
   653  	})
   654  }
   655  
   656  func (e *poolEntry) DecodeRLP(s *rlp.Stream) error {
   657  	var entry poolEntryEnc
   658  	if err := s.Decode(&entry); err != nil {
   659  		return err
   660  	}
   661  	pubkey, err := decodePubkey64(entry.Pubkey)
   662  	if err != nil {
   663  		return err
   664  	}
   665  	addr := &poolEntryAddress{ip: entry.IP, port: entry.Port, fails: entry.Fails, lastSeen: mclock.Now()}
   666  	e.node = enode.NewV4(pubkey, entry.IP, int(entry.Port), int(entry.Port))
   667  	e.addr = make(map[string]*poolEntryAddress)
   668  	e.addr[addr.strKey()] = addr
   669  	e.addrSelect = *newWeightedRandomSelect()
   670  	e.addrSelect.update(addr)
   671  	e.lastConnected = addr
   672  	e.connectStats = entry.CStat
   673  	e.delayStats = entry.DStat
   674  	e.responseStats = entry.RStat
   675  	e.timeoutStats = entry.TStat
   676  	e.shortRetry = shortRetryCnt
   677  	e.known = true
   678  	return nil
   679  }
   680  
   681  func encodePubkey64(pub *ecdsa.PublicKey) []byte {
   682  	return crypto.FromECDSAPub(pub)[1:]
   683  }
   684  
   685  func decodePubkey64(b []byte) (*ecdsa.PublicKey, error) {
   686  	return crypto.UnmarshalPubkey(append([]byte{0x04}, b...))
   687  }
   688  
   689  //DiscoveredEntry实现WRSitem
   690  type discoveredEntry poolEntry
   691  
   692  //权重为新发现的条目计算随机选择权重
   693  func (e *discoveredEntry) Weight() int64 {
   694  	if e.state != psNotConnected || e.delayedRetry {
   695  		return 0
   696  	}
   697  	t := time.Duration(mclock.Now() - e.lastDiscovered)
   698  	if t <= discoverExpireStart {
   699  		return 1000000000
   700  	}
   701  	return int64(1000000000 * math.Exp(-float64(t-discoverExpireStart)/float64(discoverExpireConst)))
   702  }
   703  
   704  //知识工具
   705  type knownEntry poolEntry
   706  
   707  //权重计算已知条目的随机选择权重
   708  func (e *knownEntry) Weight() int64 {
   709  	if e.state != psNotConnected || !e.known || e.delayedRetry {
   710  		return 0
   711  	}
   712  	return int64(1000000000 * e.connectStats.recentAvg() * math.Exp(-float64(e.lastConnected.fails)*failDropLn-e.responseStats.recentAvg()/float64(responseScoreTC)-e.delayStats.recentAvg()/float64(delayScoreTC)) * math.Pow(1-e.timeoutStats.recentAvg(), timeoutPow))
   713  }
   714  
   715  //PoolentryAddress是一个单独的对象,因为当前需要记住
   716  //池项的多个潜在网络地址。这将在
   717  //v5发现的最终实现,它将检索签名和序列
   718  //编号的广告,使其明确哪个IP/端口是最新的。
   719  type poolEntryAddress struct {
   720  	ip       net.IP
   721  	port     uint16
   722  lastSeen mclock.AbsTime //上次从数据库发现、连接或加载它时
   723  fails    uint           //自上次成功连接以来的连接失败(持久)
   724  }
   725  
   726  func (a *poolEntryAddress) Weight() int64 {
   727  	t := time.Duration(mclock.Now() - a.lastSeen)
   728  	return int64(1000000*math.Exp(-float64(t)/float64(discoverExpireConst)-float64(a.fails)*addrFailDropLn)) + 1
   729  }
   730  
   731  func (a *poolEntryAddress) strKey() string {
   732  	return a.ip.String() + ":" + strconv.Itoa(int(a.port))
   733  }
   734  
   735  //poolstats使用长期平均值对特定数量进行统计
   736  //以及一个以指数形式调整的短期值,其系数为
   737  //pstatrecentadjust与每个更新同时以指数形式返回到
   738  //时间常数pstatorntomeantc的平均值
   739  type poolStats struct {
   740  	sum, weight, avg, recent float64
   741  	lastRecalc               mclock.AbsTime
   742  }
   743  
   744  //init使用从数据库中检索到的长期sum/update count对初始化统计信息
   745  func (s *poolStats) init(sum, weight float64) {
   746  	s.sum = sum
   747  	s.weight = weight
   748  	var avg float64
   749  	if weight > 0 {
   750  		avg = s.sum / weight
   751  	}
   752  	s.avg = avg
   753  	s.recent = avg
   754  	s.lastRecalc = mclock.Now()
   755  }
   756  
   757  //重新计算近期值返回平均值和长期平均值
   758  func (s *poolStats) recalc() {
   759  	now := mclock.Now()
   760  	s.recent = s.avg + (s.recent-s.avg)*math.Exp(-float64(now-s.lastRecalc)/float64(pstatReturnToMeanTC))
   761  	if s.sum == 0 {
   762  		s.avg = 0
   763  	} else {
   764  		if s.sum > s.weight*1e30 {
   765  			s.avg = 1e30
   766  		} else {
   767  			s.avg = s.sum / s.weight
   768  		}
   769  	}
   770  	s.lastRecalc = now
   771  }
   772  
   773  //添加用新值更新统计信息
   774  func (s *poolStats) add(value, weight float64) {
   775  	s.weight += weight
   776  	s.sum += value * weight
   777  	s.recalc()
   778  }
   779  
   780  //recentavg返回短期调整平均值
   781  func (s *poolStats) recentAvg() float64 {
   782  	s.recalc()
   783  	return s.recent
   784  }
   785  
   786  func (s *poolStats) EncodeRLP(w io.Writer) error {
   787  	return rlp.Encode(w, []interface{}{math.Float64bits(s.sum), math.Float64bits(s.weight)})
   788  }
   789  
   790  func (s *poolStats) DecodeRLP(st *rlp.Stream) error {
   791  	var stats struct {
   792  		SumUint, WeightUint uint64
   793  	}
   794  	if err := st.Decode(&stats); err != nil {
   795  		return err
   796  	}
   797  	s.init(math.Float64frombits(stats.SumUint), math.Float64frombits(stats.WeightUint))
   798  	return nil
   799  }
   800  
   801  //PoolentryQueue跟踪其最近访问次数最少的条目并删除
   802  //当条目数达到限制时
   803  type poolEntryQueue struct {
   804  queue                  map[int]*poolEntry //known nodes indexed by their latest lastConnCnt value
   805  	newPtr, oldPtr, maxCnt int
   806  	removeFromPool         func(*poolEntry)
   807  }
   808  
   809  //NewPoolentryQueue返回新的PoolentryQueue
   810  func newPoolEntryQueue(maxCnt int, removeFromPool func(*poolEntry)) poolEntryQueue {
   811  	return poolEntryQueue{queue: make(map[int]*poolEntry), maxCnt: maxCnt, removeFromPool: removeFromPool}
   812  }
   813  
   814  //fetcholdst返回并删除最近访问次数最少的条目
   815  func (q *poolEntryQueue) fetchOldest() *poolEntry {
   816  	if len(q.queue) == 0 {
   817  		return nil
   818  	}
   819  	for {
   820  		if e := q.queue[q.oldPtr]; e != nil {
   821  			delete(q.queue, q.oldPtr)
   822  			q.oldPtr++
   823  			return e
   824  		}
   825  		q.oldPtr++
   826  	}
   827  }
   828  
   829  //删除从队列中删除一个条目
   830  func (q *poolEntryQueue) remove(entry *poolEntry) {
   831  	if q.queue[entry.queueIdx] == entry {
   832  		delete(q.queue, entry.queueIdx)
   833  	}
   834  }
   835  
   836  //setlatest添加或更新最近访问的条目。它还检查旧条目
   837  //需要移除,并用回调函数从父池中移除它。
   838  func (q *poolEntryQueue) setLatest(entry *poolEntry) {
   839  	if q.queue[entry.queueIdx] == entry {
   840  		delete(q.queue, entry.queueIdx)
   841  	} else {
   842  		if len(q.queue) == q.maxCnt {
   843  			e := q.fetchOldest()
   844  			q.remove(e)
   845  			q.removeFromPool(e)
   846  		}
   847  	}
   848  	entry.queueIdx = q.newPtr
   849  	q.queue[entry.queueIdx] = entry
   850  	q.newPtr++
   851  }
   852