github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/discover/table.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 12:09:43</date> 10 //</624342656134156288> 11 12 13 //包发现实现了节点发现协议。 14 // 15 //节点发现协议提供了一种查找 16 //可以连接到。它使用一个类似kademlia的协议来维护 17 //所有监听的ID和端点的分布式数据库 18 //节点。 19 package discover 20 21 import ( 22 crand "crypto/rand" 23 "encoding/binary" 24 "fmt" 25 mrand "math/rand" 26 "net" 27 "sort" 28 "sync" 29 "time" 30 31 "github.com/ethereum/go-ethereum/common" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/log" 34 "github.com/ethereum/go-ethereum/p2p/netutil" 35 ) 36 37 const ( 38 alpha = 3 //Kademlia并发因子 39 bucketSize = 16 //卡德米利亚水桶尺寸 40 maxReplacements = 10 //每个桶更换清单的尺寸 41 42 //我们把桶放在距离的1/15以上,因为 43 //我们不太可能遇到更近的节点。 44 hashBits = len(common.Hash{}) * 8 45 nBuckets = hashBits / 15 //桶数 46 bucketMinDistance = hashBits - nBuckets //最近桶的对数距离 47 48 //IP地址限制。 49 bucketIPLimit, bucketSubnet = 2, 24 //最多2个地址来自同一个/24 50 tableIPLimit, tableSubnet = 10, 24 51 52 maxFindnodeFailures = 5 //将删除超过此限制的节点 53 refreshInterval = 30 * time.Minute 54 revalidateInterval = 10 * time.Second 55 copyNodesInterval = 30 * time.Second 56 seedMinTableTime = 5 * time.Minute 57 seedCount = 30 58 seedMaxAge = 5 * 24 * time.Hour 59 ) 60 61 type Table struct { 62 mutex sync.Mutex //保护存储桶、存储桶内容、托儿所、兰特 63 buckets [nBuckets]*bucket //已知节点的距离索引 64 nursery []*Node //引导节点 65 rand *mrand.Rand //随机性来源,定期重新播种 66 ips netutil.DistinctNetSet 67 68 db *nodeDB //已知节点数据库 69 refreshReq chan chan struct{} 70 initDone chan struct{} 71 closeReq chan struct{} 72 closed chan struct{} 73 74 nodeAddedHook func(*Node) //用于测试 75 76 net transport 77 self *Node //本地节点的元数据 78 } 79 80 //传输由UDP传输实现。 81 //它是一个接口,因此我们可以在不打开大量UDP的情况下进行测试 82 //不生成私钥的套接字。 83 type transport interface { 84 ping(NodeID, *net.UDPAddr) error 85 findnode(toid NodeID, addr *net.UDPAddr, target NodeID) ([]*Node, error) 86 close() 87 } 88 89 //bucket包含按其上一个活动排序的节点。条目 90 //最近激活的元素是条目中的第一个元素。 91 type bucket struct { 92 entries []*Node //实时条目,按上次联系时间排序 93 replacements []*Node //如果重新验证失败,则使用最近看到的节点 94 ips netutil.DistinctNetSet 95 } 96 97 func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string, bootnodes []*Node) (*Table, error) { 98 //如果没有提供节点数据库,请使用内存中的数据库 99 db, err := newNodeDB(nodeDBPath, nodeDBVersion, ourID) 100 if err != nil { 101 return nil, err 102 } 103 tab := &Table{ 104 net: t, 105 db: db, 106 self: NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)), 107 refreshReq: make(chan chan struct{}), 108 initDone: make(chan struct{}), 109 closeReq: make(chan struct{}), 110 closed: make(chan struct{}), 111 rand: mrand.New(mrand.NewSource(0)), 112 ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit}, 113 } 114 if err := tab.setFallbackNodes(bootnodes); err != nil { 115 return nil, err 116 } 117 for i := range tab.buckets { 118 tab.buckets[i] = &bucket{ 119 ips: netutil.DistinctNetSet{Subnet: bucketSubnet, Limit: bucketIPLimit}, 120 } 121 } 122 tab.seedRand() 123 tab.loadSeedNodes() 124 //加载种子后启动后台过期goroutine,以便搜索 125 //种子节点还考虑将由 126 //到期。 127 tab.db.ensureExpirer() 128 go tab.loop() 129 return tab, nil 130 } 131 132 func (tab *Table) seedRand() { 133 var b [8]byte 134 crand.Read(b[:]) 135 136 tab.mutex.Lock() 137 tab.rand.Seed(int64(binary.BigEndian.Uint64(b[:]))) 138 tab.mutex.Unlock() 139 } 140 141 //self返回本地节点。 142 //调用方不应修改返回的节点。 143 func (tab *Table) Self() *Node { 144 return tab.self 145 } 146 147 //readrandomnodes用来自 148 //表。它不会多次写入同一节点。节点 149 //切片是副本,可以由调用方修改。 150 func (tab *Table) ReadRandomNodes(buf []*Node) (n int) { 151 if !tab.isInitDone() { 152 return 0 153 } 154 tab.mutex.Lock() 155 defer tab.mutex.Unlock() 156 157 //找到所有非空桶,并从中获取新的部分。 158 var buckets [][]*Node 159 for _, b := range &tab.buckets { 160 if len(b.entries) > 0 { 161 buckets = append(buckets, b.entries[:]) 162 } 163 } 164 if len(buckets) == 0 { 165 return 0 166 } 167 //洗牌。 168 for i := len(buckets) - 1; i > 0; i-- { 169 j := tab.rand.Intn(len(buckets)) 170 buckets[i], buckets[j] = buckets[j], buckets[i] 171 } 172 //将每个桶的头部移入buf,移除变空的桶。 173 var i, j int 174 for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) { 175 b := buckets[j] 176 buf[i] = &(*b[0]) 177 buckets[j] = b[1:] 178 if len(b) == 1 { 179 buckets = append(buckets[:j], buckets[j+1:]...) 180 } 181 if len(buckets) == 0 { 182 break 183 } 184 } 185 return i + 1 186 } 187 188 //close终止网络侦听器并刷新节点数据库。 189 func (tab *Table) Close() { 190 select { 191 case <-tab.closed: 192 //已经关闭。 193 case tab.closeReq <- struct{}{}: 194 <-tab.closed //等待RefreshLoop结束。 195 } 196 } 197 198 //setFallbackNodes设置初始接触点。这些节点 199 //如果表为空,则用于连接到网络 200 //数据库中没有已知节点。 201 func (tab *Table) setFallbackNodes(nodes []*Node) error { 202 for _, n := range nodes { 203 if err := n.validateComplete(); err != nil { 204 return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err) 205 } 206 } 207 tab.nursery = make([]*Node, 0, len(nodes)) 208 for _, n := range nodes { 209 cpy := *n 210 //重新计算cpy.sha,因为节点可能没有 211 //由newnode或parsenode创建。 212 cpy.sha = crypto.Keccak256Hash(n.ID[:]) 213 tab.nursery = append(tab.nursery, &cpy) 214 } 215 return nil 216 } 217 218 //IsInitDone返回表的初始种子设定过程是否已完成。 219 func (tab *Table) isInitDone() bool { 220 select { 221 case <-tab.initDone: 222 return true 223 default: 224 return false 225 } 226 } 227 228 //解析搜索具有给定ID的特定节点。 229 //如果找不到节点,则返回nil。 230 func (tab *Table) Resolve(targetID NodeID) *Node { 231 //如果节点存在于本地表中,则否 232 //需要网络交互。 233 hash := crypto.Keccak256Hash(targetID[:]) 234 tab.mutex.Lock() 235 cl := tab.closest(hash, 1) 236 tab.mutex.Unlock() 237 if len(cl.entries) > 0 && cl.entries[0].ID == targetID { 238 return cl.entries[0] 239 } 240 //否则,请执行网络查找。 241 result := tab.Lookup(targetID) 242 for _, n := range result { 243 if n.ID == targetID { 244 return n 245 } 246 } 247 return nil 248 } 249 250 //查找对关闭的节点执行网络搜索 251 //目标。它通过查询接近目标 252 //在每次迭代中离它更近的节点。 253 //给定目标不需要是实际节点 254 //标识符。 255 func (tab *Table) Lookup(targetID NodeID) []*Node { 256 return tab.lookup(targetID, true) 257 } 258 259 func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node { 260 var ( 261 target = crypto.Keccak256Hash(targetID[:]) 262 asked = make(map[NodeID]bool) 263 seen = make(map[NodeID]bool) 264 reply = make(chan []*Node, alpha) 265 pendingQueries = 0 266 result *nodesByDistance 267 ) 268 //如果我们撞到自己,不要再问了。 269 //在实践中不太可能经常发生。 270 asked[tab.self.ID] = true 271 272 for { 273 tab.mutex.Lock() 274 //生成初始结果集 275 result = tab.closest(target, bucketSize) 276 tab.mutex.Unlock() 277 if len(result.entries) > 0 || !refreshIfEmpty { 278 break 279 } 280 //结果集为空,删除了所有节点,刷新。 281 //我们实际上在这里等待刷新完成。非常 282 //第一个查询将命中此情况并运行引导 283 //逻辑。 284 <-tab.refresh() 285 refreshIfEmpty = false 286 } 287 288 for { 289 //询问我们尚未询问的alpha最近的节点 290 for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ { 291 n := result.entries[i] 292 if !asked[n.ID] { 293 asked[n.ID] = true 294 pendingQueries++ 295 go tab.findnode(n, targetID, reply) 296 } 297 } 298 if pendingQueries == 0 { 299 //我们要求所有最近的节点停止搜索 300 break 301 } 302 //等待下一个答复 303 for _, n := range <-reply { 304 if n != nil && !seen[n.ID] { 305 seen[n.ID] = true 306 result.push(n, bucketSize) 307 } 308 } 309 pendingQueries-- 310 } 311 return result.entries 312 } 313 314 func (tab *Table) findnode(n *Node, targetID NodeID, reply chan<- []*Node) { 315 fails := tab.db.findFails(n.ID) 316 r, err := tab.net.findnode(n.ID, n.addr(), targetID) 317 if err != nil || len(r) == 0 { 318 fails++ 319 tab.db.updateFindFails(n.ID, fails) 320 log.Trace("Findnode failed", "id", n.ID, "failcount", fails, "err", err) 321 if fails >= maxFindnodeFailures { 322 log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails) 323 tab.delete(n) 324 } 325 } else if fails > 0 { 326 tab.db.updateFindFails(n.ID, fails-1) 327 } 328 329 //抓取尽可能多的节点。他们中的一些人可能已经不在了,但我们会 330 //在重新验证期间,只需再次移除这些。 331 for _, n := range r { 332 tab.add(n) 333 } 334 reply <- r 335 } 336 337 func (tab *Table) refresh() <-chan struct{} { 338 done := make(chan struct{}) 339 select { 340 case tab.refreshReq <- done: 341 case <-tab.closed: 342 close(done) 343 } 344 return done 345 } 346 347 //循环计划刷新、重新验证运行并协调关闭。 348 func (tab *Table) loop() { 349 var ( 350 revalidate = time.NewTimer(tab.nextRevalidateTime()) 351 refresh = time.NewTicker(refreshInterval) 352 copyNodes = time.NewTicker(copyNodesInterval) 353 revalidateDone = make(chan struct{}) 354 refreshDone = make(chan struct{}) //Dorefresh报告完成 355 waiting = []chan struct{}{tab.initDone} //在DoRefresh运行时保留等待的呼叫者 356 ) 357 defer refresh.Stop() 358 defer revalidate.Stop() 359 defer copyNodes.Stop() 360 361 //开始初始刷新。 362 go tab.doRefresh(refreshDone) 363 364 loop: 365 for { 366 select { 367 case <-refresh.C: 368 tab.seedRand() 369 if refreshDone == nil { 370 refreshDone = make(chan struct{}) 371 go tab.doRefresh(refreshDone) 372 } 373 case req := <-tab.refreshReq: 374 waiting = append(waiting, req) 375 if refreshDone == nil { 376 refreshDone = make(chan struct{}) 377 go tab.doRefresh(refreshDone) 378 } 379 case <-refreshDone: 380 for _, ch := range waiting { 381 close(ch) 382 } 383 waiting, refreshDone = nil, nil 384 case <-revalidate.C: 385 go tab.doRevalidate(revalidateDone) 386 case <-revalidateDone: 387 revalidate.Reset(tab.nextRevalidateTime()) 388 case <-copyNodes.C: 389 go tab.copyLiveNodes() 390 case <-tab.closeReq: 391 break loop 392 } 393 } 394 395 if tab.net != nil { 396 tab.net.close() 397 } 398 if refreshDone != nil { 399 <-refreshDone 400 } 401 for _, ch := range waiting { 402 close(ch) 403 } 404 tab.db.close() 405 close(tab.closed) 406 } 407 408 //DoRefresh执行查找随机目标以保留存储桶 409 //满的。如果表为空,则插入种子节点(初始 410 //引导或丢弃错误对等)。 411 func (tab *Table) doRefresh(done chan struct{}) { 412 defer close(done) 413 414 //从数据库加载节点并插入 415 //他们。这将产生一些以前看到的节点, 416 //(希望)还活着。 417 tab.loadSeedNodes() 418 419 //运行自我查找以发现新的邻居节点。 420 tab.lookup(tab.self.ID, false) 421 422 //kademlia文件指定bucket刷新应该 423 //在最近使用最少的存储桶中执行查找。我们不能 424 //坚持这一点,因为findnode目标是512位值 425 //(不是哈希大小)并且不容易生成 426 //属于选定的桶中的sha3 preimage。 427 //我们用随机目标执行一些查找。 428 for i := 0; i < 3; i++ { 429 var target NodeID 430 crand.Read(target[:]) 431 tab.lookup(target, false) 432 } 433 } 434 435 func (tab *Table) loadSeedNodes() { 436 seeds := tab.db.querySeeds(seedCount, seedMaxAge) 437 seeds = append(seeds, tab.nursery...) 438 for i := range seeds { 439 seed := seeds[i] 440 age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.lastPongReceived(seed.ID)) }} 441 log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age) 442 tab.add(seed) 443 } 444 } 445 446 //Dorevalidate检查随机存储桶中的最后一个节点是否仍然活动 447 //如果没有,则替换或删除节点。 448 func (tab *Table) doRevalidate(done chan<- struct{}) { 449 defer func() { done <- struct{}{} }() 450 451 last, bi := tab.nodeToRevalidate() 452 if last == nil { 453 //找不到非空存储桶。 454 return 455 } 456 457 //ping所选节点并等待pong。 458 err := tab.net.ping(last.ID, last.addr()) 459 460 tab.mutex.Lock() 461 defer tab.mutex.Unlock() 462 b := tab.buckets[bi] 463 if err == nil { 464 //节点响应,将其移到前面。 465 log.Trace("Revalidated node", "b", bi, "id", last.ID) 466 b.bump(last) 467 return 468 } 469 //未收到回复,请选择替换项或删除节点(如果没有) 470 //任何替代品。 471 if r := tab.replace(b, last); r != nil { 472 log.Trace("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP) 473 } else { 474 log.Trace("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP) 475 } 476 } 477 478 //nodetorefalidate返回随机非空bucket中的最后一个节点。 479 func (tab *Table) nodeToRevalidate() (n *Node, bi int) { 480 tab.mutex.Lock() 481 defer tab.mutex.Unlock() 482 483 for _, bi = range tab.rand.Perm(len(tab.buckets)) { 484 b := tab.buckets[bi] 485 if len(b.entries) > 0 { 486 last := b.entries[len(b.entries)-1] 487 return last, bi 488 } 489 } 490 return nil, 0 491 } 492 493 func (tab *Table) nextRevalidateTime() time.Duration { 494 tab.mutex.Lock() 495 defer tab.mutex.Unlock() 496 497 return time.Duration(tab.rand.Int63n(int64(revalidateInterval))) 498 } 499 500 //CopyLiveNodes将表中的节点添加到数据库中(如果它们在表中)。 501 //比Mintable时间长。 502 func (tab *Table) copyLiveNodes() { 503 tab.mutex.Lock() 504 defer tab.mutex.Unlock() 505 506 now := time.Now() 507 for _, b := range &tab.buckets { 508 for _, n := range b.entries { 509 if now.Sub(n.addedAt) >= seedMinTableTime { 510 tab.db.updateNode(n) 511 } 512 } 513 } 514 } 515 516 //最近返回表中最接近 517 //给定的ID。调用方必须保持tab.mutex。 518 func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance { 519 //这是一种非常浪费的查找最近节点的方法,但是 520 //显然是正确的。我相信以树为基础的桶可以 521 //这更容易有效地实施。 522 close := &nodesByDistance{target: target} 523 for _, b := range &tab.buckets { 524 for _, n := range b.entries { 525 close.push(n, nresults) 526 } 527 } 528 return close 529 } 530 531 func (tab *Table) len() (n int) { 532 for _, b := range &tab.buckets { 533 n += len(b.entries) 534 } 535 return n 536 } 537 538 //bucket返回给定节点id散列的bucket。 539 func (tab *Table) bucket(sha common.Hash) *bucket { 540 d := logdist(tab.self.sha, sha) 541 if d <= bucketMinDistance { 542 return tab.buckets[0] 543 } 544 return tab.buckets[d-bucketMinDistance-1] 545 } 546 547 //添加将给定节点添加到其相应存储桶的尝试。如果桶有空间 548 //可用,添加节点立即成功。否则,如果 549 //bucket中最近活动的节点不响应ping数据包。 550 // 551 //调用方不能持有tab.mutex。 552 func (tab *Table) add(n *Node) { 553 tab.mutex.Lock() 554 defer tab.mutex.Unlock() 555 556 b := tab.bucket(n.sha) 557 if !tab.bumpOrAdd(b, n) { 558 //节点不在表中。将其添加到替换列表中。 559 tab.addReplacement(b, n) 560 } 561 } 562 563 //addthroughping将给定节点添加到表中。与平原相比 564 //“添加”有一个附加的安全措施:如果表仍然存在 565 //未添加初始化节点。这可以防止攻击 566 //只需重复发送ping就可以填写表格。 567 // 568 //调用方不能持有tab.mutex。 569 func (tab *Table) addThroughPing(n *Node) { 570 if !tab.isInitDone() { 571 return 572 } 573 tab.add(n) 574 } 575 576 //stufacture将表中的节点添加到相应bucket的末尾 577 //如果桶没满。调用方不能持有tab.mutex。 578 func (tab *Table) stuff(nodes []*Node) { 579 tab.mutex.Lock() 580 defer tab.mutex.Unlock() 581 582 for _, n := range nodes { 583 if n.ID == tab.self.ID { 584 continue //不要增加自我 585 } 586 b := tab.bucket(n.sha) 587 if len(b.entries) < bucketSize { 588 tab.bumpOrAdd(b, n) 589 } 590 } 591 } 592 593 //删除从节点表中删除一个条目。用于疏散死节点。 594 func (tab *Table) delete(node *Node) { 595 tab.mutex.Lock() 596 defer tab.mutex.Unlock() 597 598 tab.deleteInBucket(tab.bucket(node.sha), node) 599 } 600 601 func (tab *Table) addIP(b *bucket, ip net.IP) bool { 602 if netutil.IsLAN(ip) { 603 return true 604 } 605 if !tab.ips.Add(ip) { 606 log.Debug("IP exceeds table limit", "ip", ip) 607 return false 608 } 609 if !b.ips.Add(ip) { 610 log.Debug("IP exceeds bucket limit", "ip", ip) 611 tab.ips.Remove(ip) 612 return false 613 } 614 return true 615 } 616 617 func (tab *Table) removeIP(b *bucket, ip net.IP) { 618 if netutil.IsLAN(ip) { 619 return 620 } 621 tab.ips.Remove(ip) 622 b.ips.Remove(ip) 623 } 624 625 func (tab *Table) addReplacement(b *bucket, n *Node) { 626 for _, e := range b.replacements { 627 if e.ID == n.ID { 628 return //已列入清单 629 } 630 } 631 if !tab.addIP(b, n.IP) { 632 return 633 } 634 var removed *Node 635 b.replacements, removed = pushNode(b.replacements, n, maxReplacements) 636 if removed != nil { 637 tab.removeIP(b, removed.IP) 638 } 639 } 640 641 //replace从替换列表中删除n,如果“last”是 642 //桶中的最后一个条目。如果“last”不是最后一个条目,则它或已被替换 643 //和别人在一起或者变得活跃起来。 644 func (tab *Table) replace(b *bucket, last *Node) *Node { 645 if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID != last.ID { 646 //条目已移动,不要替换它。 647 return nil 648 } 649 //还是最后一个条目。 650 if len(b.replacements) == 0 { 651 tab.deleteInBucket(b, last) 652 return nil 653 } 654 r := b.replacements[tab.rand.Intn(len(b.replacements))] 655 b.replacements = deleteNode(b.replacements, r) 656 b.entries[len(b.entries)-1] = r 657 tab.removeIP(b, last.IP) 658 return r 659 } 660 661 //bump将给定节点移动到bucket条目列表的前面 662 //如果它包含在那个列表中。 663 func (b *bucket) bump(n *Node) bool { 664 for i := range b.entries { 665 if b.entries[i].ID == n.ID { 666 //把它移到前面 667 copy(b.entries[1:], b.entries[:i]) 668 b.entries[0] = n 669 return true 670 } 671 } 672 return false 673 } 674 675 //bumporadd将n移动到bucket条目列表的前面,或者如果该列表不在,则将其添加。 676 //满的。如果n在桶中,返回值为真。 677 func (tab *Table) bumpOrAdd(b *bucket, n *Node) bool { 678 if b.bump(n) { 679 return true 680 } 681 if len(b.entries) >= bucketSize || !tab.addIP(b, n.IP) { 682 return false 683 } 684 b.entries, _ = pushNode(b.entries, n, bucketSize) 685 b.replacements = deleteNode(b.replacements, n) 686 n.addedAt = time.Now() 687 if tab.nodeAddedHook != nil { 688 tab.nodeAddedHook(n) 689 } 690 return true 691 } 692 693 func (tab *Table) deleteInBucket(b *bucket, n *Node) { 694 b.entries = deleteNode(b.entries, n) 695 tab.removeIP(b, n.IP) 696 } 697 698 //pushnode将n添加到列表的前面,最多保留max项。 699 func pushNode(list []*Node, n *Node, max int) ([]*Node, *Node) { 700 if len(list) < max { 701 list = append(list, nil) 702 } 703 removed := list[len(list)-1] 704 copy(list[1:], list) 705 list[0] = n 706 return list, removed 707 } 708 709 //删除节点从列表中删除n。 710 func deleteNode(list []*Node, n *Node) []*Node { 711 for i := range list { 712 if list[i].ID == n.ID { 713 return append(list[:i], list[i+1:]...) 714 } 715 } 716 return list 717 } 718 719 //nodesByDistance是节点列表,按 720 //距离目标。 721 type nodesByDistance struct { 722 entries []*Node 723 target common.Hash 724 } 725 726 //push将给定节点添加到列表中,使总大小保持在maxelems以下。 727 func (h *nodesByDistance) push(n *Node, maxElems int) { 728 ix := sort.Search(len(h.entries), func(i int) bool { 729 return distcmp(h.target, h.entries[i].sha, n.sha) > 0 730 }) 731 if len(h.entries) < maxElems { 732 h.entries = append(h.entries, n) 733 } 734 if ix == len(h.entries) { 735 //比我们现有的所有节点都要远。 736 //如果有空间,那么节点现在是最后一个元素。 737 } else { 738 //向下滑动现有条目以腾出空间 739 //这将覆盖我们刚刚附加的条目。 740 copy(h.entries[ix+1:], h.entries[ix:]) 741 h.entries[ix] = n 742 } 743 } 744