2020-10-05 19:15:35 +08:00
|
|
|
|
package caches
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
2021-12-08 15:17:45 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
2021-06-13 17:37:57 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
2021-11-14 10:55:09 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
2020-10-05 19:15:35 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
2023-04-08 12:47:04 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
2023-07-30 09:22:13 +08:00
|
|
|
|
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
2022-03-06 17:18:06 +08:00
|
|
|
|
setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets"
|
2021-12-09 12:07:46 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
2020-11-21 22:29:57 +08:00
|
|
|
|
"github.com/cespare/xxhash"
|
2021-11-13 21:30:24 +08:00
|
|
|
|
"github.com/iwind/TeaGo/types"
|
|
|
|
|
|
"math"
|
2021-11-14 10:55:09 +08:00
|
|
|
|
"runtime"
|
2020-10-05 19:15:35 +08:00
|
|
|
|
"strconv"
|
2022-11-26 11:05:46 +08:00
|
|
|
|
"strings"
|
2020-10-05 19:15:35 +08:00
|
|
|
|
"sync"
|
2020-10-05 20:23:18 +08:00
|
|
|
|
"sync/atomic"
|
2020-10-05 19:15:35 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type MemoryItem struct {
|
2022-04-14 09:36:02 +08:00
|
|
|
|
ExpiresAt int64
|
2021-01-13 12:02:50 +08:00
|
|
|
|
HeaderValue []byte
|
|
|
|
|
|
BodyValue []byte
|
|
|
|
|
|
Status int
|
2021-03-02 19:43:05 +08:00
|
|
|
|
IsDone bool
|
2021-06-14 11:46:39 +08:00
|
|
|
|
ModifiedAt int64
|
2023-10-01 14:11:48 +08:00
|
|
|
|
|
2023-10-01 15:06:58 +08:00
|
|
|
|
IsPrepared bool
|
|
|
|
|
|
WriteOffset int64
|
2023-10-07 11:56:34 +08:00
|
|
|
|
|
|
|
|
|
|
isReferring bool // if it is referring by other objects
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
func (this *MemoryItem) IsExpired() bool {
|
2023-04-08 12:47:04 +08:00
|
|
|
|
return this.ExpiresAt < fasttime.Now().Unix()
|
2021-06-06 23:42:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 19:15:35 +08:00
|
|
|
|
type MemoryStorage struct {
|
2021-11-13 21:30:24 +08:00
|
|
|
|
parentStorage StorageInterface
|
|
|
|
|
|
|
|
|
|
|
|
policy *serverconfigs.HTTPCachePolicy
|
|
|
|
|
|
list ListInterface
|
|
|
|
|
|
locker *sync.RWMutex
|
|
|
|
|
|
|
|
|
|
|
|
valuesMap map[uint64]*MemoryItem // hash => item
|
2022-03-12 19:45:22 +08:00
|
|
|
|
|
2022-03-15 18:32:39 +08:00
|
|
|
|
dirtyChan chan string // hash chan
|
|
|
|
|
|
dirtyQueueSize int
|
2021-11-13 21:30:24 +08:00
|
|
|
|
|
|
|
|
|
|
purgeTicker *utils.Ticker
|
|
|
|
|
|
|
2023-10-01 14:11:48 +08:00
|
|
|
|
usedSize int64
|
2021-12-09 12:07:46 +08:00
|
|
|
|
writingKeyMap map[string]zero.Zero // key => bool
|
2022-03-06 17:18:06 +08:00
|
|
|
|
|
|
|
|
|
|
ignoreKeys *setutils.FixedSet
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-13 21:30:24 +08:00
|
|
|
|
func NewMemoryStorage(policy *serverconfigs.HTTPCachePolicy, parentStorage StorageInterface) *MemoryStorage {
|
|
|
|
|
|
var dirtyChan chan string
|
2022-03-15 18:32:39 +08:00
|
|
|
|
var queueSize = policy.MemoryAutoFlushQueueSize
|
|
|
|
|
|
|
2021-11-13 21:30:24 +08:00
|
|
|
|
if parentStorage != nil {
|
|
|
|
|
|
if queueSize <= 0 {
|
2023-10-02 15:20:19 +08:00
|
|
|
|
queueSize = utils.SystemMemoryGB() * 100_000
|
2021-11-13 21:30:24 +08:00
|
|
|
|
}
|
2022-03-15 18:32:39 +08:00
|
|
|
|
|
2021-11-13 21:30:24 +08:00
|
|
|
|
dirtyChan = make(chan string, queueSize)
|
|
|
|
|
|
}
|
2020-10-05 19:15:35 +08:00
|
|
|
|
return &MemoryStorage{
|
2022-03-15 18:32:39 +08:00
|
|
|
|
parentStorage: parentStorage,
|
|
|
|
|
|
policy: policy,
|
|
|
|
|
|
list: NewMemoryList(),
|
|
|
|
|
|
locker: &sync.RWMutex{},
|
|
|
|
|
|
valuesMap: map[uint64]*MemoryItem{},
|
|
|
|
|
|
dirtyChan: dirtyChan,
|
|
|
|
|
|
dirtyQueueSize: queueSize,
|
|
|
|
|
|
writingKeyMap: map[string]zero.Zero{},
|
|
|
|
|
|
ignoreKeys: setutils.NewFixedSet(32768),
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// Init 初始化
|
2020-10-05 19:15:35 +08:00
|
|
|
|
func (this *MemoryStorage) Init() error {
|
2021-06-12 10:03:33 +08:00
|
|
|
|
_ = this.list.Init()
|
|
|
|
|
|
|
2023-10-03 11:39:28 +08:00
|
|
|
|
this.list.OnAdd(func(item *Item) {
|
|
|
|
|
|
atomic.AddInt64(&this.usedSize, item.TotalSize())
|
|
|
|
|
|
})
|
|
|
|
|
|
this.list.OnRemove(func(item *Item) {
|
|
|
|
|
|
atomic.AddInt64(&this.usedSize, -item.TotalSize())
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2022-03-15 21:33:44 +08:00
|
|
|
|
this.initPurgeTicker()
|
2020-10-05 19:15:35 +08:00
|
|
|
|
|
2021-11-13 21:30:24 +08:00
|
|
|
|
// 启动定时Flush memory to disk任务
|
2022-03-06 18:09:33 +08:00
|
|
|
|
if this.parentStorage != nil {
|
2022-03-16 16:20:53 +08:00
|
|
|
|
// TODO 应该根据磁盘性能决定线程数
|
2022-08-14 16:28:40 +08:00
|
|
|
|
// TODO 线程数应该可以在缓存策略和节点中设定
|
|
|
|
|
|
var threads = runtime.NumCPU()
|
2022-03-16 16:20:53 +08:00
|
|
|
|
|
2022-03-15 18:32:39 +08:00
|
|
|
|
for i := 0; i < threads; i++ {
|
|
|
|
|
|
goman.New(func() {
|
2022-03-16 16:20:53 +08:00
|
|
|
|
this.startFlush()
|
2022-03-15 18:32:39 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
2022-03-06 18:09:33 +08:00
|
|
|
|
}
|
2021-11-13 21:30:24 +08:00
|
|
|
|
|
2020-10-05 19:15:35 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// OpenReader 读取缓存
|
2022-03-03 19:36:28 +08:00
|
|
|
|
func (this *MemoryStorage) OpenReader(key string, useStale bool, isPartial bool) (Reader, error) {
|
2023-07-17 09:29:59 +08:00
|
|
|
|
var hash = this.hash(key)
|
|
|
|
|
|
|
|
|
|
|
|
// check if exists in list
|
|
|
|
|
|
exists, _ := this.list.Exist(types.String(hash))
|
|
|
|
|
|
if !exists {
|
|
|
|
|
|
return nil, ErrNotFound
|
|
|
|
|
|
}
|
2020-10-05 19:15:35 +08:00
|
|
|
|
|
2023-07-17 09:29:59 +08:00
|
|
|
|
// read from valuesMap
|
2020-10-05 19:15:35 +08:00
|
|
|
|
this.locker.RLock()
|
2023-10-07 11:56:34 +08:00
|
|
|
|
var item = this.valuesMap[hash]
|
|
|
|
|
|
|
|
|
|
|
|
if item != nil {
|
|
|
|
|
|
item.isReferring = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-02 19:43:05 +08:00
|
|
|
|
if item == nil || !item.IsDone {
|
2021-06-17 18:04:56 +08:00
|
|
|
|
this.locker.RUnlock()
|
2021-01-13 12:02:50 +08:00
|
|
|
|
return nil, ErrNotFound
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-08 12:47:04 +08:00
|
|
|
|
if useStale || (item.ExpiresAt > fasttime.Now().Unix()) {
|
2023-07-17 09:29:59 +08:00
|
|
|
|
var reader = NewMemoryReader(item)
|
2021-01-13 12:02:50 +08:00
|
|
|
|
err := reader.Init()
|
|
|
|
|
|
if err != nil {
|
2021-06-17 18:04:56 +08:00
|
|
|
|
this.locker.RUnlock()
|
2021-01-13 12:02:50 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2021-06-17 18:04:56 +08:00
|
|
|
|
this.locker.RUnlock()
|
2021-11-13 21:30:24 +08:00
|
|
|
|
|
2021-01-13 12:02:50 +08:00
|
|
|
|
return reader, nil
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
2021-06-17 18:04:56 +08:00
|
|
|
|
this.locker.RUnlock()
|
2020-10-05 19:15:35 +08:00
|
|
|
|
|
|
|
|
|
|
_ = this.Delete(key)
|
|
|
|
|
|
|
2021-01-13 12:02:50 +08:00
|
|
|
|
return nil, ErrNotFound
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// OpenWriter 打开缓存写入器等待写入
|
2022-11-19 17:23:45 +08:00
|
|
|
|
func (this *MemoryStorage) OpenWriter(key string, expiredAt int64, status int, headerSize int, bodySize int64, maxSize int64, isPartial bool) (Writer, error) {
|
2023-07-18 12:45:25 +08:00
|
|
|
|
if maxSize > 0 && this.ignoreKeys.Has(types.String(maxSize)+"$"+key) {
|
2022-03-06 17:18:06 +08:00
|
|
|
|
return nil, ErrEntityTooLarge
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-21 17:33:58 +08:00
|
|
|
|
// TODO 内存缓存暂时不支持分块内容存储
|
|
|
|
|
|
if isPartial {
|
|
|
|
|
|
return nil, ErrFileIsWriting
|
|
|
|
|
|
}
|
2022-11-19 17:23:45 +08:00
|
|
|
|
return this.openWriter(key, expiredAt, status, headerSize, bodySize, maxSize, true)
|
2021-11-14 16:15:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-14 09:36:02 +08:00
|
|
|
|
// OpenFlushWriter 打开从其他媒介直接刷入的写入器
|
2022-11-19 17:23:45 +08:00
|
|
|
|
func (this *MemoryStorage) OpenFlushWriter(key string, expiresAt int64, status int, headerSize int, bodySize int64) (Writer, error) {
|
|
|
|
|
|
return this.openWriter(key, expiresAt, status, headerSize, bodySize, -1, true)
|
2022-04-14 09:36:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-11-19 17:23:45 +08:00
|
|
|
|
func (this *MemoryStorage) openWriter(key string, expiresAt int64, status int, headerSize int, bodySize int64, maxSize int64, isDirty bool) (Writer, error) {
|
2022-03-15 18:32:39 +08:00
|
|
|
|
// 待写入队列是否已满
|
|
|
|
|
|
if isDirty &&
|
|
|
|
|
|
this.parentStorage != nil &&
|
|
|
|
|
|
this.dirtyQueueSize > 0 &&
|
2023-10-02 15:20:19 +08:00
|
|
|
|
len(this.dirtyChan) >= this.dirtyQueueSize-int(fsutils.DiskMaxWrites) /** delta **/ { // 缓存时间过长
|
2022-03-15 18:32:39 +08:00
|
|
|
|
return nil, ErrWritingQueueFull
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
defer this.locker.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
// 是否正在写入
|
|
|
|
|
|
var isWriting = false
|
|
|
|
|
|
_, ok := this.writingKeyMap[key]
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
return nil, ErrFileIsWriting
|
|
|
|
|
|
}
|
2021-12-09 12:07:46 +08:00
|
|
|
|
this.writingKeyMap[key] = zero.New()
|
2021-06-06 23:42:11 +08:00
|
|
|
|
defer func() {
|
|
|
|
|
|
if !isWriting {
|
|
|
|
|
|
delete(this.writingKeyMap, key)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否过期
|
2023-07-17 09:29:59 +08:00
|
|
|
|
var hash = this.hash(key)
|
2021-06-06 23:42:11 +08:00
|
|
|
|
item, ok := this.valuesMap[hash]
|
|
|
|
|
|
if ok && !item.IsExpired() {
|
2023-07-17 09:29:59 +08:00
|
|
|
|
var hashString = types.String(hash)
|
|
|
|
|
|
exists, _ := this.list.Exist(hashString)
|
|
|
|
|
|
if !exists {
|
|
|
|
|
|
// remove from values map
|
|
|
|
|
|
delete(this.valuesMap, hash)
|
|
|
|
|
|
_ = this.list.Remove(hashString)
|
|
|
|
|
|
item = nil
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return nil, ErrFileIsWriting
|
|
|
|
|
|
}
|
2021-06-06 23:42:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-02 15:20:19 +08:00
|
|
|
|
// 检查是否超出容量最大值
|
2023-10-01 14:11:48 +08:00
|
|
|
|
var capacityBytes = this.memoryCapacityBytes()
|
2022-11-19 17:23:45 +08:00
|
|
|
|
if bodySize < 0 {
|
|
|
|
|
|
bodySize = 0
|
2022-02-15 16:44:39 +08:00
|
|
|
|
}
|
2023-10-01 14:11:48 +08:00
|
|
|
|
if capacityBytes > 0 && capacityBytes <= atomic.LoadInt64(&this.usedSize)+bodySize {
|
|
|
|
|
|
return nil, NewCapacityError("write memory cache failed: over memory size: " + strconv.FormatInt(capacityBytes, 10) + ", current size: " + strconv.FormatInt(this.usedSize, 10) + " bytes")
|
2020-10-05 20:23:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 先删除
|
2023-07-08 18:52:57 +08:00
|
|
|
|
err := this.deleteWithoutLocker(key)
|
2020-10-05 20:23:18 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
isWriting = true
|
2023-10-01 15:06:58 +08:00
|
|
|
|
return NewMemoryWriter(this, key, expiresAt, status, isDirty, bodySize, maxSize, func(valueItem *MemoryItem) {
|
2021-06-06 23:42:11 +08:00
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
delete(this.writingKeyMap, key)
|
|
|
|
|
|
this.locker.Unlock()
|
|
|
|
|
|
}), nil
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// Delete 删除某个键值对应的缓存
|
2020-10-05 19:15:35 +08:00
|
|
|
|
func (this *MemoryStorage) Delete(key string) error {
|
2022-11-26 11:05:46 +08:00
|
|
|
|
var hash = this.hash(key)
|
2020-10-05 19:15:35 +08:00
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
delete(this.valuesMap, hash)
|
2022-11-26 11:05:46 +08:00
|
|
|
|
_ = this.list.Remove(types.String(hash))
|
2020-10-05 19:15:35 +08:00
|
|
|
|
this.locker.Unlock()
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// Stat 统计缓存
|
2020-10-05 19:15:35 +08:00
|
|
|
|
func (this *MemoryStorage) Stat() (*Stat, error) {
|
|
|
|
|
|
this.locker.RLock()
|
|
|
|
|
|
defer this.locker.RUnlock()
|
|
|
|
|
|
|
|
|
|
|
|
return this.list.Stat(func(hash string) bool {
|
|
|
|
|
|
return true
|
2021-05-19 12:07:35 +08:00
|
|
|
|
})
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// CleanAll 清除所有缓存
|
2020-10-05 19:15:35 +08:00
|
|
|
|
func (this *MemoryStorage) CleanAll() error {
|
|
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
this.valuesMap = map[uint64]*MemoryItem{}
|
2021-05-24 09:37:37 +08:00
|
|
|
|
_ = this.list.Reset()
|
2023-10-01 14:11:48 +08:00
|
|
|
|
atomic.StoreInt64(&this.usedSize, 0)
|
2020-10-05 19:15:35 +08:00
|
|
|
|
this.locker.Unlock()
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// Purge 批量删除缓存
|
2020-12-23 21:28:50 +08:00
|
|
|
|
func (this *MemoryStorage) Purge(keys []string, urlType string) error {
|
|
|
|
|
|
// 目录
|
|
|
|
|
|
if urlType == "dir" {
|
|
|
|
|
|
for _, key := range keys {
|
2022-11-26 11:05:46 +08:00
|
|
|
|
// 检查是否有通配符 http(s)://*.example.com
|
|
|
|
|
|
var schemeIndex = strings.Index(key, "://")
|
|
|
|
|
|
if schemeIndex > 0 {
|
|
|
|
|
|
var keyRight = key[schemeIndex+3:]
|
|
|
|
|
|
if strings.HasPrefix(keyRight, "*.") {
|
|
|
|
|
|
err := this.list.CleanMatchPrefix(key)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-13 17:37:57 +08:00
|
|
|
|
err := this.list.CleanPrefix(key)
|
2021-05-19 12:07:35 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
2020-12-23 21:28:50 +08:00
|
|
|
|
}
|
2022-06-05 17:15:02 +08:00
|
|
|
|
return nil
|
2020-12-23 21:28:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-05 17:15:02 +08:00
|
|
|
|
// URL
|
2020-10-05 19:15:35 +08:00
|
|
|
|
for _, key := range keys {
|
2022-11-26 11:05:46 +08:00
|
|
|
|
// 检查是否有通配符 http(s)://*.example.com
|
|
|
|
|
|
var schemeIndex = strings.Index(key, "://")
|
|
|
|
|
|
if schemeIndex > 0 {
|
|
|
|
|
|
var keyRight = key[schemeIndex+3:]
|
|
|
|
|
|
if strings.HasPrefix(keyRight, "*.") {
|
|
|
|
|
|
err := this.list.CleanMatchKey(key)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 19:15:35 +08:00
|
|
|
|
err := this.Delete(key)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// Stop 停止缓存策略
|
2020-10-05 19:15:35 +08:00
|
|
|
|
func (this *MemoryStorage) Stop() {
|
|
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
|
|
|
|
|
|
this.valuesMap = map[uint64]*MemoryItem{}
|
2021-12-09 12:07:46 +08:00
|
|
|
|
this.writingKeyMap = map[string]zero.Zero{}
|
2021-05-24 09:37:37 +08:00
|
|
|
|
_ = this.list.Reset()
|
2021-11-13 21:30:24 +08:00
|
|
|
|
if this.purgeTicker != nil {
|
|
|
|
|
|
this.purgeTicker.Stop()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-06 18:09:33 +08:00
|
|
|
|
if this.dirtyChan != nil {
|
2021-11-13 21:30:24 +08:00
|
|
|
|
close(this.dirtyChan)
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
2021-06-13 17:37:57 +08:00
|
|
|
|
|
|
|
|
|
|
_ = this.list.Close()
|
|
|
|
|
|
|
|
|
|
|
|
this.locker.Unlock()
|
|
|
|
|
|
|
2022-03-06 17:18:06 +08:00
|
|
|
|
this.ignoreKeys.Reset()
|
|
|
|
|
|
|
2021-11-14 10:55:09 +08:00
|
|
|
|
// 回收内存
|
|
|
|
|
|
runtime.GC()
|
|
|
|
|
|
|
2021-06-13 17:37:57 +08:00
|
|
|
|
remotelogs.Println("CACHE", "close memory storage '"+strconv.FormatInt(this.policy.Id, 10)+"'")
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// Policy 获取当前存储的Policy
|
2020-10-05 19:15:35 +08:00
|
|
|
|
func (this *MemoryStorage) Policy() *serverconfigs.HTTPCachePolicy {
|
|
|
|
|
|
return this.policy
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-15 21:33:44 +08:00
|
|
|
|
// UpdatePolicy 修改策略
|
|
|
|
|
|
func (this *MemoryStorage) UpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy) {
|
|
|
|
|
|
var oldPolicy = this.policy
|
|
|
|
|
|
this.policy = newPolicy
|
|
|
|
|
|
|
|
|
|
|
|
if oldPolicy.MemoryAutoPurgeInterval != newPolicy.MemoryAutoPurgeInterval {
|
|
|
|
|
|
this.initPurgeTicker()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果是空的,则清空
|
|
|
|
|
|
if newPolicy.CapacityBytes() == 0 {
|
|
|
|
|
|
_ = this.CleanAll()
|
|
|
|
|
|
}
|
2023-07-18 12:45:25 +08:00
|
|
|
|
|
|
|
|
|
|
// reset ignored keys
|
|
|
|
|
|
this.ignoreKeys.Reset()
|
2022-03-15 21:33:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CanUpdatePolicy 检查策略是否可以更新
|
|
|
|
|
|
func (this *MemoryStorage) CanUpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy) bool {
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
// AddToList 将缓存添加到列表
|
2020-10-05 19:15:35 +08:00
|
|
|
|
func (this *MemoryStorage) AddToList(item *Item) {
|
2023-10-01 14:11:48 +08:00
|
|
|
|
// skip added item
|
|
|
|
|
|
if item.MetaSize > 0 {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-13 21:30:24 +08:00
|
|
|
|
item.MetaSize = int64(len(item.Key)) + 128 /** 128是我们评估的数据结构的长度 **/
|
2022-11-26 11:05:46 +08:00
|
|
|
|
var hash = types.String(this.hash(item.Key))
|
|
|
|
|
|
|
|
|
|
|
|
if len(item.Host) == 0 {
|
|
|
|
|
|
item.Host = ParseHost(item.Key)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-24 09:37:37 +08:00
|
|
|
|
_ = this.list.Add(hash, item)
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 11:50:36 +08:00
|
|
|
|
// TotalDiskSize 消耗的磁盘尺寸
|
|
|
|
|
|
func (this *MemoryStorage) TotalDiskSize() int64 {
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TotalMemorySize 内存尺寸
|
|
|
|
|
|
func (this *MemoryStorage) TotalMemorySize() int64 {
|
2023-10-01 14:11:48 +08:00
|
|
|
|
return atomic.LoadInt64(&this.usedSize)
|
2021-05-13 11:50:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-06 17:18:06 +08:00
|
|
|
|
// IgnoreKey 忽略某个Key,即不缓存某个Key
|
2023-07-18 12:45:25 +08:00
|
|
|
|
func (this *MemoryStorage) IgnoreKey(key string, maxSize int64) {
|
|
|
|
|
|
this.ignoreKeys.Push(types.String(maxSize) + "$" + key)
|
2022-03-06 17:18:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-04 19:45:57 +08:00
|
|
|
|
// CanSendfile 是否支持Sendfile
|
|
|
|
|
|
func (this *MemoryStorage) CanSendfile() bool {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-04 18:13:48 +08:00
|
|
|
|
// HasFreeSpaceForHotItems 是否有足够的空间提供给热门内容
|
|
|
|
|
|
func (this *MemoryStorage) HasFreeSpaceForHotItems() bool {
|
|
|
|
|
|
return atomic.LoadInt64(&this.usedSize) < this.memoryCapacityBytes()*3/4
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 19:15:35 +08:00
|
|
|
|
// 计算Key Hash
|
|
|
|
|
|
func (this *MemoryStorage) hash(key string) uint64 {
|
2020-11-21 22:29:57 +08:00
|
|
|
|
return xxhash.Sum64String(key)
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清理任务
|
|
|
|
|
|
func (this *MemoryStorage) purgeLoop() {
|
2021-11-13 21:30:24 +08:00
|
|
|
|
// 清理过期
|
|
|
|
|
|
var purgeCount = this.policy.MemoryAutoPurgeCount
|
|
|
|
|
|
if purgeCount <= 0 {
|
|
|
|
|
|
purgeCount = 2000
|
|
|
|
|
|
}
|
|
|
|
|
|
_, _ = this.list.Purge(purgeCount, func(hash string) error {
|
2020-10-05 19:15:35 +08:00
|
|
|
|
uintHash, err := strconv.ParseUint(hash, 10, 64)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
delete(this.valuesMap, uintHash)
|
|
|
|
|
|
this.locker.Unlock()
|
|
|
|
|
|
}
|
2021-05-19 12:07:35 +08:00
|
|
|
|
return nil
|
2020-10-05 19:15:35 +08:00
|
|
|
|
})
|
2021-11-13 21:30:24 +08:00
|
|
|
|
|
|
|
|
|
|
// LFU
|
2023-10-03 11:39:28 +08:00
|
|
|
|
// 计算是否应该开启LFU清理
|
|
|
|
|
|
var capacityBytes = this.policy.CapacityBytes()
|
|
|
|
|
|
var startLFU = false
|
|
|
|
|
|
|
|
|
|
|
|
var usedPercent = float32(this.TotalMemorySize()*100) / float32(capacityBytes)
|
|
|
|
|
|
var lfuFreePercent = this.policy.MemoryLFUFreePercent
|
|
|
|
|
|
if lfuFreePercent <= 0 {
|
|
|
|
|
|
lfuFreePercent = 5
|
|
|
|
|
|
}
|
|
|
|
|
|
if capacityBytes > 0 {
|
|
|
|
|
|
if lfuFreePercent < 100 {
|
|
|
|
|
|
if usedPercent >= 100-lfuFreePercent {
|
|
|
|
|
|
startLFU = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-13 21:30:24 +08:00
|
|
|
|
if startLFU {
|
|
|
|
|
|
var total, _ = this.list.Count()
|
|
|
|
|
|
if total > 0 {
|
2021-11-14 10:55:09 +08:00
|
|
|
|
var count = types.Int(math.Ceil(float64(total) * float64(lfuFreePercent*2) / 100))
|
2021-11-13 21:30:24 +08:00
|
|
|
|
if count > 0 {
|
|
|
|
|
|
// 限制单次清理的条数,防止占用太多系统资源
|
|
|
|
|
|
if count > 2000 {
|
|
|
|
|
|
count = 2000
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-14 10:55:09 +08:00
|
|
|
|
// 这里不提示LFU,因为此事件将会非常频繁
|
2021-11-13 21:30:24 +08:00
|
|
|
|
|
|
|
|
|
|
err := this.list.PurgeLFU(count, func(hash string) error {
|
|
|
|
|
|
uintHash, err := strconv.ParseUint(hash, 10, 64)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
this.locker.Lock()
|
|
|
|
|
|
delete(this.valuesMap, uintHash)
|
|
|
|
|
|
this.locker.Unlock()
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Warn("CACHE", "purge memory storage in LFU failed: "+err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-16 16:20:53 +08:00
|
|
|
|
// 开始Flush任务
|
|
|
|
|
|
func (this *MemoryStorage) startFlush() {
|
|
|
|
|
|
var statCount = 0
|
|
|
|
|
|
|
2022-11-26 11:05:46 +08:00
|
|
|
|
for key := range this.dirtyChan {
|
2022-03-16 16:20:53 +08:00
|
|
|
|
statCount++
|
|
|
|
|
|
|
|
|
|
|
|
if statCount == 100 {
|
|
|
|
|
|
statCount = 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-11-26 11:05:46 +08:00
|
|
|
|
this.flushItem(key)
|
2022-03-16 16:20:53 +08:00
|
|
|
|
|
2023-10-07 11:56:34 +08:00
|
|
|
|
if fsutils.IsInExtremelyHighLoad {
|
|
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
|
|
} else if fsutils.IsInHighLoad {
|
|
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
2022-03-16 16:20:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 单次Flush任务
|
2021-11-13 21:30:24 +08:00
|
|
|
|
func (this *MemoryStorage) flushItem(key string) {
|
|
|
|
|
|
if this.parentStorage == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
var hash = this.hash(key)
|
|
|
|
|
|
|
|
|
|
|
|
this.locker.RLock()
|
|
|
|
|
|
item, ok := this.valuesMap[hash]
|
|
|
|
|
|
this.locker.RUnlock()
|
|
|
|
|
|
|
2023-10-02 15:20:19 +08:00
|
|
|
|
// 从内存中移除,并确保无论如何都会执行
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
_ = this.Delete(key)
|
2023-10-07 11:56:34 +08:00
|
|
|
|
|
|
|
|
|
|
// 重用内存,前提是确保内存不再被引用
|
|
|
|
|
|
if ok && item.IsDone && !item.isReferring && len(item.BodyValue) > 0 {
|
|
|
|
|
|
SharedFragmentMemoryPool.Put(item.BodyValue)
|
|
|
|
|
|
}
|
2023-10-02 15:20:19 +08:00
|
|
|
|
}()
|
|
|
|
|
|
|
2021-11-13 21:30:24 +08:00
|
|
|
|
if !ok {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-10-03 11:39:28 +08:00
|
|
|
|
|
2023-03-12 16:36:59 +08:00
|
|
|
|
if !item.IsDone {
|
|
|
|
|
|
remotelogs.Error("CACHE", "flush items failed: open writer failed: item has not been done")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if item.IsExpired() {
|
2021-11-13 21:30:24 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-03 11:39:28 +08:00
|
|
|
|
// 检查是否在列表中,防止未加入列表时就开始flush
|
|
|
|
|
|
isInList, err := this.list.Exist(types.String(hash))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("CACHE", "flush items failed: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if !isInList {
|
|
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-11-19 17:23:45 +08:00
|
|
|
|
writer, err := this.parentStorage.OpenFlushWriter(key, item.ExpiresAt, item.Status, len(item.HeaderValue), int64(len(item.BodyValue)))
|
2021-11-13 21:30:24 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
if !CanIgnoreErr(err) {
|
|
|
|
|
|
remotelogs.Error("CACHE", "flush items failed: open writer failed: "+err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_, err = writer.WriteHeader(item.HeaderValue)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = writer.Discard()
|
|
|
|
|
|
remotelogs.Error("CACHE", "flush items failed: write header failed: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_, err = writer.Write(item.BodyValue)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = writer.Discard()
|
|
|
|
|
|
remotelogs.Error("CACHE", "flush items failed: writer body failed: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = writer.Close()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = writer.Discard()
|
|
|
|
|
|
remotelogs.Error("CACHE", "flush items failed: close writer failed: "+err.Error())
|
2022-03-12 19:45:22 +08:00
|
|
|
|
return
|
2021-11-13 21:30:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.parentStorage.AddToList(&Item{
|
|
|
|
|
|
Type: writer.ItemType(),
|
|
|
|
|
|
Key: key,
|
2022-11-26 11:05:46 +08:00
|
|
|
|
Host: ParseHost(key),
|
2022-04-14 09:36:02 +08:00
|
|
|
|
ExpiredAt: item.ExpiresAt,
|
2021-11-13 21:30:24 +08:00
|
|
|
|
HeaderSize: writer.HeaderSize(),
|
|
|
|
|
|
BodySize: writer.BodySize(),
|
|
|
|
|
|
})
|
2020-10-05 19:15:35 +08:00
|
|
|
|
}
|
2021-05-12 21:38:44 +08:00
|
|
|
|
|
|
|
|
|
|
func (this *MemoryStorage) memoryCapacityBytes() int64 {
|
2023-10-16 14:28:07 +08:00
|
|
|
|
var maxSystemBytes = int64(utils.SystemMemoryBytes()) / 3 // 1/3 of the system memory
|
|
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
if this.policy == nil {
|
2023-10-16 14:28:07 +08:00
|
|
|
|
return maxSystemBytes
|
2021-05-12 21:38:44 +08:00
|
|
|
|
}
|
2023-10-01 14:11:48 +08:00
|
|
|
|
|
2021-05-12 21:38:44 +08:00
|
|
|
|
if SharedManager.MaxMemoryCapacity != nil {
|
2023-10-01 14:11:48 +08:00
|
|
|
|
var capacityBytes = SharedManager.MaxMemoryCapacity.Bytes()
|
|
|
|
|
|
if capacityBytes > 0 {
|
2023-10-16 14:28:07 +08:00
|
|
|
|
if capacityBytes > maxSystemBytes {
|
|
|
|
|
|
return maxSystemBytes
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-01 14:11:48 +08:00
|
|
|
|
return capacityBytes
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var capacity = this.policy.Capacity // copy
|
|
|
|
|
|
if capacity != nil {
|
|
|
|
|
|
var capacityBytes = capacity.Bytes()
|
|
|
|
|
|
if capacityBytes > 0 {
|
2023-10-16 14:28:07 +08:00
|
|
|
|
if capacityBytes > maxSystemBytes {
|
|
|
|
|
|
return maxSystemBytes
|
|
|
|
|
|
}
|
2023-10-01 14:11:48 +08:00
|
|
|
|
return capacityBytes
|
2021-05-12 21:38:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-10-01 14:11:48 +08:00
|
|
|
|
|
2023-10-16 14:28:07 +08:00
|
|
|
|
// 1/4 of the system memory
|
|
|
|
|
|
return maxSystemBytes
|
2021-05-12 21:38:44 +08:00
|
|
|
|
}
|
2021-06-06 23:42:11 +08:00
|
|
|
|
|
2021-06-17 18:04:56 +08:00
|
|
|
|
func (this *MemoryStorage) deleteWithoutLocker(key string) error {
|
2021-06-06 23:42:11 +08:00
|
|
|
|
hash := this.hash(key)
|
|
|
|
|
|
delete(this.valuesMap, hash)
|
2022-11-26 11:05:46 +08:00
|
|
|
|
_ = this.list.Remove(types.String(hash))
|
2021-06-06 23:42:11 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2022-03-15 21:33:44 +08:00
|
|
|
|
|
|
|
|
|
|
func (this *MemoryStorage) initPurgeTicker() {
|
|
|
|
|
|
var autoPurgeInterval = this.policy.MemoryAutoPurgeInterval
|
|
|
|
|
|
if autoPurgeInterval <= 0 {
|
|
|
|
|
|
autoPurgeInterval = 5
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 启动定时清理任务
|
|
|
|
|
|
|
|
|
|
|
|
if this.purgeTicker != nil {
|
|
|
|
|
|
this.purgeTicker.Stop()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.purgeTicker = utils.NewTicker(time.Duration(autoPurgeInterval) * time.Second)
|
|
|
|
|
|
goman.New(func() {
|
|
|
|
|
|
for this.purgeTicker.Next() {
|
|
|
|
|
|
var tr = trackers.Begin("MEMORY_CACHE_STORAGE_PURGE_LOOP")
|
|
|
|
|
|
this.purgeLoop()
|
|
|
|
|
|
tr.End()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|