2024-05-17 18:30:33 +08:00
|
|
|
|
// Copyright 2022 GoEdge goedge.cdn@gmail.com. All rights reserved.
|
2022-01-12 21:09:00 +08:00
|
|
|
|
|
|
|
|
|
|
package caches
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2023-10-11 21:51:05 +08:00
|
|
|
|
"fmt"
|
2024-05-11 09:23:54 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
2022-01-12 21:09:00 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/linkedlist"
|
2024-03-28 17:17:34 +08:00
|
|
|
|
memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem"
|
2022-01-12 21:09:00 +08:00
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
|
|
|
|
"github.com/iwind/TeaGo/logs"
|
|
|
|
|
|
"github.com/iwind/TeaGo/types"
|
|
|
|
|
|
"path/filepath"
|
2022-03-31 13:30:52 +08:00
|
|
|
|
"runtime"
|
2022-01-12 21:09:00 +08:00
|
|
|
|
"sync"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2023-10-11 21:51:05 +08:00
|
|
|
|
const (
|
|
|
|
|
|
maxOpenFileSize = 256 << 20
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2022-01-12 21:09:00 +08:00
|
|
|
|
type OpenFileCache struct {
|
|
|
|
|
|
poolMap map[string]*OpenFilePool // file path => Pool
|
2023-10-03 21:38:45 +08:00
|
|
|
|
poolList *linkedlist.List[*OpenFilePool]
|
2022-01-12 21:09:00 +08:00
|
|
|
|
watcher *fsnotify.Watcher
|
|
|
|
|
|
|
2022-11-08 21:37:20 +08:00
|
|
|
|
locker sync.RWMutex
|
2022-01-12 21:09:00 +08:00
|
|
|
|
|
2023-10-11 21:51:05 +08:00
|
|
|
|
maxCount int
|
|
|
|
|
|
capacitySize int64
|
|
|
|
|
|
|
|
|
|
|
|
count int
|
|
|
|
|
|
usedSize int64
|
2022-01-12 21:09:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-11 21:51:05 +08:00
|
|
|
|
func NewOpenFileCache(maxCount int) (*OpenFileCache, error) {
|
|
|
|
|
|
if maxCount <= 0 {
|
|
|
|
|
|
maxCount = 16384
|
2022-01-12 21:09:00 +08:00
|
|
|
|
}
|
2024-05-16 20:40:41 +08:00
|
|
|
|
if maxCount > 65535 {
|
|
|
|
|
|
maxCount = 65535
|
|
|
|
|
|
}
|
2022-01-12 21:09:00 +08:00
|
|
|
|
|
|
|
|
|
|
var cache = &OpenFileCache{
|
2023-10-11 21:51:05 +08:00
|
|
|
|
maxCount: maxCount,
|
|
|
|
|
|
poolMap: map[string]*OpenFilePool{},
|
|
|
|
|
|
poolList: linkedlist.NewList[*OpenFilePool](),
|
2024-03-28 17:17:34 +08:00
|
|
|
|
capacitySize: (int64(memutils.SystemMemoryGB()) << 30) / 16,
|
2022-01-12 21:09:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
watcher, err := fsnotify.NewWatcher()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
cache.watcher = watcher
|
|
|
|
|
|
|
|
|
|
|
|
goman.New(func() {
|
|
|
|
|
|
for event := range watcher.Events {
|
2022-03-31 13:30:52 +08:00
|
|
|
|
if runtime.GOOS == "linux" || event.Op&fsnotify.Chmod != fsnotify.Chmod {
|
2022-01-12 21:09:00 +08:00
|
|
|
|
cache.Close(event.Name)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return cache, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *OpenFileCache) Get(filename string) *OpenFile {
|
2024-03-28 08:52:53 +08:00
|
|
|
|
filename = filepath.Clean(filename)
|
|
|
|
|
|
|
2022-11-08 21:37:20 +08:00
|
|
|
|
this.locker.RLock()
|
2022-01-12 21:09:00 +08:00
|
|
|
|
pool, ok := this.poolMap[filename]
|
2022-11-08 21:37:20 +08:00
|
|
|
|
this.locker.RUnlock()
|
2022-01-12 21:09:00 +08:00
|
|
|
|
if ok {
|
2023-10-11 21:51:05 +08:00
|
|
|
|
file, consumed, consumedSize := pool.Get()
|
2022-01-12 21:09:00 +08:00
|
|
|
|
if consumed {
|
2022-11-08 21:37:20 +08:00
|
|
|
|
this.locker.Lock()
|
2022-01-12 21:09:00 +08:00
|
|
|
|
this.count--
|
2023-10-11 21:51:05 +08:00
|
|
|
|
this.usedSize -= consumedSize
|
2022-11-25 14:52:04 +08:00
|
|
|
|
|
|
|
|
|
|
// pool如果为空,也不需要从列表中删除,避免put时需要重新创建
|
|
|
|
|
|
|
2022-11-08 21:37:20 +08:00
|
|
|
|
this.locker.Unlock()
|
2022-01-12 21:09:00 +08:00
|
|
|
|
}
|
2023-10-11 21:51:05 +08:00
|
|
|
|
|
2022-01-12 21:09:00 +08:00
|
|
|
|
return file
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *OpenFileCache) Put(filename string, file *OpenFile) {
|
2024-03-28 08:52:53 +08:00
|
|
|
|
filename = filepath.Clean(filename)
|
|
|
|
|
|
|
2023-10-11 21:51:05 +08:00
|
|
|
|
if file.size > maxOpenFileSize {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-01-12 21:09:00 +08:00
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
defer this.locker.Unlock()
|
|
|
|
|
|
|
2023-10-11 21:51:05 +08:00
|
|
|
|
// 如果超过当前容量,则关闭最早的
|
2023-10-17 09:59:04 +08:00
|
|
|
|
if this.count >= this.maxCount || this.usedSize+file.size >= this.capacitySize {
|
2023-10-11 21:51:05 +08:00
|
|
|
|
this.consumeHead()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-01-12 21:09:00 +08:00
|
|
|
|
pool, ok := this.poolMap[filename]
|
|
|
|
|
|
var success bool
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
success = pool.Put(file)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_ = this.watcher.Add(filename)
|
|
|
|
|
|
pool = NewOpenFilePool(filename)
|
2022-03-31 11:47:31 +08:00
|
|
|
|
pool.version = file.version
|
2022-01-12 21:09:00 +08:00
|
|
|
|
this.poolMap[filename] = pool
|
|
|
|
|
|
success = pool.Put(file)
|
|
|
|
|
|
}
|
|
|
|
|
|
this.poolList.Push(pool.linkItem)
|
|
|
|
|
|
|
|
|
|
|
|
// 检查长度
|
|
|
|
|
|
if success {
|
|
|
|
|
|
this.count++
|
2023-10-11 21:51:05 +08:00
|
|
|
|
this.usedSize += file.size
|
2022-01-12 21:09:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *OpenFileCache) Close(filename string) {
|
2024-03-28 08:52:53 +08:00
|
|
|
|
filename = filepath.Clean(filename)
|
|
|
|
|
|
|
2022-01-12 21:09:00 +08:00
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
|
|
|
|
|
|
pool, ok := this.poolMap[filename]
|
|
|
|
|
|
if ok {
|
2022-12-05 11:16:04 +08:00
|
|
|
|
// 设置关闭状态
|
|
|
|
|
|
pool.SetClosing()
|
|
|
|
|
|
|
2022-01-12 21:09:00 +08:00
|
|
|
|
delete(this.poolMap, filename)
|
|
|
|
|
|
this.poolList.Remove(pool.linkItem)
|
|
|
|
|
|
_ = this.watcher.Remove(filename)
|
|
|
|
|
|
this.count -= pool.Len()
|
2023-10-11 21:51:05 +08:00
|
|
|
|
this.usedSize -= pool.usedSize
|
2022-01-12 21:09:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.locker.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
// 在locker之外,提升性能
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
pool.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *OpenFileCache) CloseAll() {
|
|
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
for _, pool := range this.poolMap {
|
|
|
|
|
|
pool.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
this.poolMap = map[string]*OpenFilePool{}
|
|
|
|
|
|
this.poolList.Reset()
|
|
|
|
|
|
_ = this.watcher.Close()
|
2022-11-25 14:52:04 +08:00
|
|
|
|
this.count = 0
|
2023-10-11 21:51:05 +08:00
|
|
|
|
this.usedSize = 0
|
2022-01-12 21:09:00 +08:00
|
|
|
|
this.locker.Unlock()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-11 21:51:05 +08:00
|
|
|
|
func (this *OpenFileCache) SetCapacity(capacityBytes int64) {
|
|
|
|
|
|
this.capacitySize = capacityBytes
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-01-12 21:09:00 +08:00
|
|
|
|
func (this *OpenFileCache) Debug() {
|
|
|
|
|
|
var ticker = time.NewTicker(5 * time.Second)
|
|
|
|
|
|
goman.New(func() {
|
|
|
|
|
|
for range ticker.C {
|
2023-10-11 21:51:05 +08:00
|
|
|
|
logs.Println("==== " + types.String(this.count) + ", " + fmt.Sprintf("%.4fMB", float64(this.usedSize)/(1<<20)) + " ====")
|
2023-10-03 21:38:45 +08:00
|
|
|
|
this.poolList.Range(func(item *linkedlist.Item[*OpenFilePool]) (goNext bool) {
|
|
|
|
|
|
logs.Println(filepath.Base(item.Value.Filename()), item.Value.Len())
|
2022-01-12 21:09:00 +08:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2023-10-11 21:51:05 +08:00
|
|
|
|
|
|
|
|
|
|
func (this *OpenFileCache) consumeHead() {
|
|
|
|
|
|
var delta = 1
|
|
|
|
|
|
|
|
|
|
|
|
if this.count > 100 {
|
|
|
|
|
|
delta = 2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for i := 0; i < delta; i++ {
|
|
|
|
|
|
var head = this.poolList.Head()
|
|
|
|
|
|
if head == nil {
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var headPool = head.Value
|
|
|
|
|
|
headFile, consumed, consumedSize := headPool.Get()
|
|
|
|
|
|
if consumed {
|
|
|
|
|
|
this.count--
|
|
|
|
|
|
this.usedSize -= consumedSize
|
|
|
|
|
|
|
|
|
|
|
|
if headFile != nil {
|
|
|
|
|
|
_ = headFile.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if headPool.Len() == 0 {
|
|
|
|
|
|
delete(this.poolMap, headPool.filename)
|
|
|
|
|
|
this.poolList.Remove(head)
|
|
|
|
|
|
_ = this.watcher.Remove(headPool.filename)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|