github.com/TeaOSLab/EdgeNode@v1.3.8/internal/caches/storage_memory.go (about) 1 package caches 2 3 import ( 4 "fmt" 5 "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" 6 "github.com/TeaOSLab/EdgeNode/internal/goman" 7 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 8 "github.com/TeaOSLab/EdgeNode/internal/trackers" 9 "github.com/TeaOSLab/EdgeNode/internal/utils" 10 "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" 11 fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs" 12 memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem" 13 setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets" 14 "github.com/TeaOSLab/EdgeNode/internal/zero" 15 "github.com/cespare/xxhash/v2" 16 "github.com/iwind/TeaGo/types" 17 "math" 18 "runtime" 19 "strconv" 20 "strings" 21 "sync" 22 "sync/atomic" 23 "time" 24 ) 25 26 type MemoryItem struct { 27 ExpiresAt int64 28 HeaderValue []byte 29 BodyValue []byte 30 Status int 31 IsDone bool 32 ModifiedAt int64 33 34 IsPrepared bool 35 WriteOffset int64 36 37 isReferring bool // if it is referring by other objects 38 } 39 40 func (this *MemoryItem) IsExpired() bool { 41 return this.ExpiresAt < fasttime.Now().Unix() 42 } 43 44 type MemoryStorage struct { 45 parentStorage StorageInterface 46 47 policy *serverconfigs.HTTPCachePolicy 48 list ListInterface 49 locker *sync.RWMutex 50 51 valuesMap map[uint64]*MemoryItem // hash => item 52 53 dirtyChan chan string // hash chan 54 dirtyQueueSize int 55 56 purgeTicker *utils.Ticker 57 58 usedSize int64 59 totalDirtySize int64 60 61 writingKeyMap map[string]zero.Zero // key => bool 62 63 ignoreKeys *setutils.FixedSet 64 } 65 66 func NewMemoryStorage(policy *serverconfigs.HTTPCachePolicy, parentStorage StorageInterface) *MemoryStorage { 67 var dirtyChan chan string 68 var queueSize = policy.MemoryAutoFlushQueueSize 69 70 if parentStorage != nil { 71 if queueSize <= 0 { 72 queueSize = memutils.SystemMemoryGB() * 100_000 73 } 74 75 dirtyChan = make(chan string, queueSize) 76 } 77 return &MemoryStorage{ 78 parentStorage: parentStorage, 79 policy: policy, 80 list: NewMemoryList(), 81 locker: &sync.RWMutex{}, 82 valuesMap: map[uint64]*MemoryItem{}, 83 dirtyChan: dirtyChan, 84 dirtyQueueSize: queueSize, 85 writingKeyMap: map[string]zero.Zero{}, 86 ignoreKeys: setutils.NewFixedSet(32768), 87 } 88 } 89 90 // Init 初始化 91 func (this *MemoryStorage) Init() error { 92 _ = this.list.Init() 93 94 this.list.OnAdd(func(item *Item) { 95 atomic.AddInt64(&this.usedSize, item.TotalSize()) 96 }) 97 this.list.OnRemove(func(item *Item) { 98 atomic.AddInt64(&this.usedSize, -item.TotalSize()) 99 }) 100 101 this.initPurgeTicker() 102 103 // 启动定时Flush memory to disk任务 104 if this.parentStorage != nil { 105 var threads = 2 106 var numCPU = runtime.NumCPU() 107 if fsutils.DiskIsExtremelyFast() { 108 if numCPU >= 8 { 109 threads = 8 110 } else { 111 threads = 4 112 } 113 } else if fsutils.DiskIsFast() { 114 if numCPU >= 4 { 115 threads = 4 116 } 117 } 118 for i := 0; i < threads; i++ { 119 goman.New(func() { 120 this.startFlush() 121 }) 122 } 123 } 124 125 return nil 126 } 127 128 // OpenReader 读取缓存 129 func (this *MemoryStorage) OpenReader(key string, useStale bool, isPartial bool) (Reader, error) { 130 var hash = this.hash(key) 131 132 // check if exists in list 133 exists, _, _ := this.list.Exist(types.String(hash)) 134 if !exists { 135 return nil, ErrNotFound 136 } 137 138 // read from valuesMap 139 this.locker.RLock() 140 var item = this.valuesMap[hash] 141 142 if item != nil { 143 item.isReferring = true 144 } 145 146 if item == nil || !item.IsDone { 147 this.locker.RUnlock() 148 return nil, ErrNotFound 149 } 150 151 if useStale || (item.ExpiresAt > fasttime.Now().Unix()) { 152 var reader = NewMemoryReader(item) 153 err := reader.Init() 154 if err != nil { 155 this.locker.RUnlock() 156 return nil, err 157 } 158 this.locker.RUnlock() 159 160 return reader, nil 161 } 162 this.locker.RUnlock() 163 164 _ = this.Delete(key) 165 166 return nil, ErrNotFound 167 } 168 169 // OpenWriter 打开缓存写入器等待写入 170 func (this *MemoryStorage) OpenWriter(key string, expiredAt int64, status int, headerSize int, bodySize int64, maxSize int64, isPartial bool) (Writer, error) { 171 if maxSize > 0 && this.ignoreKeys.Has(types.String(maxSize)+"$"+key) { 172 return nil, ErrEntityTooLarge 173 } 174 175 // TODO 内存缓存暂时不支持分块内容存储 176 if isPartial { 177 return nil, fmt.Errorf("%w (004)", ErrFileIsWriting) 178 } 179 return this.openWriter(key, expiredAt, status, headerSize, bodySize, maxSize, true) 180 } 181 182 // OpenFlushWriter 打开从其他媒介直接刷入的写入器 183 func (this *MemoryStorage) OpenFlushWriter(key string, expiresAt int64, status int, headerSize int, bodySize int64) (Writer, error) { 184 return this.openWriter(key, expiresAt, status, headerSize, bodySize, -1, true) 185 } 186 187 func (this *MemoryStorage) openWriter(key string, expiresAt int64, status int, headerSize int, bodySize int64, maxSize int64, isDirty bool) (Writer, error) { 188 // 待写入队列是否已满 189 if isDirty && 190 this.parentStorage != nil && 191 this.dirtyQueueSize > 0 && 192 len(this.dirtyChan) >= this.dirtyQueueSize-64 /** delta **/ { // 缓存时间过长 193 return nil, ErrWritingQueueFull 194 } 195 196 this.locker.Lock() 197 defer this.locker.Unlock() 198 199 // 是否正在写入 200 var isWriting = false 201 _, ok := this.writingKeyMap[key] 202 if ok { 203 return nil, fmt.Errorf("%w (005)", ErrFileIsWriting) 204 } 205 this.writingKeyMap[key] = zero.New() 206 defer func() { 207 if !isWriting { 208 delete(this.writingKeyMap, key) 209 } 210 }() 211 212 // 检查是否过期 213 var hash = this.hash(key) 214 item, ok := this.valuesMap[hash] 215 if ok && !item.IsExpired() { 216 var hashString = types.String(hash) 217 exists, _, _ := this.list.Exist(hashString) 218 if !exists { 219 // remove from values map 220 delete(this.valuesMap, hash) 221 _ = this.list.Remove(hashString) 222 item = nil 223 } else { 224 return nil, fmt.Errorf("%w (006)", ErrFileIsWriting) 225 } 226 } 227 228 // 检查是否超出容量最大值 229 var capacityBytes = this.memoryCapacityBytes() 230 if bodySize < 0 { 231 bodySize = 0 232 } 233 if capacityBytes > 0 && capacityBytes <= atomic.LoadInt64(&this.usedSize)+bodySize { 234 return nil, NewCapacityError("write memory cache failed: over memory size: " + strconv.FormatInt(capacityBytes, 10) + ", current size: " + strconv.FormatInt(this.usedSize, 10) + " bytes") 235 } 236 237 // 先删除 238 err := this.deleteWithoutLocker(key) 239 if err != nil { 240 return nil, err 241 } 242 243 isWriting = true 244 return NewMemoryWriter(this, key, expiresAt, status, isDirty, bodySize, maxSize, func(valueItem *MemoryItem) { 245 this.locker.Lock() 246 delete(this.writingKeyMap, key) 247 this.locker.Unlock() 248 }), nil 249 } 250 251 // Delete 删除某个键值对应的缓存 252 func (this *MemoryStorage) Delete(key string) error { 253 var hash = this.hash(key) 254 this.locker.Lock() 255 delete(this.valuesMap, hash) 256 _ = this.list.Remove(types.String(hash)) 257 this.locker.Unlock() 258 return nil 259 } 260 261 // Stat 统计缓存 262 func (this *MemoryStorage) Stat() (*Stat, error) { 263 this.locker.RLock() 264 defer this.locker.RUnlock() 265 266 return this.list.Stat(func(hash string) bool { 267 return true 268 }) 269 } 270 271 // CleanAll 清除所有缓存 272 func (this *MemoryStorage) CleanAll() error { 273 this.locker.Lock() 274 this.valuesMap = map[uint64]*MemoryItem{} 275 _ = this.list.Reset() 276 atomic.StoreInt64(&this.usedSize, 0) 277 this.locker.Unlock() 278 return nil 279 } 280 281 // Purge 批量删除缓存 282 func (this *MemoryStorage) Purge(keys []string, urlType string) error { 283 // 目录 284 if urlType == "dir" { 285 for _, key := range keys { 286 // 检查是否有通配符 http(s)://*.example.com 287 var schemeIndex = strings.Index(key, "://") 288 if schemeIndex > 0 { 289 var keyRight = key[schemeIndex+3:] 290 if strings.HasPrefix(keyRight, "*.") { 291 err := this.list.CleanMatchPrefix(key) 292 if err != nil { 293 return err 294 } 295 continue 296 } 297 } 298 299 err := this.list.CleanPrefix(key) 300 if err != nil { 301 return err 302 } 303 } 304 return nil 305 } 306 307 // URL 308 for _, key := range keys { 309 // 检查是否有通配符 http(s)://*.example.com 310 var schemeIndex = strings.Index(key, "://") 311 if schemeIndex > 0 { 312 var keyRight = key[schemeIndex+3:] 313 if strings.HasPrefix(keyRight, "*.") { 314 err := this.list.CleanMatchKey(key) 315 if err != nil { 316 return err 317 } 318 continue 319 } 320 } 321 322 err := this.Delete(key) 323 if err != nil { 324 return err 325 } 326 } 327 return nil 328 } 329 330 // Stop 停止缓存策略 331 func (this *MemoryStorage) Stop() { 332 this.locker.Lock() 333 334 this.valuesMap = map[uint64]*MemoryItem{} 335 this.writingKeyMap = map[string]zero.Zero{} 336 _ = this.list.Reset() 337 if this.purgeTicker != nil { 338 this.purgeTicker.Stop() 339 } 340 341 if this.dirtyChan != nil { 342 close(this.dirtyChan) 343 } 344 345 this.usedSize = 0 346 this.totalDirtySize = 0 347 348 _ = this.list.Close() 349 350 this.locker.Unlock() 351 352 this.ignoreKeys.Reset() 353 354 // 回收内存 355 runtime.GC() 356 357 remotelogs.Println("CACHE", "close memory storage '"+strconv.FormatInt(this.policy.Id, 10)+"'") 358 } 359 360 // Policy 获取当前存储的Policy 361 func (this *MemoryStorage) Policy() *serverconfigs.HTTPCachePolicy { 362 return this.policy 363 } 364 365 // UpdatePolicy 修改策略 366 func (this *MemoryStorage) UpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy) { 367 var oldPolicy = this.policy 368 this.policy = newPolicy 369 370 if oldPolicy.MemoryAutoPurgeInterval != newPolicy.MemoryAutoPurgeInterval { 371 this.initPurgeTicker() 372 } 373 374 // 如果是空的,则清空 375 if newPolicy.CapacityBytes() == 0 { 376 _ = this.CleanAll() 377 } 378 379 // reset ignored keys 380 this.ignoreKeys.Reset() 381 } 382 383 // CanUpdatePolicy 检查策略是否可以更新 384 func (this *MemoryStorage) CanUpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy) bool { 385 return newPolicy != nil && newPolicy.Type == serverconfigs.CachePolicyStorageMemory 386 } 387 388 // AddToList 将缓存添加到列表 389 func (this *MemoryStorage) AddToList(item *Item) { 390 // skip added item 391 if item.MetaSize > 0 { 392 return 393 } 394 395 item.MetaSize = int64(len(item.Key)) + 128 /** 128是我们评估的数据结构的长度 **/ 396 var hash = types.String(this.hash(item.Key)) 397 398 if len(item.Host) == 0 { 399 item.Host = ParseHost(item.Key) 400 } 401 402 _ = this.list.Add(hash, item) 403 } 404 405 // TotalDiskSize 消耗的磁盘尺寸 406 func (this *MemoryStorage) TotalDiskSize() int64 { 407 return 0 408 } 409 410 // TotalMemorySize 内存尺寸 411 func (this *MemoryStorage) TotalMemorySize() int64 { 412 return atomic.LoadInt64(&this.usedSize) 413 } 414 415 // IgnoreKey 忽略某个Key,即不缓存某个Key 416 func (this *MemoryStorage) IgnoreKey(key string, maxSize int64) { 417 this.ignoreKeys.Push(types.String(maxSize) + "$" + key) 418 } 419 420 // CanSendfile 是否支持Sendfile 421 func (this *MemoryStorage) CanSendfile() bool { 422 return false 423 } 424 425 // HasFreeSpaceForHotItems 是否有足够的空间提供给热门内容 426 func (this *MemoryStorage) HasFreeSpaceForHotItems() bool { 427 return atomic.LoadInt64(&this.usedSize) < this.memoryCapacityBytes()*3/4 428 } 429 430 // 计算Key Hash 431 func (this *MemoryStorage) hash(key string) uint64 { 432 return xxhash.Sum64String(key) 433 } 434 435 // 清理任务 436 func (this *MemoryStorage) purgeLoop() { 437 // 清理过期 438 var purgeCount = this.policy.MemoryAutoPurgeCount 439 if purgeCount <= 0 { 440 purgeCount = 2000 441 } 442 _, _ = this.list.Purge(purgeCount, func(hash string) error { 443 uintHash, err := strconv.ParseUint(hash, 10, 64) 444 if err == nil { 445 this.locker.Lock() 446 delete(this.valuesMap, uintHash) 447 this.locker.Unlock() 448 } 449 return nil 450 }) 451 452 // LFU 453 // 计算是否应该开启LFU清理 454 var capacityBytes = this.policy.CapacityBytes() 455 var startLFU = false 456 457 var usedPercent = float32(this.TotalMemorySize()*100) / float32(capacityBytes) 458 var lfuFreePercent = this.policy.MemoryLFUFreePercent 459 if lfuFreePercent <= 0 { 460 lfuFreePercent = 5 461 } 462 if capacityBytes > 0 { 463 if lfuFreePercent < 100 { 464 if usedPercent >= 100-lfuFreePercent { 465 startLFU = true 466 } 467 } 468 } 469 470 if startLFU { 471 var total, _ = this.list.Count() 472 if total > 0 { 473 var count = types.Int(math.Ceil(float64(total) * float64(lfuFreePercent*2) / 100)) 474 if count > 0 { 475 // 限制单次清理的条数,防止占用太多系统资源 476 if count > 2000 { 477 count = 2000 478 } 479 480 // 这里不提示LFU,因为此事件将会非常频繁 481 482 err := this.list.PurgeLFU(count, func(hash string) error { 483 uintHash, err := strconv.ParseUint(hash, 10, 64) 484 if err == nil { 485 this.locker.Lock() 486 delete(this.valuesMap, uintHash) 487 this.locker.Unlock() 488 } 489 return nil 490 }) 491 if err != nil { 492 remotelogs.Warn("CACHE", "purge memory storage in LFU failed: "+err.Error()) 493 } 494 } 495 } 496 } 497 } 498 499 // 开始Flush任务 500 func (this *MemoryStorage) startFlush() { 501 var statCount = 0 502 503 for key := range this.dirtyChan { 504 statCount++ 505 506 if statCount == 100 { 507 statCount = 0 508 } 509 510 this.flushItem(key) 511 512 if fsutils.IsInExtremelyHighLoad { 513 time.Sleep(1 * time.Second) 514 } 515 } 516 } 517 518 // 单次Flush任务 519 func (this *MemoryStorage) flushItem(fullKey string) { 520 sizeString, key, found := strings.Cut(fullKey, "@") 521 if !found { 522 return 523 } 524 defer func() { 525 atomic.AddInt64(&this.totalDirtySize, -types.Int64(sizeString)) 526 }() 527 528 if this.parentStorage == nil { 529 return 530 } 531 var hash = this.hash(key) 532 533 this.locker.RLock() 534 item, ok := this.valuesMap[hash] 535 this.locker.RUnlock() 536 537 // 从内存中移除,并确保无论如何都会执行 538 defer func() { 539 _ = this.Delete(key) 540 }() 541 542 if !ok { 543 return 544 } 545 546 if !item.IsDone { 547 remotelogs.Error("CACHE", "flush items failed: open writer failed: item has not been done") 548 return 549 } 550 if item.IsExpired() { 551 return 552 } 553 554 // 检查是否在列表中,防止未加入列表时就开始flush 555 isInList, _, err := this.list.Exist(types.String(hash)) 556 if err != nil { 557 remotelogs.Error("CACHE", "flush items failed: "+err.Error()) 558 return 559 } 560 if !isInList { 561 for i := 0; i < 1000; i++ { 562 isInList, _, err = this.list.Exist(types.String(hash)) 563 if err != nil { 564 remotelogs.Error("CACHE", "flush items failed: "+err.Error()) 565 time.Sleep(1 * time.Second) 566 continue 567 } 568 if isInList { 569 break 570 } 571 time.Sleep(1 * time.Millisecond) 572 } 573 if !isInList { 574 // discard 575 return 576 } 577 } 578 579 writer, err := this.parentStorage.OpenFlushWriter(key, item.ExpiresAt, item.Status, len(item.HeaderValue), int64(len(item.BodyValue))) 580 if err != nil { 581 if !CanIgnoreErr(err) { 582 remotelogs.Error("CACHE", "flush items failed: open writer failed: "+err.Error()) 583 } 584 return 585 } 586 587 _, err = writer.WriteHeader(item.HeaderValue) 588 if err != nil { 589 _ = writer.Discard() 590 remotelogs.Error("CACHE", "flush items failed: write header failed: "+err.Error()) 591 return 592 } 593 594 _, err = writer.Write(item.BodyValue) 595 if err != nil { 596 _ = writer.Discard() 597 remotelogs.Error("CACHE", "flush items failed: writer body failed: "+err.Error()) 598 return 599 } 600 601 err = writer.Close() 602 if err != nil { 603 _ = writer.Discard() 604 remotelogs.Error("CACHE", "flush items failed: close writer failed: "+err.Error()) 605 return 606 } 607 608 this.parentStorage.AddToList(&Item{ 609 Type: writer.ItemType(), 610 Key: key, 611 Host: ParseHost(key), 612 ExpiresAt: item.ExpiresAt, 613 HeaderSize: writer.HeaderSize(), 614 BodySize: writer.BodySize(), 615 }) 616 } 617 618 func (this *MemoryStorage) memoryCapacityBytes() int64 { 619 var maxSystemBytes = SharedManager.MaxSystemMemoryBytesPerStorage() 620 if this.policy == nil { 621 return maxSystemBytes 622 } 623 624 if SharedManager.MaxMemoryCapacity != nil { 625 var capacityBytes = SharedManager.MaxMemoryCapacity.Bytes() 626 if capacityBytes > 0 { 627 if capacityBytes > maxSystemBytes { 628 return maxSystemBytes 629 } 630 631 return capacityBytes 632 } 633 } 634 635 var capacity = this.policy.Capacity // copy 636 if capacity != nil { 637 var capacityBytes = capacity.Bytes() 638 if capacityBytes > 0 { 639 if capacityBytes > maxSystemBytes { 640 return maxSystemBytes 641 } 642 return capacityBytes 643 } 644 } 645 646 return maxSystemBytes 647 } 648 649 func (this *MemoryStorage) deleteWithoutLocker(key string) error { 650 hash := this.hash(key) 651 delete(this.valuesMap, hash) 652 _ = this.list.Remove(types.String(hash)) 653 return nil 654 } 655 656 func (this *MemoryStorage) initPurgeTicker() { 657 var autoPurgeInterval = this.policy.MemoryAutoPurgeInterval 658 if autoPurgeInterval <= 0 { 659 autoPurgeInterval = 5 660 } 661 662 // 启动定时清理任务 663 664 if this.purgeTicker != nil { 665 this.purgeTicker.Stop() 666 } 667 668 this.purgeTicker = utils.NewTicker(time.Duration(autoPurgeInterval) * time.Second) 669 goman.New(func() { 670 for this.purgeTicker.Next() { 671 var tr = trackers.Begin("MEMORY_CACHE_STORAGE_PURGE_LOOP") 672 this.purgeLoop() 673 tr.End() 674 } 675 }) 676 }