github.com/TeaOSLab/EdgeNode@v1.3.8/internal/caches/list_memory.go (about) 1 package caches 2 3 import ( 4 "github.com/TeaOSLab/EdgeCommon/pkg/configutils" 5 "github.com/iwind/TeaGo/logs" 6 "net" 7 "net/url" 8 "strconv" 9 "strings" 10 "sync" 11 "sync/atomic" 12 "testing" 13 ) 14 15 // MemoryList 内存缓存列表管理 16 type MemoryList struct { 17 count int64 18 19 itemMaps map[string]map[string]*Item // prefix => { hash => item } 20 21 prefixes []string 22 locker sync.RWMutex 23 onAdd func(item *Item) 24 onRemove func(item *Item) 25 26 purgeIndex int 27 } 28 29 func NewMemoryList() ListInterface { 30 return &MemoryList{ 31 itemMaps: map[string]map[string]*Item{}, 32 } 33 } 34 35 func (this *MemoryList) Init() error { 36 this.prefixes = []string{"000"} 37 for i := 100; i <= 999; i++ { 38 this.prefixes = append(this.prefixes, strconv.Itoa(i)) 39 } 40 41 for _, prefix := range this.prefixes { 42 this.itemMaps[prefix] = map[string]*Item{} 43 } 44 45 return nil 46 } 47 48 func (this *MemoryList) Reset() error { 49 this.locker.Lock() 50 for key := range this.itemMaps { 51 this.itemMaps[key] = map[string]*Item{} 52 } 53 this.locker.Unlock() 54 55 atomic.StoreInt64(&this.count, 0) 56 57 return nil 58 } 59 60 func (this *MemoryList) Add(hash string, item *Item) error { 61 this.locker.Lock() 62 63 prefix := this.prefix(hash) 64 itemMap, ok := this.itemMaps[prefix] 65 if !ok { 66 itemMap = map[string]*Item{} 67 this.itemMaps[prefix] = itemMap 68 } 69 70 // 先删除,为了可以正确触发统计 71 oldItem, ok := itemMap[hash] 72 if ok { 73 // 回调 74 if this.onRemove != nil { 75 this.onRemove(oldItem) 76 } 77 } else { 78 atomic.AddInt64(&this.count, 1) 79 } 80 81 // 添加 82 if this.onAdd != nil { 83 this.onAdd(item) 84 } 85 86 itemMap[hash] = item 87 88 this.locker.Unlock() 89 return nil 90 } 91 92 func (this *MemoryList) Exist(hash string) (bool, int64, error) { 93 this.locker.RLock() 94 defer this.locker.RUnlock() 95 96 prefix := this.prefix(hash) 97 itemMap, ok := this.itemMaps[prefix] 98 if !ok { 99 return false, -1, nil 100 } 101 item, ok := itemMap[hash] 102 if !ok { 103 return false, -1, nil 104 } 105 106 return !item.IsExpired(), -1, nil 107 } 108 109 // CleanPrefix 根据前缀进行清除 110 func (this *MemoryList) CleanPrefix(prefix string) error { 111 this.locker.RLock() 112 defer this.locker.RUnlock() 113 114 // TODO 需要优化性能,支持千万级数据低于1s的处理速度 115 for _, itemMap := range this.itemMaps { 116 for _, item := range itemMap { 117 if strings.HasPrefix(item.Key, prefix) { 118 item.ExpiresAt = 0 119 } 120 } 121 } 122 return nil 123 } 124 125 // CleanMatchKey 清理通配符匹配的缓存数据,类似于 https://*.example.com/hello 126 func (this *MemoryList) CleanMatchKey(key string) error { 127 if strings.Contains(key, SuffixAll) { 128 return nil 129 } 130 131 u, err := url.Parse(key) 132 if err != nil { 133 return nil 134 } 135 136 var host = u.Host 137 hostPart, _, err := net.SplitHostPort(host) 138 if err == nil && len(hostPart) > 0 { 139 host = hostPart 140 } 141 142 if len(host) == 0 { 143 return nil 144 } 145 var requestURI = u.RequestURI() 146 147 this.locker.RLock() 148 defer this.locker.RUnlock() 149 150 // TODO 需要优化性能,支持千万级数据低于1s的处理速度 151 for _, itemMap := range this.itemMaps { 152 for _, item := range itemMap { 153 if configutils.MatchDomain(host, item.Host) { 154 var itemRequestURI = item.RequestURI() 155 if itemRequestURI == requestURI || strings.HasPrefix(itemRequestURI, requestURI+SuffixAll) { 156 item.ExpiresAt = 0 157 } 158 } 159 } 160 } 161 162 return nil 163 } 164 165 // CleanMatchPrefix 清理通配符匹配的缓存数据,类似于 https://*.example.com/prefix/ 166 func (this *MemoryList) CleanMatchPrefix(prefix string) error { 167 u, err := url.Parse(prefix) 168 if err != nil { 169 return nil 170 } 171 172 var host = u.Host 173 hostPart, _, err := net.SplitHostPort(host) 174 if err == nil && len(hostPart) > 0 { 175 host = hostPart 176 } 177 if len(host) == 0 { 178 return nil 179 } 180 var requestURI = u.RequestURI() 181 var isRootPath = requestURI == "/" 182 183 this.locker.RLock() 184 defer this.locker.RUnlock() 185 186 // TODO 需要优化性能,支持千万级数据低于1s的处理速度 187 for _, itemMap := range this.itemMaps { 188 for _, item := range itemMap { 189 if configutils.MatchDomain(host, item.Host) { 190 var itemRequestURI = item.RequestURI() 191 if isRootPath || strings.HasPrefix(itemRequestURI, requestURI) { 192 item.ExpiresAt = 0 193 } 194 } 195 } 196 } 197 198 return nil 199 } 200 201 func (this *MemoryList) Remove(hash string) error { 202 this.locker.Lock() 203 204 itemMap, ok := this.itemMaps[this.prefix(hash)] 205 if !ok { 206 this.locker.Unlock() 207 return nil 208 } 209 210 item, ok := itemMap[hash] 211 if ok { 212 if this.onRemove != nil { 213 this.onRemove(item) 214 } 215 216 atomic.AddInt64(&this.count, -1) 217 delete(itemMap, hash) 218 } 219 220 this.locker.Unlock() 221 return nil 222 } 223 224 // Purge 清理过期的缓存 225 // count 每次遍历的最大数量,控制此数字可以保证每次清理的时候不用花太多时间 226 // callback 每次发现过期key的调用 227 func (this *MemoryList) Purge(count int, callback func(hash string) error) (int, error) { 228 this.locker.Lock() 229 var deletedHashList = []string{} 230 231 if this.purgeIndex >= len(this.prefixes) { 232 this.purgeIndex = 0 233 } 234 var prefix = this.prefixes[this.purgeIndex] 235 236 this.purgeIndex++ 237 238 itemMap, ok := this.itemMaps[prefix] 239 if !ok { 240 this.locker.Unlock() 241 return 0, nil 242 } 243 var countFound = 0 244 for hash, item := range itemMap { 245 if count <= 0 { 246 break 247 } 248 249 if item.IsExpired() { 250 if this.onRemove != nil { 251 this.onRemove(item) 252 } 253 254 atomic.AddInt64(&this.count, -1) 255 delete(itemMap, hash) 256 deletedHashList = append(deletedHashList, hash) 257 258 countFound++ 259 } 260 261 count-- 262 } 263 this.locker.Unlock() 264 265 // 执行外部操作 266 for _, hash := range deletedHashList { 267 if callback != nil { 268 err := callback(hash) 269 if err != nil { 270 return 0, err 271 } 272 } 273 } 274 return countFound, nil 275 } 276 277 func (this *MemoryList) PurgeLFU(count int, callback func(hash string) error) error { 278 if count <= 0 { 279 return nil 280 } 281 282 var deletedHashList = []string{} 283 284 var week = currentWeek() 285 var round = 0 286 287 this.locker.Lock() 288 289 Loop: 290 for { 291 var found = false 292 round++ 293 for _, itemMap := range this.itemMaps { 294 for hash, item := range itemMap { 295 found = true 296 297 if week-item.Week <= 1 /** 最近有在使用 **/ && round <= 3 /** 查找轮数过多还不满足数量要求的就不再限制 **/ { 298 continue 299 } 300 301 if this.onRemove != nil { 302 this.onRemove(item) 303 } 304 305 atomic.AddInt64(&this.count, -1) 306 delete(itemMap, hash) 307 deletedHashList = append(deletedHashList, hash) 308 309 count-- 310 if count <= 0 { 311 break Loop 312 } 313 314 break 315 } 316 } 317 if !found { 318 break 319 } 320 } 321 322 this.locker.Unlock() 323 324 // 执行外部操作 325 for _, hash := range deletedHashList { 326 if callback != nil { 327 err := callback(hash) 328 if err != nil { 329 return err 330 } 331 } 332 } 333 334 return nil 335 } 336 337 func (this *MemoryList) CleanAll() error { 338 return this.Reset() 339 } 340 341 func (this *MemoryList) Stat(check func(hash string) bool) (*Stat, error) { 342 this.locker.RLock() 343 defer this.locker.RUnlock() 344 345 result := &Stat{ 346 Count: 0, 347 Size: 0, 348 } 349 for _, itemMap := range this.itemMaps { 350 for hash, item := range itemMap { 351 if !item.IsExpired() { 352 // 检查文件是否存在、内容是否正确等 353 if check != nil && check(hash) { 354 result.Count++ 355 result.ValueSize += item.Size() 356 result.Size += item.TotalSize() 357 } 358 } 359 } 360 } 361 return result, nil 362 } 363 364 // Count 总数量 365 func (this *MemoryList) Count() (int64, error) { 366 var count = atomic.LoadInt64(&this.count) 367 return count, nil 368 } 369 370 // OnAdd 添加事件 371 func (this *MemoryList) OnAdd(f func(item *Item)) { 372 this.onAdd = f 373 } 374 375 // OnRemove 删除事件 376 func (this *MemoryList) OnRemove(f func(item *Item)) { 377 this.onRemove = f 378 } 379 380 func (this *MemoryList) Close() error { 381 return nil 382 } 383 384 // IncreaseHit 增加点击量 385 func (this *MemoryList) IncreaseHit(hash string) error { 386 this.locker.Lock() 387 388 itemMap, ok := this.itemMaps[this.prefix(hash)] 389 if !ok { 390 this.locker.Unlock() 391 return nil 392 } 393 394 item, ok := itemMap[hash] 395 if ok { 396 item.Week = currentWeek() 397 } 398 399 this.locker.Unlock() 400 return nil 401 } 402 403 func (this *MemoryList) Prefixes() []string { 404 return this.prefixes 405 } 406 407 func (this *MemoryList) ItemMaps() map[string]map[string]*Item { 408 return this.itemMaps 409 } 410 411 func (this *MemoryList) PurgeIndex() int { 412 return this.purgeIndex 413 } 414 415 func (this *MemoryList) Print(t *testing.T) { 416 this.locker.Lock() 417 for _, itemMap := range this.itemMaps { 418 if len(itemMap) > 0 { 419 logs.PrintAsJSON(itemMap, t) 420 } 421 } 422 this.locker.Unlock() 423 } 424 425 func (this *MemoryList) prefix(hash string) string { 426 var prefix string 427 if len(hash) > 3 { 428 prefix = hash[:3] 429 } else { 430 prefix = hash 431 } 432 _, ok := this.itemMaps[prefix] 433 if !ok { 434 prefix = "000" 435 } 436 return prefix 437 }