github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/les/freeclient.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:38</date> 10 //</624450093826707456> 11 12 13 //包les实现轻以太坊子协议。 14 package les 15 16 import ( 17 "io" 18 "math" 19 "sync" 20 "time" 21 22 "github.com/ethereum/go-ethereum/common/mclock" 23 "github.com/ethereum/go-ethereum/common/prque" 24 "github.com/ethereum/go-ethereum/ethdb" 25 "github.com/ethereum/go-ethereum/log" 26 "github.com/ethereum/go-ethereum/rlp" 27 ) 28 29 //FreeClientPool实现限制连接时间的客户端数据库 30 //对每个客户机,并管理接受/拒绝传入连接,甚至 31 //排除一些连接的客户机。池计算最近的使用时间 32 //对于每个已知客户机(当客户机 33 //已连接,未连接时按指数递减)。低级别客户 34 //最新的用法是首选的,未知节点具有最高优先级。已经 35 //连接的节点会收到一个有利于它们的小偏差,以避免接受 36 //立刻把客户赶出去。 37 // 38 //注意:池可以使用任何字符串来标识客户机。使用签名 39 //如果知道钥匙有负面影响,那么就没有意义了。 40 //客户端的值。目前,LES协议管理器使用IP地址 41 //(没有端口地址)以标识客户机。 42 type freeClientPool struct { 43 db ethdb.Database 44 lock sync.Mutex 45 clock mclock.Clock 46 closed bool 47 48 connectedLimit, totalLimit int 49 50 addressMap map[string]*freeClientPoolEntry 51 connPool, disconnPool *prque.Prque 52 startupTime mclock.AbsTime 53 logOffsetAtStartup int64 54 } 55 56 const ( 57 recentUsageExpTC = time.Hour //“最近”服务器使用的指数加权窗口的时间常数 58 fixedPointMultiplier = 0x1000000 //常量将对数转换为定点格式 59 connectedBias = time.Minute //这种偏见适用于已经建立联系的客户,以避免他们很快被淘汰。 60 ) 61 62 //NewFreeClientPool创建新的免费客户端池 63 func newFreeClientPool(db ethdb.Database, connectedLimit, totalLimit int, clock mclock.Clock) *freeClientPool { 64 pool := &freeClientPool{ 65 db: db, 66 clock: clock, 67 addressMap: make(map[string]*freeClientPoolEntry), 68 connPool: prque.New(poolSetIndex), 69 disconnPool: prque.New(poolSetIndex), 70 connectedLimit: connectedLimit, 71 totalLimit: totalLimit, 72 } 73 pool.loadFromDb() 74 return pool 75 } 76 77 func (f *freeClientPool) stop() { 78 f.lock.Lock() 79 f.closed = true 80 f.saveToDb() 81 f.lock.Unlock() 82 } 83 84 //成功握手后应调用Connect。如果连接是 85 //拒绝,不需要呼叫断开。 86 // 87 //注意:disconnectfn回调不应阻塞。 88 func (f *freeClientPool) connect(address string, disconnectFn func()) bool { 89 f.lock.Lock() 90 defer f.lock.Unlock() 91 92 if f.closed { 93 return false 94 } 95 e := f.addressMap[address] 96 now := f.clock.Now() 97 var recentUsage int64 98 if e == nil { 99 e = &freeClientPoolEntry{address: address, index: -1} 100 f.addressMap[address] = e 101 } else { 102 if e.connected { 103 log.Debug("Client already connected", "address", address) 104 return false 105 } 106 recentUsage = int64(math.Exp(float64(e.logUsage-f.logOffset(now)) / fixedPointMultiplier)) 107 } 108 e.linUsage = recentUsage - int64(now) 109 //检查(Linusage+ConnectedBias)是否小于连接池中的最高条目 110 if f.connPool.Size() == f.connectedLimit { 111 i := f.connPool.PopItem().(*freeClientPoolEntry) 112 if e.linUsage+int64(connectedBias)-i.linUsage < 0 { 113 //把它踢出去接受新客户 114 f.connPool.Remove(i.index) 115 f.calcLogUsage(i, now) 116 i.connected = false 117 f.disconnPool.Push(i, -i.logUsage) 118 log.Debug("Client kicked out", "address", i.address) 119 i.disconnectFn() 120 } else { 121 //保留旧客户并拒绝新客户 122 f.connPool.Push(i, i.linUsage) 123 log.Debug("Client rejected", "address", address) 124 return false 125 } 126 } 127 f.disconnPool.Remove(e.index) 128 e.connected = true 129 e.disconnectFn = disconnectFn 130 f.connPool.Push(e, e.linUsage) 131 if f.connPool.Size()+f.disconnPool.Size() > f.totalLimit { 132 f.disconnPool.Pop() 133 } 134 log.Debug("Client accepted", "address", address) 135 return true 136 } 137 138 //连接终止时应调用Disconnect。如果断开 139 //由池本身使用disconnectfn启动,然后调用disconnect是 140 //不必要,但允许。 141 func (f *freeClientPool) disconnect(address string) { 142 f.lock.Lock() 143 defer f.lock.Unlock() 144 145 if f.closed { 146 return 147 } 148 e := f.addressMap[address] 149 now := f.clock.Now() 150 if !e.connected { 151 log.Debug("Client already disconnected", "address", address) 152 return 153 } 154 155 f.connPool.Remove(e.index) 156 f.calcLogUsage(e, now) 157 e.connected = false 158 f.disconnPool.Push(e, -e.logUsage) 159 log.Debug("Client disconnected", "address", address) 160 } 161 162 //Logoffset计算对数的时间相关偏移量 163 //最近使用的表示法 164 func (f *freeClientPool) logOffset(now mclock.AbsTime) int64 { 165 //注意:这里fixedpointmultipler用作乘数;除数的原因 166 //是为了避免Int64溢出。我们假设int64(recentusageexptc)>>固定点乘数。 167 logDecay := int64((time.Duration(now - f.startupTime)) / (recentUsageExpTC / fixedPointMultiplier)) 168 return f.logOffsetAtStartup + logDecay 169 } 170 171 //calclogusage将最近的用法从线性表示转换为对数表示 172 //断开对等机连接或关闭客户端池时 173 func (f *freeClientPool) calcLogUsage(e *freeClientPoolEntry, now mclock.AbsTime) { 174 dt := e.linUsage + int64(now) 175 if dt < 1 { 176 dt = 1 177 } 178 e.logUsage = int64(math.Log(float64(dt))*fixedPointMultiplier) + f.logOffset(now) 179 } 180 181 //FreeClientPoolStorage是池数据库存储的RLP表示形式 182 type freeClientPoolStorage struct { 183 LogOffset uint64 184 List []*freeClientPoolEntry 185 } 186 187 //loadfromdb从数据库存储还原池状态 188 //(初始化时自动调用) 189 func (f *freeClientPool) loadFromDb() { 190 enc, err := f.db.Get([]byte("freeClientPool")) 191 if err != nil { 192 return 193 } 194 var storage freeClientPoolStorage 195 err = rlp.DecodeBytes(enc, &storage) 196 if err != nil { 197 log.Error("Failed to decode client list", "err", err) 198 return 199 } 200 f.logOffsetAtStartup = int64(storage.LogOffset) 201 f.startupTime = f.clock.Now() 202 for _, e := range storage.List { 203 log.Debug("Loaded free client record", "address", e.address, "logUsage", e.logUsage) 204 f.addressMap[e.address] = e 205 f.disconnPool.Push(e, -e.logUsage) 206 } 207 } 208 209 //savetodb将池状态保存到数据库存储 210 //(关机时自动调用) 211 func (f *freeClientPool) saveToDb() { 212 now := f.clock.Now() 213 storage := freeClientPoolStorage{ 214 LogOffset: uint64(f.logOffset(now)), 215 List: make([]*freeClientPoolEntry, len(f.addressMap)), 216 } 217 i := 0 218 for _, e := range f.addressMap { 219 if e.connected { 220 f.calcLogUsage(e, now) 221 } 222 storage.List[i] = e 223 i++ 224 } 225 enc, err := rlp.EncodeToBytes(storage) 226 if err != nil { 227 log.Error("Failed to encode client list", "err", err) 228 } else { 229 f.db.Put([]byte("freeClientPool"), enc) 230 } 231 } 232 233 //FreeClientPoolentry表示池已知的客户端地址。 234 //连接后,最近的使用量计算为linusage+int64(clock.now()) 235 //断开连接时,计算为exp(logusage-logoffset),其中logoffset 236 //服务器运行时,也会随着时间线性增长。 237 //线性和对数表示之间的转换发生在连接时 238 //或者断开节点。 239 // 240 //注:linusage和logusage是不断增加偏移量的值,因此 241 //even though they are close to each other at any time they may wrap around int64 242 //时间的限制。应进行相应的比较。 243 type freeClientPoolEntry struct { 244 address string 245 connected bool 246 disconnectFn func() 247 linUsage, logUsage int64 248 index int 249 } 250 251 func (e *freeClientPoolEntry) EncodeRLP(w io.Writer) error { 252 return rlp.Encode(w, []interface{}{e.address, uint64(e.logUsage)}) 253 } 254 255 func (e *freeClientPoolEntry) DecodeRLP(s *rlp.Stream) error { 256 var entry struct { 257 Address string 258 LogUsage uint64 259 } 260 if err := s.Decode(&entry); err != nil { 261 return err 262 } 263 e.address = entry.Address 264 e.logUsage = int64(entry.LogUsage) 265 e.connected = false 266 e.index = -1 267 return nil 268 } 269 270 //poolSetIndex callback is used by both priority queues to set/update the index of 271 //队列中的元素。需要索引来删除顶部元素以外的元素。 272 func poolSetIndex(a interface{}, i int) { 273 a.(*freeClientPoolEntry).index = i 274 } 275