github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/core/bloombits/scheduler.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 //</624450078299394048> 11 12 13 package bloombits 14 15 import ( 16 "sync" 17 ) 18 19 //请求表示一个Bloom检索任务,以确定优先级并从本地 20 //数据库或从网络远程访问。 21 type request struct { 22 section uint64 //从中检索位向量的节索引 23 bit uint //段内的位索引,用于检索 24 } 25 26 //响应表示通过调度程序请求的位向量的状态。 27 type response struct { 28 cached []byte //用于消除多个请求的缓存位 29 done chan struct{} //允许等待完成的通道 30 } 31 32 //调度程序处理Bloom过滤器检索操作的调度 33 //整段批次属于一个单独的开孔钻头。除了安排 34 //retrieval operations, this struct also deduplicates the requests and caches 35 //即使在复杂的过滤中,也能最大限度地减少网络/数据库开销。 36 //情节。 37 type scheduler struct { 38 bit uint //此计划程序负责的Bloom筛选器中位的索引 39 responses map[uint64]*response //当前挂起的检索请求或已缓存的响应 40 lock sync.Mutex //防止响应并发访问的锁 41 } 42 43 //Newscheduler为特定的 44 //位索引。 45 func newScheduler(idx uint) *scheduler { 46 return &scheduler{ 47 bit: idx, 48 responses: make(map[uint64]*response), 49 } 50 } 51 52 //运行创建检索管道,从节和 53 //通过完成通道以相同顺序返回结果。同时发生的 54 //允许运行同一个调度程序,从而导致检索任务重复数据消除。 55 func (s *scheduler) run(sections chan uint64, dist chan *request, done chan []byte, quit chan struct{}, wg *sync.WaitGroup) { 56 //在与相同大小的请求和响应之间创建转发器通道 57 //分配通道(因为这样会阻塞管道)。 58 pend := make(chan uint64, cap(dist)) 59 60 //启动管道调度程序在用户->分发服务器->用户之间转发 61 wg.Add(2) 62 go s.scheduleRequests(sections, dist, pend, quit, wg) 63 go s.scheduleDeliveries(pend, done, quit, wg) 64 } 65 66 //重置将清除以前运行的所有剩余部分。这是在 67 //重新启动以确保以前没有请求但从未传递的状态将 68 //导致锁定。 69 func (s *scheduler) reset() { 70 s.lock.Lock() 71 defer s.lock.Unlock() 72 73 for section, res := range s.responses { 74 if res.cached == nil { 75 delete(s.responses, section) 76 } 77 } 78 } 79 80 //schedulerequests从输入通道读取段检索请求, 81 //消除流中的重复数据并将唯一的检索任务推送到分发中 82 //数据库或网络层的通道。 83 func (s *scheduler) scheduleRequests(reqs chan uint64, dist chan *request, pend chan uint64, quit chan struct{}, wg *sync.WaitGroup) { 84 //完成后清理Goroutine和管道 85 defer wg.Done() 86 defer close(pend) 87 88 //继续阅读和安排分区请求 89 for { 90 select { 91 case <-quit: 92 return 93 94 case section, ok := <-reqs: 95 //请求新分区检索 96 if !ok { 97 return 98 } 99 //消除重复检索请求 100 unique := false 101 102 s.lock.Lock() 103 if s.responses[section] == nil { 104 s.responses[section] = &response{ 105 done: make(chan struct{}), 106 } 107 unique = true 108 } 109 s.lock.Unlock() 110 111 //安排检索部分的时间,并通知交付者期望此部分 112 if unique { 113 select { 114 case <-quit: 115 return 116 case dist <- &request{bit: s.bit, section: section}: 117 } 118 } 119 select { 120 case <-quit: 121 return 122 case pend <- section: 123 } 124 } 125 } 126 } 127 128 //ScheduledDeliveries读取区段接受通知并等待它们 129 //要传递,将它们推入输出数据缓冲区。 130 func (s *scheduler) scheduleDeliveries(pend chan uint64, done chan []byte, quit chan struct{}, wg *sync.WaitGroup) { 131 //完成后清理Goroutine和管道 132 defer wg.Done() 133 defer close(done) 134 135 //继续阅读通知并安排交货 136 for { 137 select { 138 case <-quit: 139 return 140 141 case idx, ok := <-pend: 142 //新分区检索挂起 143 if !ok { 144 return 145 } 146 //等到请求得到满足为止 147 s.lock.Lock() 148 res := s.responses[idx] 149 s.lock.Unlock() 150 151 select { 152 case <-quit: 153 return 154 case <-res.done: 155 } 156 //交付结果 157 select { 158 case <-quit: 159 return 160 case done <- res.cached: 161 } 162 } 163 } 164 } 165 166 //请求分发服务器在对请求的答复到达时调用传递。 167 func (s *scheduler) deliver(sections []uint64, data [][]byte) { 168 s.lock.Lock() 169 defer s.lock.Unlock() 170 171 for i, section := range sections { 172 if res := s.responses[section]; res != nil && res.cached == nil { //避免非请求和双倍交货 173 res.cached = data[i] 174 close(res.done) 175 } 176 } 177 } 178