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 := ®isterReq{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