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  }