github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/core/bloombits/matcher.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:35</date> 10 //</624450078198730752> 11 12 13 package bloombits 14 15 import ( 16 "bytes" 17 "context" 18 "errors" 19 "math" 20 "sort" 21 "sync" 22 "sync/atomic" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common/bitutil" 26 "github.com/ethereum/go-ethereum/crypto" 27 ) 28 29 //bloom indexes表示bloom过滤器中属于 30 //一些关键。 31 type bloomIndexes [3]uint 32 33 //calcBloomIndexes返回属于给定键的BloomFilter位索引。 34 func calcBloomIndexes(b []byte) bloomIndexes { 35 b = crypto.Keccak256(b) 36 37 var idxs bloomIndexes 38 for i := 0; i < len(idxs); i++ { 39 idxs[i] = (uint(b[2*i])<<8)&2047 + uint(b[2*i+1]) 40 } 41 return idxs 42 } 43 44 //带有非零矢量的粒子图表示一个部分,其中一些子- 45 //匹配者已经找到了可能的匹配项。后续的子匹配者将 46 //二进制及其与该向量的匹配。如果矢量为零,则表示 47 //由第一个子匹配器处理的部分。 48 type partialMatches struct { 49 section uint64 50 bitset []byte 51 } 52 53 //检索表示对给定任务的检索任务分配的请求 54 //具有给定数量的提取元素的位,或对这种请求的响应。 55 //它还可以将实际结果集用作传递数据结构。 56 // 57 //“竞赛”和“错误”字段由轻型客户端用于终止匹配 58 //如果在管道的某个路径上遇到错误,则为早期。 59 type Retrieval struct { 60 Bit uint 61 Sections []uint64 62 Bitsets [][]byte 63 64 Context context.Context 65 Error error 66 } 67 68 //Matcher是一个由调度程序和逻辑匹配程序组成的流水线系统,执行 69 //位流上的二进制和/或操作,创建一个潜在的流 70 //要检查数据内容的块。 71 type Matcher struct { 72 sectionSize uint64 //要筛选的数据批的大小 73 74 filters [][]bloomIndexes //系统匹配的筛选器 75 schedulers map[uint]*scheduler //用于装载钻头的检索调度程序 76 77 retrievers chan chan uint //检索器处理等待位分配 78 counters chan chan uint //检索器进程正在等待任务计数报告 79 retrievals chan chan *Retrieval //检索器进程正在等待任务分配 80 deliveries chan *Retrieval //检索器进程正在等待任务响应传递 81 82 running uint32 //原子标记会话是否处于活动状态 83 } 84 85 //NewMatcher创建了一个新的管道,用于检索Bloom位流并执行 86 //地址和主题过滤。将筛选器组件设置为“nil”是 87 //允许并将导致跳过该筛选规则(或0x11…1)。 88 func NewMatcher(sectionSize uint64, filters [][][]byte) *Matcher { 89 //创建Matcher实例 90 m := &Matcher{ 91 sectionSize: sectionSize, 92 schedulers: make(map[uint]*scheduler), 93 retrievers: make(chan chan uint), 94 counters: make(chan chan uint), 95 retrievals: make(chan chan *Retrieval), 96 deliveries: make(chan *Retrieval), 97 } 98 //计算我们感兴趣的组的bloom位索引 99 m.filters = nil 100 101 for _, filter := range filters { 102 //采集滤波规则的位指标,专用套管零滤波器 103 if len(filter) == 0 { 104 continue 105 } 106 bloomBits := make([]bloomIndexes, len(filter)) 107 for i, clause := range filter { 108 if clause == nil { 109 bloomBits = nil 110 break 111 } 112 bloomBits[i] = calcBloomIndexes(clause) 113 } 114 //如果不包含nil规则,则累积筛选规则 115 if bloomBits != nil { 116 m.filters = append(m.filters, bloomBits) 117 } 118 } 119 //对于每个位,创建一个调度程序来加载/下载位向量 120 for _, bloomIndexLists := range m.filters { 121 for _, bloomIndexList := range bloomIndexLists { 122 for _, bloomIndex := range bloomIndexList { 123 m.addScheduler(bloomIndex) 124 } 125 } 126 } 127 return m 128 } 129 130 //addscheduler为给定的位索引添加位流检索计划程序,如果 131 //它以前不存在。如果已选择位进行过滤,则 132 //可以使用现有的计划程序。 133 func (m *Matcher) addScheduler(idx uint) { 134 if _, ok := m.schedulers[idx]; ok { 135 return 136 } 137 m.schedulers[idx] = newScheduler(idx) 138 } 139 140 //Start启动匹配过程并返回一个bloom匹配流 141 //给定的块范围。如果范围内没有更多匹配项,则返回结果 142 //通道关闭。 143 func (m *Matcher) Start(ctx context.Context, begin, end uint64, results chan uint64) (*MatcherSession, error) { 144 //确保我们没有创建并发会话 145 if atomic.SwapUint32(&m.running, 1) == 1 { 146 return nil, errors.New("matcher already running") 147 } 148 defer atomic.StoreUint32(&m.running, 0) 149 150 //启动新的匹配轮 151 session := &MatcherSession{ 152 matcher: m, 153 quit: make(chan struct{}), 154 kill: make(chan struct{}), 155 ctx: ctx, 156 } 157 for _, scheduler := range m.schedulers { 158 scheduler.reset() 159 } 160 sink := m.run(begin, end, cap(results), session) 161 162 //从结果接收器读取输出并传递给用户 163 session.pend.Add(1) 164 go func() { 165 defer session.pend.Done() 166 defer close(results) 167 168 for { 169 select { 170 case <-session.quit: 171 return 172 173 case res, ok := <-sink: 174 //找到新的匹配结果 175 if !ok { 176 return 177 } 178 //计算该节的第一个和最后一个块 179 sectionStart := res.section * m.sectionSize 180 181 first := sectionStart 182 if begin > first { 183 first = begin 184 } 185 last := sectionStart + m.sectionSize - 1 186 if end < last { 187 last = end 188 } 189 //遍历该节中的所有块并返回匹配的块 190 for i := first; i <= last; i++ { 191 //如果在内部没有找到匹配项,则跳过整个字节(我们正在处理整个字节!) 192 next := res.bitset[(i-sectionStart)/8] 193 if next == 0 { 194 if i%8 == 0 { 195 i += 7 196 } 197 continue 198 } 199 //设置一些位,做实际的子匹配 200 if bit := 7 - i%8; next&(1<<bit) != 0 { 201 select { 202 case <-session.quit: 203 return 204 case results <- i: 205 } 206 } 207 } 208 } 209 } 210 }() 211 return session, nil 212 } 213 214 //运行创建子匹配器的菊花链,一个用于地址集,另一个用于 215 //对于每个主题集,每个子匹配器仅在上一个主题集 216 //所有人都在该路段的一个街区找到了一个潜在的匹配点, 217 //然后二进制和ing自己匹配并将结果转发到下一个结果。 218 // 219 //该方法开始向上的第一个子匹配器提供节索引。 220 //并返回接收结果的接收通道。 221 func (m *Matcher) run(begin, end uint64, buffer int, session *MatcherSession) chan *partialMatches { 222 //创建源通道并将节索引馈送到 223 source := make(chan *partialMatches, buffer) 224 225 session.pend.Add(1) 226 go func() { 227 defer session.pend.Done() 228 defer close(source) 229 230 for i := begin / m.sectionSize; i <= end/m.sectionSize; i++ { 231 select { 232 case <-session.quit: 233 return 234 case source <- &partialMatches{i, bytes.Repeat([]byte{0xff}, int(m.sectionSize/8))}: 235 } 236 } 237 }() 238 //组装菊花链过滤管道 239 next := source 240 dist := make(chan *request, buffer) 241 242 for _, bloom := range m.filters { 243 next = m.subMatch(next, dist, bloom, session) 244 } 245 //启动请求分发 246 session.pend.Add(1) 247 go m.distributor(dist, session) 248 249 return next 250 } 251 252 //Submatch创建一个子匹配器,该匹配器过滤一组地址或主题(二进制或-s),然后 253 //二进制和-s将结果发送到菊花链输入(源),并将其转发到菊花链输出。 254 //每个地址/主题的匹配是通过获取属于 255 //这个地址/主题,二进制和将这些向量组合在一起。 256 func (m *Matcher) subMatch(source chan *partialMatches, dist chan *request, bloom []bloomIndexes, session *MatcherSession) chan *partialMatches { 257 //为Bloom过滤器所需的每个位启动并发调度程序 258 sectionSources := make([][3]chan uint64, len(bloom)) 259 sectionSinks := make([][3]chan []byte, len(bloom)) 260 for i, bits := range bloom { 261 for j, bit := range bits { 262 sectionSources[i][j] = make(chan uint64, cap(source)) 263 sectionSinks[i][j] = make(chan []byte, cap(source)) 264 265 m.schedulers[bit].run(sectionSources[i][j], dist, sectionSinks[i][j], session.quit, &session.pend) 266 } 267 } 268 269 process := make(chan *partialMatches, cap(source)) //在初始化提取之后,源中的条目将在此处转发。 270 results := make(chan *partialMatches, cap(source)) 271 272 session.pend.Add(2) 273 go func() { 274 //关闭Goroutine并终止所有源通道 275 defer session.pend.Done() 276 defer close(process) 277 278 defer func() { 279 for _, bloomSources := range sectionSources { 280 for _, bitSource := range bloomSources { 281 close(bitSource) 282 } 283 } 284 }() 285 //从源信道中读取段,并多路复用到所有位调度程序中。 286 for { 287 select { 288 case <-session.quit: 289 return 290 291 case subres, ok := <-source: 292 //从上一链接新建子结果 293 if !ok { 294 return 295 } 296 //将区段索引多路复用到所有位调度程序 297 for _, bloomSources := range sectionSources { 298 for _, bitSource := range bloomSources { 299 select { 300 case <-session.quit: 301 return 302 case bitSource <- subres.section: 303 } 304 } 305 } 306 //通知处理器此部分将可用 307 select { 308 case <-session.quit: 309 return 310 case process <- subres: 311 } 312 } 313 } 314 }() 315 316 go func() { 317 //拆除Goroutine并终止最终水槽通道 318 defer session.pend.Done() 319 defer close(results) 320 321 //读取源通知并收集传递的结果 322 for { 323 select { 324 case <-session.quit: 325 return 326 327 case subres, ok := <-process: 328 //已通知正在检索的节 329 if !ok { 330 return 331 } 332 //收集所有子结果并将它们合并在一起 333 var orVector []byte 334 for _, bloomSinks := range sectionSinks { 335 var andVector []byte 336 for _, bitSink := range bloomSinks { 337 var data []byte 338 select { 339 case <-session.quit: 340 return 341 case data = <-bitSink: 342 } 343 if andVector == nil { 344 andVector = make([]byte, int(m.sectionSize/8)) 345 copy(andVector, data) 346 } else { 347 bitutil.ANDBytes(andVector, andVector, data) 348 } 349 } 350 if orVector == nil { 351 orVector = andVector 352 } else { 353 bitutil.ORBytes(orVector, orVector, andVector) 354 } 355 } 356 357 if orVector == nil { 358 orVector = make([]byte, int(m.sectionSize/8)) 359 } 360 if subres.bitset != nil { 361 bitutil.ANDBytes(orVector, orVector, subres.bitset) 362 } 363 if bitutil.TestBytes(orVector) { 364 select { 365 case <-session.quit: 366 return 367 case results <- &partialMatches{subres.section, orVector}: 368 } 369 } 370 } 371 } 372 }() 373 return results 374 } 375 376 //分发服务器接收来自调度程序的请求并将它们排队到一个集合中 377 //待处理的请求,这些请求被分配给想要完成它们的检索器。 378 func (m *Matcher) distributor(dist chan *request, session *MatcherSession) { 379 defer session.pend.Done() 380 381 var ( 382 requests = make(map[uint][]uint64) //按节号排序的节请求的逐位列表 383 unallocs = make(map[uint]struct{}) //具有挂起请求但未分配给任何检索器的位 384 retrievers chan chan uint //等待检索器(如果unallocs为空,则切换为nil) 385 ) 386 var ( 387 allocs int //处理正常关闭请求的活动分配数 388 shutdown = session.quit //关闭请求通道,将优雅地等待挂起的请求 389 ) 390 391 //assign是一个帮助方法,用于尝试将挂起的位 392 //监听服务,或者在有人到达后安排它。 393 assign := func(bit uint) { 394 select { 395 case fetcher := <-m.retrievers: 396 allocs++ 397 fetcher <- bit 398 default: 399 //没有激活的检索器,开始监听新的检索器 400 retrievers = m.retrievers 401 unallocs[bit] = struct{}{} 402 } 403 } 404 405 for { 406 select { 407 case <-shutdown: 408 //请求正常关闭,等待所有挂起的请求得到满足。 409 if allocs == 0 { 410 return 411 } 412 shutdown = nil 413 414 case <-session.kill: 415 //未及时处理的未决请求,硬终止 416 return 417 418 case req := <-dist: 419 //到达新的检索请求,将其分发到某个提取进程 420 queue := requests[req.bit] 421 index := sort.Search(len(queue), func(i int) bool { return queue[i] >= req.section }) 422 requests[req.bit] = append(queue[:index], append([]uint64{req.section}, queue[index:]...)...) 423 424 //如果是一个新的位,我们有等待取数器,分配给他们 425 if len(queue) == 0 { 426 assign(req.bit) 427 } 428 429 case fetcher := <-retrievers: 430 //新的检索器到达,找到要分配的最下面的ed位。 431 bit, best := uint(0), uint64(math.MaxUint64) 432 for idx := range unallocs { 433 if requests[idx][0] < best { 434 bit, best = idx, requests[idx][0] 435 } 436 } 437 //停止跟踪此位(如果没有更多工作可用,则停止分配通知) 438 delete(unallocs, bit) 439 if len(unallocs) == 0 { 440 retrievers = nil 441 } 442 allocs++ 443 fetcher <- bit 444 445 case fetcher := <-m.counters: 446 //新任务计数请求已到达,返回项目数 447 fetcher <- uint(len(requests[<-fetcher])) 448 449 case fetcher := <-m.retrievals: 450 //等待任务检索、分配的新提取程序 451 task := <-fetcher 452 if want := len(task.Sections); want >= len(requests[task.Bit]) { 453 task.Sections = requests[task.Bit] 454 delete(requests, task.Bit) 455 } else { 456 task.Sections = append(task.Sections[:0], requests[task.Bit][:want]...) 457 requests[task.Bit] = append(requests[task.Bit][:0], requests[task.Bit][want:]...) 458 } 459 fetcher <- task 460 461 //如果有未分配的内容,请尝试分配给其他人 462 if len(requests[task.Bit]) > 0 { 463 assign(task.Bit) 464 } 465 466 case result := <-m.deliveries: 467 //从获取器中新建检索任务响应,拆分缺少的部分和 468 //交付完整的 469 var ( 470 sections = make([]uint64, 0, len(result.Sections)) 471 bitsets = make([][]byte, 0, len(result.Bitsets)) 472 missing = make([]uint64, 0, len(result.Sections)) 473 ) 474 for i, bitset := range result.Bitsets { 475 if len(bitset) == 0 { 476 missing = append(missing, result.Sections[i]) 477 continue 478 } 479 sections = append(sections, result.Sections[i]) 480 bitsets = append(bitsets, bitset) 481 } 482 m.schedulers[result.Bit].deliver(sections, bitsets) 483 allocs-- 484 485 //重新安排缺少的部分,如果新的部分可用,则分配位 486 if len(missing) > 0 { 487 queue := requests[result.Bit] 488 for _, section := range missing { 489 index := sort.Search(len(queue), func(i int) bool { return queue[i] >= section }) 490 queue = append(queue[:index], append([]uint64{section}, queue[index:]...)...) 491 } 492 requests[result.Bit] = queue 493 494 if len(queue) == len(missing) { 495 assign(result.Bit) 496 } 497 } 498 //如果我们正在关闭,请终止 499 if allocs == 0 && shutdown == nil { 500 return 501 } 502 } 503 } 504 } 505 506 //MatcherSession由已启动的Matcher返回,用作终止符 507 //对于正在运行的匹配操作。 508 type MatcherSession struct { 509 matcher *Matcher 510 511 closer sync.Once //同步对象以确保只关闭一次 512 quit chan struct{} //退出通道以请求管道终止 513 kill chan struct{} //表示非正常强制停机的通道 514 515 ctx context.Context //轻型客户端用于中止筛选的上下文 516 err atomic.Value //跟踪链深处检索失败的全局错误 517 518 pend sync.WaitGroup 519 } 520 521 //关闭停止匹配进程并等待所有子进程终止 522 //返回前。超时可用于正常关机,允许 523 //当前正在运行要在此时间之前完成的检索。 524 func (s *MatcherSession) Close() { 525 s.closer.Do(func() { 526 //信号终止并等待所有Goroutine关闭 527 close(s.quit) 528 time.AfterFunc(time.Second, func() { close(s.kill) }) 529 s.pend.Wait() 530 }) 531 } 532 533 //错误返回匹配会话期间遇到的任何失败。 534 func (s *MatcherSession) Error() error { 535 if err := s.err.Load(); err != nil { 536 return err.(error) 537 } 538 return nil 539 } 540 541 //allocateretrieval将一个bloom位索引分配给一个客户端进程,该进程可以 542 //立即请求并获取分配给该位的节内容或等待 543 //有一段时间需要更多的部分。 544 func (s *MatcherSession) AllocateRetrieval() (uint, bool) { 545 fetcher := make(chan uint) 546 547 select { 548 case <-s.quit: 549 return 0, false 550 case s.matcher.retrievers <- fetcher: 551 bit, ok := <-fetcher 552 return bit, ok 553 } 554 } 555 556 //PendingSections返回属于 557 //给定的牙轮钻头指数。 558 func (s *MatcherSession) PendingSections(bit uint) int { 559 fetcher := make(chan uint) 560 561 select { 562 case <-s.quit: 563 return 0 564 case s.matcher.counters <- fetcher: 565 fetcher <- bit 566 return int(<-fetcher) 567 } 568 } 569 570 //分配操作分配已分配的位任务队列的全部或部分 571 //到请求过程。 572 func (s *MatcherSession) AllocateSections(bit uint, count int) []uint64 { 573 fetcher := make(chan *Retrieval) 574 575 select { 576 case <-s.quit: 577 return nil 578 case s.matcher.retrievals <- fetcher: 579 task := &Retrieval{ 580 Bit: bit, 581 Sections: make([]uint64, count), 582 } 583 fetcher <- task 584 return (<-fetcher).Sections 585 } 586 } 587 588 //deliversections为特定的bloom提供一批区段位向量 589 //要注入处理管道的位索引。 590 func (s *MatcherSession) DeliverSections(bit uint, sections []uint64, bitsets [][]byte) { 591 select { 592 case <-s.kill: 593 return 594 case s.matcher.deliveries <- &Retrieval{Bit: bit, Sections: sections, Bitsets: bitsets}: 595 } 596 } 597 598 //多路复用轮询匹配器会话以执行检索任务,并将其多路复用到 599 //请求的检索队列将与其他会话一起提供服务。 600 // 601 //此方法将在会话的生存期内阻塞。即使在终止之后 602 //在会议期间,任何在飞行中的请求都需要得到响应!空响应 603 //不过在那种情况下还是可以的。 604 func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan *Retrieval) { 605 for { 606 //分配新的Bloom位索引以检索数据,完成后停止 607 bit, ok := s.AllocateRetrieval() 608 if !ok { 609 return 610 } 611 //位分配,如果低于批处理限制,则限制一点 612 if s.PendingSections(bit) < batch { 613 select { 614 case <-s.quit: 615 //会话终止,我们无法有意义地服务,中止 616 s.AllocateSections(bit, 0) 617 s.DeliverSections(bit, []uint64{}, [][]byte{}) 618 return 619 620 case <-time.After(wait): 621 //节流,获取任何可用的 622 } 623 } 624 //尽可能多地分配和请求服务 625 sections := s.AllocateSections(bit, batch) 626 request := make(chan *Retrieval) 627 628 select { 629 case <-s.quit: 630 //会话终止,我们无法有意义地服务,中止 631 s.DeliverSections(bit, sections, make([][]byte, len(sections))) 632 return 633 634 case mux <- request: 635 //接受检索,必须在中止之前到达 636 request <- &Retrieval{Bit: bit, Sections: sections, Context: s.ctx} 637 638 result := <-request 639 if result.Error != nil { 640 s.err.Store(result.Error) 641 s.Close() 642 } 643 s.DeliverSections(result.Bit, result.Sections, result.Bitsets) 644 } 645 } 646 } 647