github.com/TeaOSLab/EdgeNode@v1.3.8/internal/caches/storage_file.go (about) 1 package caches 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" 10 "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" 11 teaconst "github.com/TeaOSLab/EdgeNode/internal/const" 12 "github.com/TeaOSLab/EdgeNode/internal/events" 13 "github.com/TeaOSLab/EdgeNode/internal/goman" 14 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 15 "github.com/TeaOSLab/EdgeNode/internal/trackers" 16 "github.com/TeaOSLab/EdgeNode/internal/utils" 17 "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" 18 fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs" 19 memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem" 20 setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets" 21 "github.com/TeaOSLab/EdgeNode/internal/zero" 22 "github.com/iwind/TeaGo/Tea" 23 "github.com/iwind/TeaGo/rands" 24 "github.com/iwind/TeaGo/types" 25 stringutil "github.com/iwind/TeaGo/utils/string" 26 timeutil "github.com/iwind/TeaGo/utils/time" 27 "github.com/iwind/gosock/pkg/gosock" 28 "github.com/shirou/gopsutil/v3/load" 29 "math" 30 "os" 31 "path/filepath" 32 "regexp" 33 "sort" 34 "strconv" 35 "strings" 36 "sync" 37 "syscall" 38 "time" 39 ) 40 41 const ( 42 SizeExpiresAt = 4 43 OffsetExpiresAt = 0 44 SizeStatus = 3 45 OffsetStatus = SizeExpiresAt 46 SizeURLLength = 4 47 OffsetURLLength = OffsetStatus + SizeStatus 48 SizeHeaderLength = 4 49 OffsetHeaderLength = OffsetURLLength + SizeURLLength 50 SizeBodyLength = 8 51 OffsetBodyLength = OffsetHeaderLength + SizeHeaderLength 52 53 SizeMeta = SizeExpiresAt + SizeStatus + SizeURLLength + SizeHeaderLength + SizeBodyLength 54 ) 55 56 const ( 57 FileStorageMaxIgnoreKeys = 32768 // 最大可忽略的键值数(尺寸过大的键值) 58 HotItemSize = 1024 // 热点数据数量 59 HotItemLifeSeconds int64 = 3600 // 热点数据生命周期 60 FileTmpSuffix = ".tmp" 61 DefaultMinDiskFreeSpace uint64 = 5 << 30 // 当前磁盘最小剩余空间 62 DefaultStaleCacheSeconds = 1200 // 过时缓存留存时间 63 HashKeyLength = 32 64 ) 65 66 var FileToMemoryMaxSize int64 = 32 << 20 // 可以从文件写入到内存的最大文件尺寸 67 68 func init() { 69 var availableMemoryGB = memutils.AvailableMemoryGB() 70 if availableMemoryGB > 64 { 71 FileToMemoryMaxSize = 512 << 20 72 } else if availableMemoryGB > 32 { 73 FileToMemoryMaxSize = 256 << 20 74 } else if availableMemoryGB > 16 { 75 FileToMemoryMaxSize = 128 << 20 76 } else if availableMemoryGB > 8 { 77 FileToMemoryMaxSize = 64 << 20 78 } 79 } 80 81 var sharedWritingFileKeyMap = map[string]zero.Zero{} // key => bool 82 var sharedWritingFileKeyLocker = sync.Mutex{} 83 84 // FileStorage 文件缓存 85 // 86 // 文件结构: 87 // [expires time] | [ status ] | [url length] | [header length] | [body length] | [url] [header data] [body data] 88 type FileStorage struct { 89 policy *serverconfigs.HTTPCachePolicy 90 options *serverconfigs.HTTPFileCacheStorage // 二级缓存 91 memoryStorage *MemoryStorage // 一级缓存 92 93 list ListInterface 94 locker sync.RWMutex 95 purgeTicker *utils.Ticker 96 97 hotMap map[string]*HotItem // key => count 98 hotMapLocker sync.Mutex 99 lastHotSize int 100 hotTicker *utils.Ticker 101 102 ignoreKeys *setutils.FixedSet 103 104 openFileCache *OpenFileCache 105 106 mainDiskIsFull bool 107 mainDiskTotalSize uint64 108 109 subDirs []*FileDir 110 } 111 112 func NewFileStorage(policy *serverconfigs.HTTPCachePolicy) *FileStorage { 113 return &FileStorage{ 114 policy: policy, 115 hotMap: map[string]*HotItem{}, 116 lastHotSize: -1, 117 ignoreKeys: setutils.NewFixedSet(FileStorageMaxIgnoreKeys), 118 } 119 } 120 121 // Policy 获取当前的Policy 122 func (this *FileStorage) Policy() *serverconfigs.HTTPCachePolicy { 123 return this.policy 124 } 125 126 // CanUpdatePolicy 检查策略是否可以更新 127 func (this *FileStorage) CanUpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy) bool { 128 if newPolicy == nil { 129 return false 130 } 131 132 // 检查类型 133 if newPolicy.Type != serverconfigs.CachePolicyStorageFile { 134 return false 135 } 136 137 // 检查路径是否有变化 138 oldOptionsJSON, err := json.Marshal(this.policy.Options) 139 if err != nil { 140 return false 141 } 142 var oldOptions = serverconfigs.NewHTTPFileCacheStorage() 143 err = json.Unmarshal(oldOptionsJSON, oldOptions) 144 if err != nil { 145 return false 146 } 147 148 newOptionsJSON, err := json.Marshal(newPolicy.Options) 149 if err != nil { 150 return false 151 } 152 var newOptions = serverconfigs.NewHTTPFileCacheStorage() 153 err = json.Unmarshal(newOptionsJSON, newOptions) 154 if err != nil { 155 return false 156 } 157 158 if oldOptions.Dir == newOptions.Dir { 159 return true 160 } 161 162 return false 163 } 164 165 // UpdatePolicy 修改策略 166 func (this *FileStorage) UpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy) { 167 var oldOpenFileCache = this.options.OpenFileCache 168 169 this.policy = newPolicy 170 171 newOptionsJSON, err := json.Marshal(newPolicy.Options) 172 if err != nil { 173 return 174 } 175 var newOptions = serverconfigs.NewHTTPFileCacheStorage() 176 err = json.Unmarshal(newOptionsJSON, newOptions) 177 if err != nil { 178 remotelogs.Error("CACHE", "update policy '"+types.String(this.policy.Id)+"' failed: decode options failed: "+err.Error()) 179 return 180 } 181 182 var subDirs = []*FileDir{} 183 for _, subDir := range newOptions.SubDirs { 184 subDirs = append(subDirs, &FileDir{ 185 Path: subDir.Path, 186 Capacity: subDir.Capacity, 187 IsFull: false, 188 }) 189 } 190 this.subDirs = subDirs 191 this.checkDiskSpace() 192 193 err = newOptions.Init() 194 if err != nil { 195 remotelogs.Error("CACHE", "update policy '"+types.String(this.policy.Id)+"' failed: init options failed: "+err.Error()) 196 return 197 } 198 199 this.options = newOptions 200 201 var memoryStorage = this.memoryStorage 202 if memoryStorage != nil { 203 if newOptions.MemoryPolicy != nil && newOptions.MemoryPolicy.CapacityBytes() > 0 { 204 memoryStorage.UpdatePolicy(newOptions.MemoryPolicy) 205 } else { 206 memoryStorage.Stop() 207 this.memoryStorage = nil 208 } 209 } else if newOptions.MemoryPolicy != nil && this.options.MemoryPolicy.Capacity != nil && this.options.MemoryPolicy.Capacity.Count > 0 { 210 err = this.createMemoryStorage() 211 if err != nil { 212 remotelogs.Error("CACHE", "update policy '"+types.String(this.policy.Id)+"' failed: create memory storage failed: "+err.Error()) 213 } 214 } 215 216 // open cache 217 oldOpenFileCacheJSON, _ := json.Marshal(oldOpenFileCache) 218 newOpenFileCacheJSON, _ := json.Marshal(this.options.OpenFileCache) 219 if !bytes.Equal(oldOpenFileCacheJSON, newOpenFileCacheJSON) { 220 this.initOpenFileCache() 221 } 222 223 // Purge Ticker 224 if newPolicy.PersistenceAutoPurgeInterval != this.policy.PersistenceAutoPurgeInterval { 225 this.initPurgeTicker() 226 } 227 228 // reset ignored keys 229 this.ignoreKeys.Reset() 230 } 231 232 // Init 初始化 233 func (this *FileStorage) Init() error { 234 this.locker.Lock() 235 defer this.locker.Unlock() 236 237 var before = time.Now() 238 239 // 配置 240 var options = serverconfigs.NewHTTPFileCacheStorage() 241 optionsJSON, err := json.Marshal(this.policy.Options) 242 if err != nil { 243 return err 244 } 245 err = json.Unmarshal(optionsJSON, options) 246 if err != nil { 247 return err 248 } 249 this.options = options 250 251 if !filepath.IsAbs(this.options.Dir) { 252 this.options.Dir = Tea.Root + Tea.DS + this.options.Dir 253 } 254 255 this.options.Dir = filepath.Clean(this.options.Dir) 256 var dir = this.options.Dir 257 258 var subDirs = []*FileDir{} 259 for _, subDir := range this.options.SubDirs { 260 subDirs = append(subDirs, &FileDir{ 261 Path: subDir.Path, 262 Capacity: subDir.Capacity, 263 IsFull: false, 264 }) 265 } 266 this.subDirs = subDirs 267 if len(subDirs) > 0 { 268 this.checkDiskSpace() 269 } 270 271 if len(dir) == 0 { 272 return errors.New("[CACHE]cache storage dir can not be empty") 273 } 274 275 // read list 276 var list ListInterface 277 var sqliteIndexesDir = dir + "/p" + types.String(this.policy.Id) + "/.indexes" 278 _, sqliteIndexesDirErr := os.Stat(sqliteIndexesDir) 279 if sqliteIndexesDirErr == nil || !teaconst.EnableKVCacheStore { 280 list = NewSQLiteFileList(sqliteIndexesDir) 281 err = list.Init() 282 if err != nil { 283 return err 284 } 285 list.(*SQLiteFileList).SetOldDir(dir + "/p" + types.String(this.policy.Id)) 286 } else { 287 list = NewKVFileList(dir + "/p" + types.String(this.policy.Id) + "/.stores") 288 err = list.Init() 289 if err != nil { 290 return err 291 } 292 } 293 294 this.list = list 295 296 // 检查目录是否存在 297 _, err = os.Stat(dir) 298 if err != nil { 299 if !os.IsNotExist(err) { 300 return err 301 } else { 302 err = os.MkdirAll(dir, 0777) 303 if err != nil { 304 return fmt.Errorf("[CACHE]can not create dir: %w", err) 305 } 306 } 307 } 308 309 defer func() { 310 // 统计 311 var totalSize = this.TotalDiskSize() 312 var cost = time.Since(before).Seconds() * 1000 313 var sizeMB = types.String(totalSize) + " Bytes" 314 if totalSize > (1 << 30) { 315 sizeMB = fmt.Sprintf("%.3f GiB", float64(totalSize)/(1<<30)) 316 } else if totalSize > (1 << 20) { 317 sizeMB = fmt.Sprintf("%.3f MiB", float64(totalSize)/(1<<20)) 318 } else if totalSize > (1 << 10) { 319 sizeMB = fmt.Sprintf("%.3f KiB", float64(totalSize)/(1<<10)) 320 } 321 322 var mmapTag = "disabled" 323 if this.options.EnableMMAP { 324 mmapTag = "enabled" 325 } 326 remotelogs.Println("CACHE", "init policy "+types.String(this.policy.Id)+" from '"+this.options.Dir+"', cost: "+fmt.Sprintf("%.2f", cost)+" ms, size: "+sizeMB+", mmap: "+mmapTag) 327 }() 328 329 // 初始化list 330 err = this.initList() 331 if err != nil { 332 return err 333 } 334 335 // 加载内存缓存 336 if this.options.MemoryPolicy != nil && this.options.MemoryPolicy.Capacity != nil && this.options.MemoryPolicy.Capacity.Count > 0 { 337 err = this.createMemoryStorage() 338 if err != nil { 339 return err 340 } 341 } 342 343 // open file cache 344 this.initOpenFileCache() 345 346 // 检查磁盘空间 347 this.checkDiskSpace() 348 349 // clean *.trash directories 350 this.cleanAllDeletedDirs() 351 352 return nil 353 } 354 355 func (this *FileStorage) OpenReader(key string, useStale bool, isPartial bool) (Reader, error) { 356 return this.openReader(key, true, useStale, isPartial) 357 } 358 359 func (this *FileStorage) openReader(key string, allowMemory bool, useStale bool, isPartial bool) (Reader, error) { 360 // 使用陈旧缓存的时候,我们认为是短暂的,只需要从文件里检查即可 361 if useStale { 362 allowMemory = false 363 } 364 365 // 区间缓存只存在文件中 366 if isPartial { 367 allowMemory = false 368 } 369 370 // 先尝试内存缓存 371 var memoryStorage = this.memoryStorage 372 if allowMemory && memoryStorage != nil { 373 reader, err := memoryStorage.OpenReader(key, useStale, isPartial) 374 if err == nil { 375 return reader, err 376 } 377 } 378 379 hash, path, _ := this.keyPath(key) 380 381 // 检查文件记录是否已过期 382 var estimatedSize int64 383 var existInList bool 384 if !useStale { 385 exists, filesize, err := this.list.Exist(hash) 386 if err != nil { 387 return nil, err 388 } 389 if !exists { 390 return nil, ErrNotFound 391 } 392 estimatedSize = filesize 393 existInList = true 394 } 395 396 // 尝试通过MMAP读取 397 if estimatedSize > 0 { 398 reader, err := this.tryMMAPReader(isPartial, estimatedSize, path) 399 if err != nil { 400 return nil, err 401 } 402 if reader != nil { 403 return reader, nil 404 } 405 } 406 407 var isOk = false 408 var openFile *OpenFile 409 var openFileCache = this.openFileCache // 因为中间可能有修改,所以先赋值再获取 410 if openFileCache != nil { 411 openFile = openFileCache.Get(path) 412 } 413 var fp *os.File 414 415 var err error 416 if openFile == nil { 417 if existInList { 418 fsutils.ReaderLimiter.Ack() 419 } 420 fp, err = os.OpenFile(path, os.O_RDONLY, 0444) 421 if existInList { 422 fsutils.ReaderLimiter.Release() 423 } 424 if err != nil { 425 if !os.IsNotExist(err) { 426 return nil, err 427 } 428 return nil, ErrNotFound 429 } 430 } else { 431 fp = openFile.fp 432 } 433 defer func() { 434 if !isOk { 435 _ = fp.Close() 436 _ = this.removeCacheFile(path) 437 } 438 }() 439 440 var reader Reader 441 if isPartial { 442 var partialFileReader = NewPartialFileReader(fsutils.NewFile(fp, fsutils.FlagRead)) 443 partialFileReader.openFile = openFile 444 partialFileReader.openFileCache = openFileCache 445 reader = partialFileReader 446 } else { 447 var fileReader = NewFileReader(fsutils.NewFile(fp, fsutils.FlagRead)) 448 fileReader.openFile = openFile 449 fileReader.openFileCache = openFileCache 450 reader = fileReader 451 } 452 453 err = reader.Init() 454 if err != nil { 455 return nil, err 456 } 457 458 // 增加点击量 459 // 1/1000采样 460 if !isPartial && allowMemory && reader.BodySize() < FileToMemoryMaxSize { 461 this.increaseHit(key, hash, reader) 462 } 463 464 isOk = true 465 return reader, nil 466 } 467 468 // OpenWriter 打开缓存文件等待写入 469 func (this *FileStorage) OpenWriter(key string, expiresAt int64, status int, headerSize int, bodySize int64, maxSize int64, isPartial bool) (Writer, error) { 470 return this.openWriter(key, expiresAt, status, headerSize, bodySize, maxSize, isPartial, false) 471 } 472 473 // OpenFlushWriter 打开从其他媒介直接刷入的写入器 474 func (this *FileStorage) OpenFlushWriter(key string, expiresAt int64, status int, headerSize int, bodySize int64) (Writer, error) { 475 return this.openWriter(key, expiresAt, status, headerSize, bodySize, -1, false, true) 476 } 477 478 func (this *FileStorage) openWriter(key string, expiredAt int64, status int, headerSize int, bodySize int64, maxSize int64, isPartial bool, isFlushing bool) (Writer, error) { 479 // 是否正在退出 480 if teaconst.IsQuiting { 481 return nil, ErrWritingUnavailable 482 } 483 484 // 是否已忽略 485 if maxSize > 0 && this.ignoreKeys.Has(types.String(maxSize)+"$"+key) { 486 return nil, ErrEntityTooLarge 487 } 488 489 // 检查磁盘是否超出容量 490 // 需要在内存缓存之前执行,避免成功写进到了内存缓存,但无法刷到磁盘 491 var capacityBytes = this.diskCapacityBytes() 492 if capacityBytes > 0 && capacityBytes <= this.TotalDiskSize()+(32<<20 /** 余量 **/) { 493 return nil, NewCapacityError("write file cache failed: over disk size, current: " + types.String(this.TotalDiskSize()) + ", capacity: " + types.String(capacityBytes)) 494 } 495 496 // 先尝试内存缓存 497 // 我们限定仅小文件优先存在内存中 498 var maxMemorySize = FileToMemoryMaxSize 499 if maxSize > 0 && maxSize < maxMemorySize { 500 maxMemorySize = maxSize 501 } 502 var memoryStorage = this.memoryStorage 503 if !isFlushing && !isPartial && memoryStorage != nil && ((bodySize > 0 && bodySize < maxMemorySize) || bodySize < 0) { 504 writer, err := memoryStorage.OpenWriter(key, expiredAt, status, headerSize, bodySize, maxMemorySize, false) 505 if err == nil { 506 return writer, nil 507 } 508 509 // 如果队列满了,则等待 510 if errors.Is(err, ErrWritingQueueFull) { 511 return nil, err 512 } 513 514 if IsCapacityError(err) && bodySize > 0 && memoryStorage.totalDirtySize > (128<<20) { 515 return nil, err 516 } 517 } 518 519 // 是否正在写入 520 var isOk = false 521 sharedWritingFileKeyLocker.Lock() 522 _, ok := sharedWritingFileKeyMap[key] 523 if ok { 524 sharedWritingFileKeyLocker.Unlock() 525 return nil, fmt.Errorf("%w(001)", ErrFileIsWriting) 526 } 527 528 sharedWritingFileKeyMap[key] = zero.New() 529 sharedWritingFileKeyLocker.Unlock() 530 defer func() { 531 if !isOk { 532 sharedWritingFileKeyLocker.Lock() 533 delete(sharedWritingFileKeyMap, key) 534 sharedWritingFileKeyLocker.Unlock() 535 } 536 }() 537 538 var hash = stringutil.Md5(key) 539 540 dir, diskIsFull := this.subDir(hash) 541 if diskIsFull { 542 return nil, NewCapacityError("the disk is full") 543 } 544 545 // 检查缓存是否已经生成 546 var cachePathName = dir + "/" + hash 547 var cachePath = cachePathName + ".cache" 548 549 // 关闭OpenFileCache 550 var openFileCache = this.openFileCache 551 if openFileCache != nil { 552 openFileCache.Close(cachePath) 553 } 554 555 // 查询当前已有缓存文件 556 stat, err := os.Stat(cachePath) 557 558 // 检查两次写入缓存的时间是否过于相近,分片内容不受此限制 559 if err == nil && !isPartial && time.Since(stat.ModTime()) <= 1*time.Second { 560 // 防止并发连续写入 561 return nil, fmt.Errorf("%w(002)", ErrFileIsWriting) 562 } 563 564 // 构造文件名 565 var tmpPath = cachePath 566 var existsFile = false 567 if stat != nil { 568 existsFile = true 569 570 // 如果已经存在,则增加一个.tmp后缀,防止读写冲突 571 tmpPath += FileTmpSuffix 572 } 573 574 if isPartial { 575 tmpPath = cachePathName + ".cache" 576 } 577 578 // 先删除 579 if !isPartial { 580 err = this.list.Remove(hash) 581 if err != nil { 582 return nil, err 583 } 584 } 585 586 // 从已经存储的内容中读取信息 587 var isNewCreated = true 588 var partialBodyOffset int64 589 var partialRanges *PartialRanges 590 if isPartial { 591 // 数据库中是否存在 592 existsCacheItem, _, _ := this.list.Exist(hash) 593 if existsCacheItem { 594 readerFp, err := fsutils.OpenFile(tmpPath, os.O_RDONLY, 0444) 595 if err == nil { 596 var partialReader = NewPartialFileReader(fsutils.NewFile(readerFp, fsutils.FlagRead)) 597 err = partialReader.Init() 598 _ = partialReader.Close() 599 if err == nil && partialReader.bodyOffset > 0 { 600 partialRanges = partialReader.Ranges() 601 if bodySize > 0 && partialRanges != nil && partialRanges.BodySize > 0 && bodySize != partialRanges.BodySize { 602 _ = this.removeCacheFile(tmpPath) 603 } else { 604 isNewCreated = false 605 partialBodyOffset = partialReader.bodyOffset 606 } 607 } else { 608 _ = this.removeCacheFile(tmpPath) 609 } 610 } 611 } 612 if isNewCreated { 613 err = this.list.Remove(hash) 614 if err != nil { 615 return nil, err 616 } 617 } 618 if partialRanges == nil { 619 partialRanges = NewPartialRanges(expiredAt) 620 } 621 } 622 623 var flags = os.O_CREATE | os.O_WRONLY 624 if isNewCreated && existsFile { 625 flags |= os.O_TRUNC 626 } 627 if !isFlushing { 628 if !fsutils.WriterLimiter.TryAck() { 629 return nil, ErrServerIsBusy 630 } 631 } 632 fp, err := os.OpenFile(tmpPath, flags, 0666) 633 if !isFlushing { 634 fsutils.WriterLimiter.Release() 635 } 636 if err != nil { 637 if os.IsNotExist(err) { 638 _ = os.MkdirAll(dir, 0777) 639 640 // open file again 641 fsutils.WriterLimiter.Ack() 642 fp, err = os.OpenFile(tmpPath, flags, 0666) 643 fsutils.WriterLimiter.Release() 644 } 645 if err != nil { 646 return nil, err 647 } 648 } 649 650 var writer = fsutils.NewFile(fp, fsutils.FlagWrite) 651 652 var removeOnFailure = true 653 defer func() { 654 if err != nil { 655 isOk = false 656 } 657 658 // 如果出错了,就删除文件,避免写一半 659 if !isOk { 660 _ = writer.Close() 661 if removeOnFailure { 662 _ = fsutils.Remove(tmpPath) 663 } 664 } 665 }() 666 667 // 尝试锁定,如果锁定失败,则直接返回 668 fsutils.WriterLimiter.Ack() 669 err = syscall.Flock(int(writer.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) 670 fsutils.WriterLimiter.Release() 671 if err != nil { 672 removeOnFailure = false 673 return nil, fmt.Errorf("%w (003)", ErrFileIsWriting) 674 } 675 676 // 关闭 677 if openFileCache != nil { 678 openFileCache.Close(cachePath) 679 } 680 681 var metaBodySize int64 = -1 682 var metaHeaderSize = -1 683 if isNewCreated { 684 // 写入meta 685 // 从v0.5.8开始不再在meta中写入Key 686 var metaBytes = make([]byte, SizeMeta) 687 binary.BigEndian.PutUint32(metaBytes[OffsetExpiresAt:], uint32(expiredAt)) 688 689 // 写入状态码 690 if status > 999 || status < 100 { 691 status = 200 692 } 693 copy(metaBytes[OffsetStatus:], strconv.Itoa(status)) 694 695 // 写入Header Length 696 if headerSize > 0 { 697 binary.BigEndian.PutUint32(metaBytes[OffsetHeaderLength:], uint32(headerSize)) 698 metaHeaderSize = headerSize 699 } 700 701 // 写入Body Length 702 if bodySize > 0 { 703 binary.BigEndian.PutUint64(metaBytes[OffsetBodyLength:], uint64(bodySize)) 704 metaBodySize = bodySize 705 } 706 707 _, err = writer.Write(metaBytes) 708 if err != nil { 709 return nil, err 710 } 711 } 712 713 isOk = true 714 if isPartial { 715 return NewPartialFileWriter(writer, key, expiredAt, metaHeaderSize, metaBodySize, isNewCreated, isPartial, partialBodyOffset, partialRanges, func() { 716 sharedWritingFileKeyLocker.Lock() 717 delete(sharedWritingFileKeyMap, key) 718 sharedWritingFileKeyLocker.Unlock() 719 }), nil 720 } else { 721 return NewFileWriter(this, writer, key, expiredAt, metaHeaderSize, metaBodySize, maxSize, func() { 722 sharedWritingFileKeyLocker.Lock() 723 delete(sharedWritingFileKeyMap, key) 724 sharedWritingFileKeyLocker.Unlock() 725 }), nil 726 } 727 } 728 729 // AddToList 添加到List 730 func (this *FileStorage) AddToList(item *Item) { 731 // 是否正在退出 732 if teaconst.IsQuiting { 733 return 734 } 735 736 var memoryStorage = this.memoryStorage 737 if memoryStorage != nil { 738 if item.Type == ItemTypeMemory { 739 memoryStorage.AddToList(item) 740 return 741 } 742 } 743 744 item.MetaSize = SizeMeta + 128 745 var hash = stringutil.Md5(item.Key) 746 err := this.list.Add(hash, item) 747 if err != nil && !strings.Contains(err.Error(), "UNIQUE constraint failed") { 748 remotelogs.Error("CACHE", "add to list failed: "+err.Error()) 749 } 750 } 751 752 // Delete 删除某个键值对应的缓存 753 func (this *FileStorage) Delete(key string) error { 754 // 是否正在退出 755 if teaconst.IsQuiting { 756 return nil 757 } 758 759 // 先尝试内存缓存 760 this.runMemoryStorageSafety(func(memoryStorage *MemoryStorage) { 761 _ = memoryStorage.Delete(key) 762 }) 763 764 hash, path, _ := this.keyPath(key) 765 err := this.list.Remove(hash) 766 if err != nil { 767 return err 768 } 769 err = this.removeCacheFile(path) 770 if err == nil || os.IsNotExist(err) { 771 return nil 772 } 773 774 return err 775 } 776 777 // Stat 统计 778 func (this *FileStorage) Stat() (*Stat, error) { 779 return this.list.Stat(func(hash string) bool { 780 return true 781 }) 782 } 783 784 // CleanAll 清除所有的缓存 785 func (this *FileStorage) CleanAll() error { 786 this.locker.Lock() 787 defer this.locker.Unlock() 788 789 // 先尝试内存缓存 790 this.runMemoryStorageSafety(func(memoryStorage *MemoryStorage) { 791 _ = memoryStorage.CleanAll() 792 }) 793 794 err := this.list.CleanAll() 795 if err != nil { 796 return err 797 } 798 799 // 删除缓存和目录 800 // 不能直接删除子目录,比较危险 801 802 var rootDirs = []string{this.options.Dir} 803 var subDirs = this.subDirs // copy slice 804 if len(subDirs) > 0 { 805 for _, subDir := range subDirs { 806 rootDirs = append(rootDirs, subDir.Path) 807 } 808 } 809 810 var dirNameReg = regexp.MustCompile(`^[0-9a-f]{2}$`) 811 for _, rootDir := range rootDirs { 812 var dir = rootDir + "/p" + types.String(this.policy.Id) 813 err = func(dir string) error { 814 fp, err := os.Open(dir) 815 if err != nil { 816 return err 817 } 818 defer func() { 819 _ = fp.Close() 820 }() 821 822 stat, err := fp.Stat() 823 if err != nil { 824 return err 825 } 826 827 if !stat.IsDir() { 828 return nil 829 } 830 831 // 改成待删除 832 subDirs, err := fp.Readdir(-1) 833 if err != nil { 834 return err 835 } 836 for _, info := range subDirs { 837 var subDir = info.Name() 838 839 // 检查目录名 840 if !dirNameReg.MatchString(subDir) { 841 continue 842 } 843 844 // 修改目录名 845 var tmpDir = dir + "/" + subDir + "." + timeutil.Format("YmdHis") + ".trash" 846 err = os.Rename(dir+"/"+subDir, tmpDir) 847 if err != nil { 848 return err 849 } 850 } 851 852 // 重新遍历待删除 853 goman.New(func() { 854 err = this.cleanDeletedDirs(dir) 855 if err != nil { 856 remotelogs.Warn("CACHE", "delete '*.trash' dirs failed: "+err.Error()) 857 } else { 858 // try to clean again, to delete writing files when deleting 859 time.Sleep(10 * time.Minute) 860 _ = this.cleanDeletedDirs(dir) 861 } 862 }) 863 864 return nil 865 }(dir) 866 if err != nil { 867 return err 868 } 869 } 870 871 return nil 872 } 873 874 // Purge 清理过期的缓存 875 func (this *FileStorage) Purge(keys []string, urlType string) error { 876 // 是否正在退出 877 if teaconst.IsQuiting { 878 return nil 879 } 880 881 // 先尝试内存缓存 882 this.runMemoryStorageSafety(func(memoryStorage *MemoryStorage) { 883 _ = memoryStorage.Purge(keys, urlType) 884 }) 885 886 // 目录 887 if urlType == "dir" { 888 for _, key := range keys { 889 // 检查是否有通配符 http(s)://*.example.com 890 var schemeIndex = strings.Index(key, "://") 891 if schemeIndex > 0 { 892 var keyRight = key[schemeIndex+3:] 893 if strings.HasPrefix(keyRight, "*.") { 894 err := this.list.CleanMatchPrefix(key) 895 if err != nil { 896 return err 897 } 898 continue 899 } 900 } 901 902 err := this.list.CleanPrefix(key) 903 if err != nil { 904 return err 905 } 906 } 907 return nil 908 } 909 910 // URL 911 for _, key := range keys { 912 // 检查是否有通配符 http(s)://*.example.com 913 var schemeIndex = strings.Index(key, "://") 914 if schemeIndex > 0 { 915 var keyRight = key[schemeIndex+3:] 916 if strings.HasPrefix(keyRight, "*.") { 917 err := this.list.CleanMatchKey(key) 918 if err != nil { 919 return err 920 } 921 continue 922 } 923 } 924 925 // 普通的Key 926 hash, path, _ := this.keyPath(key) 927 err := this.removeCacheFile(path) 928 if err != nil && !os.IsNotExist(err) { 929 return err 930 } 931 932 err = this.list.Remove(hash) 933 if err != nil { 934 return err 935 } 936 } 937 return nil 938 } 939 940 // Stop 停止 941 func (this *FileStorage) Stop() { 942 events.Remove(this) 943 944 this.locker.Lock() 945 defer this.locker.Unlock() 946 947 // 先尝试内存缓存 948 this.runMemoryStorageSafety(func(memoryStorage *MemoryStorage) { 949 memoryStorage.Stop() 950 }) 951 952 if this.list != nil { 953 _ = this.list.Reset() 954 } 955 956 if this.purgeTicker != nil { 957 this.purgeTicker.Stop() 958 } 959 if this.hotTicker != nil { 960 this.hotTicker.Stop() 961 } 962 963 if this.list != nil { 964 _ = this.list.Close() 965 } 966 967 var openFileCache = this.openFileCache 968 if openFileCache != nil { 969 openFileCache.CloseAll() 970 } 971 972 this.ignoreKeys.Reset() 973 } 974 975 // TotalDiskSize 消耗的磁盘尺寸 976 func (this *FileStorage) TotalDiskSize() int64 { 977 stat, err := fsutils.StatDeviceCache(this.options.Dir) 978 if err == nil { 979 return int64(stat.UsedSize()) 980 } 981 return 0 982 } 983 984 // TotalMemorySize 内存尺寸 985 func (this *FileStorage) TotalMemorySize() int64 { 986 var memoryStorage = this.memoryStorage 987 if memoryStorage == nil { 988 return 0 989 } 990 return memoryStorage.TotalMemorySize() 991 } 992 993 // IgnoreKey 忽略某个Key,即不缓存某个Key 994 func (this *FileStorage) IgnoreKey(key string, maxSize int64) { 995 this.ignoreKeys.Push(types.String(maxSize) + "$" + key) 996 } 997 998 // CanSendfile 是否支持Sendfile 999 func (this *FileStorage) CanSendfile() bool { 1000 if this.options == nil { 1001 return false 1002 } 1003 return this.options.EnableSendfile 1004 } 1005 1006 // 获取Key对应的文件路径 1007 func (this *FileStorage) keyPath(key string) (hash string, path string, diskIsFull bool) { 1008 hash = stringutil.Md5(key) 1009 var dir string 1010 dir, diskIsFull = this.subDir(hash) 1011 path = dir + "/" + hash + ".cache" 1012 return 1013 } 1014 1015 // 获取Hash对应的文件路径 1016 func (this *FileStorage) hashPath(hash string) (path string, diskIsFull bool) { 1017 if len(hash) != HashKeyLength { 1018 return "", false 1019 } 1020 var dir string 1021 dir, diskIsFull = this.subDir(hash) 1022 path = dir + "/" + hash + ".cache" 1023 return 1024 } 1025 1026 // 初始化List 1027 func (this *FileStorage) initList() error { 1028 err := this.list.Reset() 1029 if err != nil { 1030 return err 1031 } 1032 1033 // 使用异步防止阻塞主线程 1034 /**goman.New(func() { 1035 dir := this.dir() 1036 1037 // 清除tmp 1038 // TODO 需要一个更加高效的实现 1039 })**/ 1040 1041 // 启动定时清理任务 1042 this.initPurgeTicker() 1043 1044 // 热点处理任务 1045 this.hotTicker = utils.NewTicker(1 * time.Minute) 1046 if Tea.IsTesting() { 1047 this.hotTicker = utils.NewTicker(10 * time.Second) 1048 } 1049 goman.New(func() { 1050 for this.hotTicker.Next() { 1051 trackers.Run("FILE_CACHE_STORAGE_HOT_LOOP", func() { 1052 this.hotLoop() 1053 }) 1054 } 1055 }) 1056 1057 // 退出时停止 1058 events.OnKey(events.EventQuit, this, func() { 1059 remotelogs.Println("CACHE", "quit clean timer") 1060 1061 { 1062 var ticker = this.purgeTicker 1063 if ticker != nil { 1064 ticker.Stop() 1065 } 1066 } 1067 { 1068 var ticker = this.hotTicker 1069 if ticker != nil { 1070 ticker.Stop() 1071 } 1072 } 1073 }) 1074 1075 return nil 1076 } 1077 1078 // 清理任务 1079 // TODO purge每个分区 1080 func (this *FileStorage) purgeLoop() { 1081 // load 1082 systemLoad, _ := load.Avg() 1083 1084 // TODO 计算平均最近每日新增用量 1085 1086 // 计算是否应该开启LFU清理 1087 var capacityBytes = this.diskCapacityBytes() 1088 var startLFU = false 1089 var requireFullLFU = false // 是否需要完整执行LFU 1090 var lfuFreePercent = this.policy.PersistenceLFUFreePercent 1091 if lfuFreePercent <= 0 { 1092 lfuFreePercent = 5 1093 1094 if systemLoad == nil || systemLoad.Load5 > 10 { 1095 // 2TB级别以上 1096 if capacityBytes>>30 > 2000 { 1097 lfuFreePercent = 100 /** GB **/ / float32(capacityBytes>>30) * 100 /** % **/ 1098 if lfuFreePercent > 3 { 1099 lfuFreePercent = 3 1100 } 1101 } 1102 } 1103 } 1104 1105 var hasFullDisk = this.hasFullDisk() 1106 if hasFullDisk { 1107 startLFU = true 1108 } else { 1109 var usedPercent = float32(this.TotalDiskSize()*100) / float32(capacityBytes) 1110 if capacityBytes > 0 { 1111 if lfuFreePercent < 100 { 1112 if usedPercent >= 100-lfuFreePercent { 1113 startLFU = true 1114 } 1115 } 1116 } 1117 } 1118 1119 // 清理过期 1120 { 1121 var times = 1 1122 1123 // 空闲时间多清理 1124 if systemLoad != nil { 1125 if systemLoad.Load5 < 3 { 1126 times = 5 1127 } else if systemLoad.Load5 < 5 { 1128 times = 3 1129 } else if systemLoad.Load5 < 10 { 1130 times = 2 1131 } 1132 } 1133 1134 // 高速硬盘多清理 1135 if fsutils.DiskIsExtremelyFast() { 1136 times *= 8 1137 } else if fsutils.DiskIsFast() { 1138 times *= 4 1139 } 1140 1141 // 处于LFU阈值时,多清理 1142 if startLFU { 1143 times *= 5 1144 } 1145 1146 var purgeCount = this.policy.PersistenceAutoPurgeCount 1147 if purgeCount <= 0 { 1148 purgeCount = 1000 1149 1150 if fsutils.DiskIsExtremelyFast() { 1151 purgeCount = 4000 1152 } else if fsutils.DiskIsFast() { 1153 purgeCount = 2000 1154 } 1155 } 1156 1157 for i := 0; i < times; i++ { 1158 countFound, err := this.list.Purge(purgeCount, func(hash string) error { 1159 path, _ := this.hashPath(hash) 1160 err := this.removeCacheFile(path) 1161 if err != nil && !os.IsNotExist(err) { 1162 remotelogs.Error("CACHE", "purge '"+path+"' error: "+err.Error()) 1163 } 1164 1165 return nil 1166 }) 1167 if err != nil { 1168 remotelogs.Warn("CACHE", "purge file storage failed: "+err.Error()) 1169 continue 1170 } 1171 1172 if countFound < purgeCount { 1173 if i == 0 && startLFU { 1174 requireFullLFU = true 1175 } 1176 1177 break 1178 } 1179 1180 time.Sleep(1 * time.Second) 1181 } 1182 } 1183 1184 // 磁盘空间不足时,清除老旧的缓存 1185 if startLFU { 1186 var maxCount = 1000 1187 var maxLoops = 5 1188 1189 if fsutils.DiskIsExtremelyFast() { 1190 maxCount = 4000 1191 } else if fsutils.DiskIsFast() { 1192 maxCount = 2000 1193 } 1194 1195 var total, _ = this.list.Count() 1196 if total > 0 { 1197 for { 1198 maxLoops-- 1199 if maxLoops <= 0 { 1200 break 1201 } 1202 1203 // 开始清理 1204 var count = types.Int(math.Ceil(float64(total) * float64(lfuFreePercent*2) / 100)) 1205 if count <= 0 { 1206 break 1207 } 1208 1209 // 限制单次清理的条数,防止占用太多系统资源 1210 if count > maxCount { 1211 count = maxCount 1212 } 1213 1214 var before = time.Now() 1215 err := this.list.PurgeLFU(count, func(hash string) error { 1216 path, _ := this.hashPath(hash) 1217 err := this.removeCacheFile(path) 1218 if err != nil && !os.IsNotExist(err) { 1219 remotelogs.Error("CACHE", "purge '"+path+"' error: "+err.Error()) 1220 } 1221 1222 return nil 1223 }) 1224 1225 var prefix = "" 1226 if requireFullLFU { 1227 prefix = "fully " 1228 } 1229 remotelogs.Println("CACHE", prefix+"LFU purge policy '"+this.policy.Name+"' id: "+types.String(this.policy.Id)+", count: "+types.String(count)+", cost: "+fmt.Sprintf("%.2fms", time.Since(before).Seconds()*1000)) 1230 1231 if err != nil { 1232 remotelogs.Warn("CACHE", "purge file storage in LFU failed: "+err.Error()) 1233 } 1234 1235 // 检查硬盘空间状态 1236 if !requireFullLFU && !this.hasFullDisk() { 1237 break 1238 } 1239 } 1240 } 1241 } 1242 } 1243 1244 // 热点数据任务 1245 func (this *FileStorage) hotLoop() { 1246 var memoryStorage = this.memoryStorage // copy 1247 if memoryStorage == nil { 1248 return 1249 } 1250 1251 // check memory space size 1252 if !memoryStorage.HasFreeSpaceForHotItems() { 1253 return 1254 } 1255 1256 this.hotMapLocker.Lock() 1257 if len(this.hotMap) == 0 { 1258 this.hotMapLocker.Unlock() 1259 this.lastHotSize = 0 1260 return 1261 } 1262 1263 this.lastHotSize = len(this.hotMap) 1264 1265 var result = []*HotItem{} // [ {key: ..., hits: ...}, ... ] 1266 for _, v := range this.hotMap { 1267 if v.Hits <= 1 { 1268 continue 1269 } 1270 result = append(result, v) 1271 } 1272 1273 this.hotMap = map[string]*HotItem{} 1274 this.hotMapLocker.Unlock() 1275 1276 // 取Top10%写入内存 1277 if len(result) > 0 { 1278 sort.Slice(result, func(i, j int) bool { 1279 return result[i].Hits > result[j].Hits 1280 }) 1281 var size = 1 1282 if len(result) < 10 { 1283 size = 1 1284 } else { 1285 size = len(result) / 10 1286 } 1287 1288 var buf = utils.BytePool16k.Get() 1289 1290 defer utils.BytePool16k.Put(buf) 1291 for _, item := range result[:size] { 1292 reader, err := this.openReader(item.Key, false, false, false) 1293 if err != nil { 1294 continue 1295 } 1296 if reader == nil { 1297 continue 1298 } 1299 1300 // 如果即将过期,则忽略 1301 var nowUnixTime = time.Now().Unix() 1302 if reader.ExpiresAt() <= nowUnixTime+600 { 1303 continue 1304 } 1305 1306 // 计算合适的过期时间 1307 var bestExpiresAt = nowUnixTime + HotItemLifeSeconds 1308 var hotTimes = int64(item.Hits) / 1000 1309 if hotTimes > 8 { 1310 hotTimes = 8 1311 } 1312 bestExpiresAt += hotTimes * HotItemLifeSeconds 1313 var expiresAt = reader.ExpiresAt() 1314 if expiresAt <= 0 || expiresAt > bestExpiresAt { 1315 expiresAt = bestExpiresAt 1316 } 1317 1318 writer, err := memoryStorage.openWriter(item.Key, expiresAt, reader.Status(), types.Int(reader.HeaderSize()), reader.BodySize(), -1, false) 1319 if err != nil { 1320 if !CanIgnoreErr(err) { 1321 remotelogs.Error("CACHE", "transfer hot item failed: "+err.Error()) 1322 } 1323 _ = reader.Close() 1324 continue 1325 } 1326 if writer == nil { 1327 _ = reader.Close() 1328 continue 1329 } 1330 1331 err = reader.ReadHeader(buf.Bytes, func(n int) (goNext bool, err error) { 1332 _, err = writer.WriteHeader(buf.Bytes[:n]) 1333 return 1334 }) 1335 if err != nil { 1336 _ = reader.Close() 1337 _ = writer.Discard() 1338 continue 1339 } 1340 1341 err = reader.ReadBody(buf.Bytes, func(n int) (goNext bool, err error) { 1342 goNext = true 1343 if n > 0 { 1344 _, err = writer.Write(buf.Bytes[:n]) 1345 if err != nil { 1346 goNext = false 1347 } 1348 } 1349 return 1350 }) 1351 if err != nil { 1352 _ = reader.Close() 1353 _ = writer.Discard() 1354 continue 1355 } 1356 1357 memoryStorage.AddToList(&Item{ 1358 Type: writer.ItemType(), 1359 Key: item.Key, 1360 Host: ParseHost(item.Key), 1361 ExpiresAt: expiresAt, 1362 HeaderSize: writer.HeaderSize(), 1363 BodySize: writer.BodySize(), 1364 }) 1365 1366 _ = reader.Close() 1367 _ = writer.Close() 1368 } 1369 } 1370 } 1371 1372 func (this *FileStorage) diskCapacityBytes() int64 { 1373 var c1 = this.policy.CapacityBytes() 1374 var nodeCapacity = SharedManager.MaxDiskCapacity // copy 1375 if nodeCapacity != nil { 1376 var c2 = nodeCapacity.Bytes() 1377 if c2 > 0 { 1378 if this.mainDiskTotalSize > 0 && c2 >= int64(this.mainDiskTotalSize) { 1379 c2 = int64(this.mainDiskTotalSize) * 95 / 100 // keep 5% free 1380 } 1381 return c2 1382 } 1383 } 1384 1385 if c1 <= 0 || (this.mainDiskTotalSize > 0 && c1 >= int64(this.mainDiskTotalSize)) { 1386 c1 = int64(this.mainDiskTotalSize) * 95 / 100 // keep 5% free 1387 } 1388 1389 return c1 1390 } 1391 1392 // remove all *.trash directories under policy directory 1393 func (this *FileStorage) cleanAllDeletedDirs() { 1394 var rootDirs = []string{this.options.Dir} 1395 var subDirs = this.subDirs // copy slice 1396 if len(subDirs) > 0 { 1397 for _, subDir := range subDirs { 1398 rootDirs = append(rootDirs, subDir.Path) 1399 } 1400 } 1401 1402 for _, rootDir := range rootDirs { 1403 var dir = rootDir + "/p" + types.String(this.policy.Id) 1404 goman.New(func() { 1405 _ = this.cleanDeletedDirs(dir) 1406 }) 1407 } 1408 } 1409 1410 // 清理 *.trash 目录 1411 // 由于在很多硬盘上耗时非常久,所以应该放在后台运行 1412 func (this *FileStorage) cleanDeletedDirs(dir string) error { 1413 fp, err := os.Open(dir) 1414 if err != nil { 1415 return err 1416 } 1417 defer func() { 1418 _ = fp.Close() 1419 }() 1420 subDirs, err := fp.Readdir(-1) 1421 if err != nil { 1422 return err 1423 } 1424 for _, info := range subDirs { 1425 var subDir = info.Name() 1426 if !strings.HasSuffix(subDir, ".trash") { 1427 continue 1428 } 1429 1430 // 删除 1431 err = os.RemoveAll(dir + "/" + subDir) 1432 if err != nil { 1433 if !os.IsNotExist(err) { 1434 return err 1435 } 1436 } 1437 } 1438 return nil 1439 } 1440 1441 // 增加某个Key的点击量 1442 func (this *FileStorage) increaseHit(key string, hash string, reader Reader) { 1443 var rate = this.policy.PersistenceHitSampleRate 1444 if rate <= 0 { 1445 rate = 1000 1446 } 1447 if rands.Int(0, rate) == 0 { 1448 var memoryStorage = this.memoryStorage 1449 1450 // 增加到热点 1451 // 这里不收录缓存尺寸过大的文件 1452 if memoryStorage != nil && reader.BodySize() > 0 && reader.BodySize() < (128<<20) { 1453 this.hotMapLocker.Lock() 1454 hotItem, ok := this.hotMap[key] 1455 1456 if ok { 1457 hotItem.Hits++ 1458 } else if len(this.hotMap) < HotItemSize { // 控制数量 1459 this.hotMap[key] = &HotItem{ 1460 Key: key, 1461 Hits: 1, 1462 } 1463 } 1464 this.hotMapLocker.Unlock() 1465 1466 // 只有重复点击的才增加点击量 1467 if ok { 1468 var hitErr = this.list.IncreaseHit(hash) 1469 if hitErr != nil { 1470 // 此错误可以忽略 1471 remotelogs.Error("CACHE", "increase hit failed: "+hitErr.Error()) 1472 } 1473 } 1474 } 1475 } 1476 } 1477 1478 // 删除缓存文件 1479 func (this *FileStorage) removeCacheFile(path string) error { 1480 var openFileCache = this.openFileCache 1481 if openFileCache != nil { 1482 openFileCache.Close(path) 1483 } 1484 1485 var err = fsutils.Remove(path) 1486 if err == nil || os.IsNotExist(err) { 1487 err = nil 1488 1489 // 删除Partial相关 1490 var partialPath = PartialRangesFilePath(path) 1491 if openFileCache != nil { 1492 openFileCache.Close(partialPath) 1493 } 1494 1495 _, statErr := os.Stat(partialPath) 1496 if statErr == nil { 1497 _ = fsutils.Remove(partialPath) 1498 SharedPartialRangesQueue.Delete(partialPath) 1499 } 1500 } 1501 return err 1502 } 1503 1504 // 创建当前策略包含的内存缓存 1505 func (this *FileStorage) createMemoryStorage() error { 1506 var memoryPolicy = &serverconfigs.HTTPCachePolicy{ 1507 Id: this.policy.Id, 1508 IsOn: this.policy.IsOn, 1509 Name: this.policy.Name, 1510 Description: this.policy.Description, 1511 Capacity: this.options.MemoryPolicy.Capacity, 1512 MaxSize: &shared.SizeCapacity{Count: 128, Unit: shared.SizeCapacityUnitMB}, // TODO 将来可以修改 1513 Type: serverconfigs.CachePolicyStorageMemory, 1514 Options: this.policy.Options, 1515 Life: this.policy.Life, 1516 MinLife: this.policy.MinLife, 1517 MaxLife: this.policy.MaxLife, 1518 1519 MemoryAutoPurgeCount: this.policy.MemoryAutoPurgeCount, 1520 MemoryAutoPurgeInterval: this.policy.MemoryAutoPurgeInterval, 1521 MemoryLFUFreePercent: this.policy.MemoryLFUFreePercent, 1522 } 1523 err := memoryPolicy.Init() 1524 if err != nil { 1525 return err 1526 } 1527 var memoryStorage = NewMemoryStorage(memoryPolicy, this) 1528 err = memoryStorage.Init() 1529 if err != nil { 1530 return err 1531 } 1532 this.memoryStorage = memoryStorage 1533 1534 return nil 1535 } 1536 1537 func (this *FileStorage) initPurgeTicker() { 1538 var autoPurgeInterval = this.policy.PersistenceAutoPurgeInterval 1539 if autoPurgeInterval <= 0 { 1540 autoPurgeInterval = 30 1541 if Tea.IsTesting() { 1542 autoPurgeInterval = 10 1543 } 1544 } 1545 if this.purgeTicker != nil { 1546 this.purgeTicker.Stop() 1547 } 1548 this.purgeTicker = utils.NewTicker(time.Duration(autoPurgeInterval) * time.Second) 1549 goman.New(func() { 1550 for this.purgeTicker.Next() { 1551 trackers.Run("FILE_CACHE_STORAGE_PURGE_LOOP", func() { 1552 this.purgeLoop() 1553 }) 1554 } 1555 }) 1556 } 1557 1558 func (this *FileStorage) initOpenFileCache() { 1559 var err error 1560 1561 var oldOpenFileCache = this.openFileCache 1562 1563 // 启用新的 1564 if this.options.OpenFileCache != nil && this.options.OpenFileCache.IsOn && this.options.OpenFileCache.Max > 0 { 1565 this.openFileCache, err = NewOpenFileCache(this.options.OpenFileCache.Max) 1566 if err != nil { 1567 remotelogs.Error("CACHE", "open file cache failed: "+err.Error()) 1568 } 1569 } 1570 1571 // 关闭老的 1572 if oldOpenFileCache != nil { 1573 oldOpenFileCache.CloseAll() 1574 } 1575 } 1576 1577 func (this *FileStorage) runMemoryStorageSafety(f func(memoryStorage *MemoryStorage)) { 1578 var memoryStorage = this.memoryStorage // copy 1579 if memoryStorage != nil { 1580 f(memoryStorage) 1581 } 1582 } 1583 1584 // 检查磁盘剩余空间 1585 func (this *FileStorage) checkDiskSpace() { 1586 var minFreeSize = DefaultMinDiskFreeSpace 1587 1588 var options = this.options // copy 1589 if options != nil && options.MinFreeSize != nil && options.MinFreeSize.Bytes() > 0 { 1590 minFreeSize = uint64(options.MinFreeSize.Bytes()) 1591 } 1592 1593 if options != nil && len(options.Dir) > 0 { 1594 stat, err := fsutils.StatDevice(options.Dir) 1595 if err == nil { 1596 this.mainDiskIsFull = stat.FreeSize() < minFreeSize 1597 this.mainDiskTotalSize = stat.TotalSize() 1598 1599 // check capacity (only on main directory) when node capacity had not been set 1600 if !this.mainDiskIsFull { 1601 var capacityBytes int64 1602 var maxDiskCapacity = SharedManager.MaxDiskCapacity // copy 1603 if maxDiskCapacity != nil && maxDiskCapacity.Bytes() > 0 { 1604 capacityBytes = SharedManager.MaxDiskCapacity.Bytes() 1605 } else { 1606 var policy = this.policy // copy 1607 if policy != nil { 1608 capacityBytes = policy.CapacityBytes() // copy 1609 } 1610 } 1611 1612 if capacityBytes > 0 && stat.UsedSize() >= uint64(capacityBytes) { 1613 this.mainDiskIsFull = true 1614 } 1615 } 1616 } 1617 } 1618 var subDirs = this.subDirs // copy slice 1619 for _, subDir := range subDirs { 1620 stat, err := fsutils.StatDevice(subDir.Path) 1621 if err == nil { 1622 subDir.IsFull = stat.FreeSize() < minFreeSize 1623 } 1624 } 1625 } 1626 1627 // 检查是否有已满的磁盘分区 1628 func (this *FileStorage) hasFullDisk() bool { 1629 this.checkDiskSpace() 1630 1631 var hasFullDisk = this.mainDiskIsFull 1632 if !hasFullDisk { 1633 var subDirs = this.subDirs // copy slice 1634 for _, subDir := range subDirs { 1635 if subDir.IsFull { 1636 hasFullDisk = true 1637 break 1638 } 1639 } 1640 } 1641 return hasFullDisk 1642 } 1643 1644 // 获取目录 1645 func (this *FileStorage) subDir(hash string) (dirPath string, dirIsFull bool) { 1646 var suffix = "/p" + types.String(this.policy.Id) + "/" + hash[:2] + "/" + hash[2:4] 1647 1648 if len(hash) < 4 { 1649 return this.options.Dir + suffix, this.mainDiskIsFull 1650 } 1651 1652 var subDirs = this.subDirs // copy slice 1653 var countSubDirs = len(subDirs) 1654 if countSubDirs == 0 { 1655 return this.options.Dir + suffix, this.mainDiskIsFull 1656 } 1657 1658 countSubDirs++ // add main dir 1659 1660 // 最多只支持16个目录 1661 if countSubDirs > 16 { 1662 countSubDirs = 16 1663 } 1664 1665 var dirIndex = this.charCode(hash[0]) % uint8(countSubDirs) 1666 if dirIndex == 0 { 1667 return this.options.Dir + suffix, this.mainDiskIsFull 1668 } 1669 var subDir = subDirs[dirIndex-1] 1670 return subDir.Path + suffix, subDir.IsFull 1671 } 1672 1673 // ScanGarbageCaches 清理目录中“失联”的缓存文件 1674 // “失联”为不在HashMap中的文件 1675 func (this *FileStorage) ScanGarbageCaches(fileCallback func(path string) error) error { 1676 _, isSQLite := this.list.(*SQLiteFileList) 1677 if isSQLite && !this.list.(*SQLiteFileList).HashMapIsLoaded() { 1678 return errors.New("cache list is loading") 1679 } 1680 1681 var mainDir = this.options.Dir 1682 var allDirs = []string{mainDir} 1683 var subDirs = this.subDirs // copy 1684 for _, subDir := range subDirs { 1685 allDirs = append(allDirs, subDir.Path) 1686 } 1687 1688 var countDirs = 0 1689 1690 // process progress 1691 var progressSock = gosock.NewTmpSock(teaconst.CacheGarbageSockName) 1692 _, sockErr := progressSock.SendTimeout(&gosock.Command{Code: "progress", Params: map[string]any{"progress": 0}}, 1*time.Second) 1693 var canReportProgress = sockErr == nil 1694 var lastProgress float64 1695 var countFound = 0 1696 1697 for _, subDir := range allDirs { 1698 var dir0 = subDir + "/p" + types.String(this.policy.Id) 1699 dir1Matches, err := filepath.Glob(dir0 + "/*") 1700 if err != nil { 1701 // ignore error 1702 continue 1703 } 1704 1705 for _, dir1 := range dir1Matches { 1706 if len(filepath.Base(dir1)) != 2 { 1707 continue 1708 } 1709 1710 dir2Matches, globErr := filepath.Glob(dir1 + "/*") 1711 if globErr != nil { 1712 // ignore error 1713 continue 1714 } 1715 for _, dir2 := range dir2Matches { 1716 if len(filepath.Base(dir2)) != 2 { 1717 continue 1718 } 1719 1720 countDirs++ 1721 1722 // report progress 1723 if canReportProgress { 1724 var progress = float64(countDirs) / 65536 1725 if fmt.Sprintf("%.2f", lastProgress) != fmt.Sprintf("%.2f", progress) { 1726 lastProgress = progress 1727 _, _ = progressSock.SendTimeout(&gosock.Command{Code: "progress", Params: map[string]any{ 1728 "progress": progress, 1729 "count": countFound, 1730 }}, 100*time.Millisecond) 1731 } 1732 } 1733 1734 fileMatches, globDir2Err := filepath.Glob(dir2 + "/*.cache") 1735 if globDir2Err != nil { 1736 // ignore error 1737 continue 1738 } 1739 1740 for _, file := range fileMatches { 1741 var filename = filepath.Base(file) 1742 var hash = strings.TrimSuffix(filename, ".cache") 1743 if len(hash) != HashKeyLength { 1744 continue 1745 } 1746 1747 var isReady, found bool 1748 switch rawList := this.list.(type) { 1749 case *SQLiteFileList: 1750 isReady, found = rawList.ExistQuick(hash) 1751 case *KVFileList: 1752 isReady = true 1753 var checkErr error 1754 found, checkErr = rawList.ExistQuick(hash) 1755 if checkErr != nil { 1756 return checkErr 1757 } 1758 } 1759 1760 if !isReady { 1761 continue 1762 } 1763 1764 if found { 1765 continue 1766 } 1767 1768 // 检查文件正在被写入 1769 stat, statErr := os.Stat(file) 1770 if statErr != nil { 1771 continue 1772 } 1773 if fasttime.Now().Unix()-stat.ModTime().Unix() < 300 /** 5 minutes **/ { 1774 continue 1775 } 1776 1777 if fileCallback != nil { 1778 countFound++ 1779 callbackErr := fileCallback(file) 1780 if callbackErr != nil { 1781 return callbackErr 1782 } 1783 } 1784 } 1785 } 1786 } 1787 } 1788 1789 // 100% progress 1790 if canReportProgress && lastProgress != 1 { 1791 _, _ = progressSock.SendTimeout(&gosock.Command{Code: "progress", Params: map[string]any{ 1792 "progress": 1, 1793 "count": countFound, 1794 }}, 100*time.Millisecond) 1795 } 1796 1797 return nil 1798 } 1799 1800 // 计算字节数字代号 1801 func (this *FileStorage) charCode(r byte) uint8 { 1802 if r >= '0' && r <= '9' { 1803 return r - '0' 1804 } 1805 if r >= 'a' && r <= 'z' { 1806 return r - 'a' + 10 1807 } 1808 return 0 1809 }