🎉 Initial commit
This commit is contained in:
313
cache/cache.go
vendored
Normal file
313
cache/cache.go
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CacheOptions 缓存配置选项
|
||||
type CacheOptions struct {
|
||||
Enabled bool // 是否启用缓存
|
||||
Size int // 缓存大小,0表示无限制
|
||||
Expiration time.Duration // 缓存项过期时间,0表示永不过期
|
||||
EvictionType string // 缓存淘汰策略,支持"lru"(最近最少使用)和"fifo"(先进先出)
|
||||
Compression CompressOptions // 压缩选项
|
||||
Monitoring MonitorOptions // 监控选项
|
||||
}
|
||||
|
||||
// DefaultCacheOptions 默认缓存配置
|
||||
var DefaultCacheOptions = CacheOptions{
|
||||
Enabled: true,
|
||||
Size: 100, // 默认缓存100个SVG
|
||||
Expiration: time.Hour, // 默认缓存项过期时间为1小时
|
||||
EvictionType: "lru", // 默认使用LRU淘汰策略
|
||||
Compression: DefaultCompressOptions, // 默认压缩选项
|
||||
Monitoring: DefaultMonitorOptions, // 默认监控选项
|
||||
}
|
||||
|
||||
// CacheKey 缓存键结构
|
||||
type CacheKey struct {
|
||||
Id string
|
||||
SansEnv bool
|
||||
Theme int
|
||||
Part int
|
||||
}
|
||||
|
||||
// CacheItem 缓存项结构
|
||||
type CacheItem struct {
|
||||
SVG string // SVG内容
|
||||
Compressed []byte // 压缩后的SVG数据
|
||||
IsCompressed bool // 是否已压缩
|
||||
CreatedAt time.Time // 创建时间
|
||||
LastUsed time.Time // 最后使用时间
|
||||
}
|
||||
|
||||
// PNCache SVG缓存结构
|
||||
type PNCache struct {
|
||||
Options CacheOptions
|
||||
Items map[CacheKey]*list.Element // 存储缓存项的映射
|
||||
EvictionList *list.List // 用于实现LRU/FIFO的双向链表
|
||||
Mutex sync.RWMutex
|
||||
Hits int // 缓存命中次数
|
||||
Misses int // 缓存未命中次数
|
||||
Monitor *Monitor // 缓存监控器
|
||||
}
|
||||
|
||||
// NewCache 创建一个新的缓存实例
|
||||
func NewCache(options CacheOptions) *PNCache {
|
||||
cache := &PNCache{
|
||||
Options: options,
|
||||
Items: make(map[CacheKey]*list.Element),
|
||||
EvictionList: list.New(),
|
||||
Hits: 0,
|
||||
Misses: 0,
|
||||
}
|
||||
|
||||
// 如果启用了监控,创建并启动监控器
|
||||
if options.Monitoring.Enabled {
|
||||
cache.Monitor = NewMonitor(cache, options.Monitoring)
|
||||
cache.Monitor.Start()
|
||||
}
|
||||
|
||||
return cache
|
||||
}
|
||||
|
||||
// NewDefaultCache 使用默认配置创建一个新的缓存实例
|
||||
func NewDefaultCache() *PNCache {
|
||||
return NewCache(DefaultCacheOptions)
|
||||
}
|
||||
|
||||
// Get 从缓存中获取SVG
|
||||
func (c *PNCache) Get(key CacheKey) (string, bool) {
|
||||
if !c.Options.Enabled {
|
||||
c.Misses++
|
||||
return "", false
|
||||
}
|
||||
|
||||
c.Mutex.Lock() // 使用写锁以便更新LRU信息
|
||||
defer c.Mutex.Unlock()
|
||||
|
||||
element, found := c.Items[key]
|
||||
if !found {
|
||||
c.Misses++
|
||||
return "", false
|
||||
}
|
||||
|
||||
// 获取缓存项
|
||||
cacheItem := element.Value.(*CacheItem)
|
||||
|
||||
// 检查是否过期
|
||||
if c.Options.Expiration > 0 {
|
||||
if time.Since(cacheItem.CreatedAt) > c.Options.Expiration {
|
||||
// 删除过期项
|
||||
c.EvictionList.Remove(element)
|
||||
delete(c.Items, key)
|
||||
c.Misses++
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新LRU信息
|
||||
if c.Options.EvictionType == "lru" {
|
||||
cacheItem.LastUsed = time.Now()
|
||||
c.EvictionList.MoveToFront(element)
|
||||
}
|
||||
|
||||
c.Hits++
|
||||
|
||||
// 如果数据已压缩,需要解压缩
|
||||
if cacheItem.IsCompressed {
|
||||
svg, err := DecompressSVG(cacheItem.Compressed, true)
|
||||
if err != nil {
|
||||
// 解压失败,返回未压缩的原始数据
|
||||
return cacheItem.SVG, true
|
||||
}
|
||||
return svg, true
|
||||
}
|
||||
|
||||
return cacheItem.SVG, true
|
||||
}
|
||||
|
||||
// Set 将SVG存入缓存
|
||||
func (c *PNCache) Set(key CacheKey, svg string) {
|
||||
if !c.Options.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
c.Mutex.Lock()
|
||||
defer c.Mutex.Unlock()
|
||||
|
||||
// 尝试压缩SVG数据
|
||||
var compressed []byte
|
||||
var isCompressed bool
|
||||
|
||||
// 如果启用了压缩,尝试压缩SVG
|
||||
if c.Options.Compression.Enabled {
|
||||
// 首先优化SVG
|
||||
optimizedSVG := OptimizeSVG(svg)
|
||||
|
||||
// 然后压缩
|
||||
compressed, isCompressed = CompressSVG(optimizedSVG, c.Options.Compression)
|
||||
|
||||
// 如果压缩成功,使用优化后的SVG
|
||||
if isCompressed {
|
||||
svg = optimizedSVG
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
if element, exists := c.Items[key]; exists {
|
||||
// 更新现有项
|
||||
cacheItem := element.Value.(*CacheItem)
|
||||
cacheItem.SVG = svg
|
||||
cacheItem.Compressed = compressed
|
||||
cacheItem.IsCompressed = isCompressed
|
||||
cacheItem.LastUsed = time.Now()
|
||||
cacheItem.CreatedAt = time.Now()
|
||||
|
||||
// 如果使用LRU策略,将项移到链表前端
|
||||
if c.Options.EvictionType == "lru" {
|
||||
c.EvictionList.MoveToFront(element)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 如果达到大小限制,需要淘汰一个项
|
||||
if c.Options.Size > 0 && len(c.Items) >= c.Options.Size {
|
||||
c.evictItem()
|
||||
}
|
||||
|
||||
// 创建新的缓存项
|
||||
now := time.Now()
|
||||
cacheItem := &CacheItem{
|
||||
SVG: svg,
|
||||
Compressed: compressed,
|
||||
IsCompressed: isCompressed,
|
||||
CreatedAt: now,
|
||||
LastUsed: now,
|
||||
}
|
||||
|
||||
// 添加到链表和映射
|
||||
element := c.EvictionList.PushFront(cacheItem)
|
||||
c.Items[key] = element
|
||||
}
|
||||
|
||||
// evictItem 根据淘汰策略移除一个缓存项
|
||||
func (c *PNCache) evictItem() {
|
||||
if c.EvictionList.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取要淘汰的元素
|
||||
var element *list.Element
|
||||
switch c.Options.EvictionType {
|
||||
case "lru":
|
||||
// LRU策略:移除链表尾部元素(最近最少使用)
|
||||
element = c.EvictionList.Back()
|
||||
default:
|
||||
// 默认使用FIFO策略:移除链表尾部元素(最先添加)
|
||||
element = c.EvictionList.Back()
|
||||
}
|
||||
|
||||
if element != nil {
|
||||
// 从链表中移除
|
||||
c.EvictionList.Remove(element)
|
||||
|
||||
// 从映射中找到并删除对应的键
|
||||
for k, v := range c.Items {
|
||||
if v == element {
|
||||
delete(c.Items, k)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear 清空缓存
|
||||
func (c *PNCache) Clear() {
|
||||
c.Mutex.Lock()
|
||||
defer c.Mutex.Unlock()
|
||||
|
||||
c.Items = make(map[CacheKey]*list.Element)
|
||||
c.EvictionList = list.New()
|
||||
c.Hits = 0
|
||||
c.Misses = 0
|
||||
}
|
||||
|
||||
// Size 返回当前缓存项数量
|
||||
func (c *PNCache) Size() int {
|
||||
c.Mutex.RLock()
|
||||
defer c.Mutex.RUnlock()
|
||||
|
||||
return len(c.Items)
|
||||
}
|
||||
|
||||
// Stats 返回缓存统计信息
|
||||
func (c *PNCache) Stats() (Hits, Misses int, hitRate float64) {
|
||||
c.Mutex.RLock()
|
||||
defer c.Mutex.RUnlock()
|
||||
|
||||
Hits = c.Hits
|
||||
Misses = c.Misses
|
||||
total := Hits + Misses
|
||||
if total > 0 {
|
||||
hitRate = float64(Hits) / float64(total)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RemoveExpired 移除所有过期的缓存项
|
||||
func (c *PNCache) RemoveExpired() int {
|
||||
if c.Options.Expiration <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.Mutex.Lock()
|
||||
defer c.Mutex.Unlock()
|
||||
|
||||
count := 0
|
||||
now := time.Now()
|
||||
|
||||
// 遍历所有缓存项,检查是否过期
|
||||
for key, element := range c.Items {
|
||||
cacheItem := element.Value.(*CacheItem)
|
||||
if now.Sub(cacheItem.CreatedAt) > c.Options.Expiration {
|
||||
// 从链表中移除
|
||||
c.EvictionList.Remove(element)
|
||||
// 从映射中删除
|
||||
delete(c.Items, key)
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// GetOptions 获取当前缓存选项
|
||||
func (c *PNCache) GetOptions() CacheOptions {
|
||||
c.Mutex.RLock()
|
||||
defer c.Mutex.RUnlock()
|
||||
|
||||
return c.Options
|
||||
}
|
||||
|
||||
// UpdateOptions 更新缓存选项
|
||||
func (c *PNCache) UpdateOptions(options CacheOptions) {
|
||||
c.Mutex.Lock()
|
||||
defer c.Mutex.Unlock()
|
||||
|
||||
// 更新选项
|
||||
c.Options = options
|
||||
|
||||
// 如果新的缓存大小小于当前项数,需要淘汰一些项
|
||||
if c.Options.Size > 0 && c.Options.Size < len(c.Items) {
|
||||
// 计算需要淘汰的项数
|
||||
toEvict := len(c.Items) - c.Options.Size
|
||||
|
||||
// 淘汰多余的项
|
||||
for i := 0; i < toEvict; i++ {
|
||||
c.evictItem()
|
||||
}
|
||||
}
|
||||
}
|
142
cache/compress.go
vendored
Normal file
142
cache/compress.go
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CompressOptions 压缩选项
|
||||
type CompressOptions struct {
|
||||
Enabled bool // 是否启用压缩
|
||||
Level int // 压缩级别 (1-9),1为最快压缩,9为最佳压缩
|
||||
MinSizeBytes int // 最小压缩大小,小于此大小的数据不进行压缩
|
||||
Ratio float64 // 压缩比阈值,压缩后大小/原始大小,小于此值才保存压缩结果
|
||||
}
|
||||
|
||||
// DefaultCompressOptions 默认压缩选项
|
||||
var DefaultCompressOptions = CompressOptions{
|
||||
Enabled: true,
|
||||
Level: 6, // 默认压缩级别为6,平衡压缩率和性能
|
||||
MinSizeBytes: 100, // 默认最小压缩大小为100字节
|
||||
Ratio: 0.9, // 默认压缩比阈值为0.9,即至少要压缩到原始大小的90%以下才保存压缩结果
|
||||
}
|
||||
|
||||
// CompressSVG 压缩SVG数据
|
||||
// 返回压缩后的数据和是否进行了压缩
|
||||
func CompressSVG(svg string, options CompressOptions) ([]byte, bool) {
|
||||
if !options.Enabled || len(svg) < options.MinSizeBytes {
|
||||
return []byte(svg), false
|
||||
}
|
||||
|
||||
// 创建一个bytes.Buffer来存储压缩数据
|
||||
var buf bytes.Buffer
|
||||
|
||||
// 创建一个gzip.Writer,设置压缩级别
|
||||
writer, err := gzip.NewWriterLevel(&buf, options.Level)
|
||||
if err != nil {
|
||||
return []byte(svg), false
|
||||
}
|
||||
|
||||
// 写入SVG数据
|
||||
_, err = writer.Write([]byte(svg))
|
||||
if err != nil {
|
||||
return []byte(svg), false
|
||||
}
|
||||
|
||||
// 关闭writer,确保所有数据都被写入
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return []byte(svg), false
|
||||
}
|
||||
|
||||
// 获取压缩后的数据
|
||||
compressed := buf.Bytes()
|
||||
|
||||
// 计算压缩比
|
||||
ratio := float64(len(compressed)) / float64(len(svg))
|
||||
|
||||
// 如果压缩比不理想,返回原始数据
|
||||
if ratio >= options.Ratio {
|
||||
return []byte(svg), false
|
||||
}
|
||||
|
||||
return compressed, true
|
||||
}
|
||||
|
||||
// DecompressSVG 解压缩SVG数据
|
||||
func DecompressSVG(data []byte, isCompressed bool) (string, error) {
|
||||
// 如果数据未压缩,直接返回字符串
|
||||
if !isCompressed {
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// 检查数据是否为gzip格式
|
||||
if !isGzipped(data) {
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// 创建一个gzip.Reader
|
||||
reader, err := gzip.NewReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return string(data), err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
// 读取解压缩后的数据
|
||||
decompressed, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
return string(decompressed), nil
|
||||
}
|
||||
|
||||
// isGzipped 检查数据是否为gzip格式
|
||||
func isGzipped(data []byte) bool {
|
||||
// gzip文件的魔数是0x1f 0x8b
|
||||
return len(data) > 2 && data[0] == 0x1f && data[1] == 0x8b
|
||||
}
|
||||
|
||||
// OptimizeSVG 优化SVG字符串,移除不必要的空白和注释
|
||||
func OptimizeSVG(svg string) string {
|
||||
// 移除XML注释
|
||||
svg = removeXMLComments(svg)
|
||||
|
||||
// 移除多余的空白
|
||||
svg = removeExtraWhitespace(svg)
|
||||
|
||||
return svg
|
||||
}
|
||||
|
||||
// removeXMLComments 移除XML注释
|
||||
func removeXMLComments(svg string) string {
|
||||
for {
|
||||
start := strings.Index(svg, "<!--")
|
||||
if start == -1 {
|
||||
break
|
||||
}
|
||||
end := strings.Index(svg[start:], "-->") + start
|
||||
if end > start {
|
||||
svg = svg[:start] + svg[end+3:]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return svg
|
||||
}
|
||||
|
||||
// removeExtraWhitespace 移除多余的空白
|
||||
func removeExtraWhitespace(svg string) string {
|
||||
// 替换多个空白字符为单个空格
|
||||
svg = strings.Join(strings.Fields(svg), " ")
|
||||
|
||||
// 优化常见的SVG标签周围的空白
|
||||
svg = strings.ReplaceAll(svg, "> <", "><")
|
||||
svg = strings.ReplaceAll(svg, " />", "/>")
|
||||
svg = strings.ReplaceAll(svg, " =", "=")
|
||||
svg = strings.ReplaceAll(svg, "= ", "=")
|
||||
|
||||
return svg
|
||||
}
|
214
cache/monitor.go
vendored
Normal file
214
cache/monitor.go
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MonitorOptions 缓存监控选项
|
||||
type MonitorOptions struct {
|
||||
Enabled bool // 是否启用监控
|
||||
SampleInterval time.Duration // 采样间隔时间
|
||||
AdjustInterval time.Duration // 调整间隔时间
|
||||
MinSize int // 最小缓存大小
|
||||
MaxSize int // 最大缓存大小
|
||||
TargetHitRate float64 // 目标命中率
|
||||
SizeGrowthFactor float64 // 缓存大小增长因子
|
||||
SizeShrinkFactor float64 // 缓存大小收缩因子
|
||||
ExpirationFactor float64 // 过期时间调整因子
|
||||
}
|
||||
|
||||
// DefaultMonitorOptions 默认监控选项
|
||||
var DefaultMonitorOptions = MonitorOptions{
|
||||
Enabled: true,
|
||||
SampleInterval: time.Minute, // 每分钟采样一次
|
||||
AdjustInterval: time.Minute * 10, // 每10分钟调整一次
|
||||
MinSize: 50, // 最小缓存大小
|
||||
MaxSize: 1000, // 最大缓存大小
|
||||
TargetHitRate: 0.8, // 目标命中率80%
|
||||
SizeGrowthFactor: 1.2, // 增长20%
|
||||
SizeShrinkFactor: 0.8, // 收缩20%
|
||||
ExpirationFactor: 1.5, // 过期时间调整因子
|
||||
}
|
||||
|
||||
// CacheStats 缓存统计信息
|
||||
type CacheStats struct {
|
||||
Size int // 当前缓存大小
|
||||
Hits int // 命中次数
|
||||
Misses int // 未命中次数
|
||||
HitRate float64 // 命中率
|
||||
MemoryUsage int64 // 内存使用量(字节)
|
||||
LastAdjusted time.Time // 最后调整时间
|
||||
SamplesCount int // 样本数量
|
||||
AvgAccessTime float64 // 平均访问时间(纳秒)
|
||||
}
|
||||
|
||||
// Monitor 缓存监控器
|
||||
type Monitor struct {
|
||||
options MonitorOptions
|
||||
cache *PNCache
|
||||
stats CacheStats
|
||||
sampleHistory []CacheStats
|
||||
mutex sync.RWMutex
|
||||
stopChan chan struct{}
|
||||
isRunning bool
|
||||
}
|
||||
|
||||
// NewMonitor 创建一个新的缓存监控器
|
||||
func NewMonitor(cache *PNCache, options MonitorOptions) *Monitor {
|
||||
return &Monitor{
|
||||
options: options,
|
||||
cache: cache,
|
||||
sampleHistory: make([]CacheStats, 0, 100), // 预分配100个样本的容量
|
||||
stopChan: make(chan struct{}),
|
||||
isRunning: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Start 启动监控器
|
||||
func (m *Monitor) Start() {
|
||||
if !m.options.Enabled || m.isRunning {
|
||||
return
|
||||
}
|
||||
|
||||
m.mutex.Lock()
|
||||
m.isRunning = true
|
||||
m.mutex.Unlock()
|
||||
|
||||
go m.monitorRoutine()
|
||||
}
|
||||
|
||||
// Stop 停止监控器
|
||||
func (m *Monitor) Stop() {
|
||||
if !m.isRunning {
|
||||
return
|
||||
}
|
||||
|
||||
m.mutex.Lock()
|
||||
m.isRunning = false
|
||||
m.mutex.Unlock()
|
||||
|
||||
m.stopChan <- struct{}{}
|
||||
}
|
||||
|
||||
// GetStats 获取当前缓存统计信息
|
||||
func (m *Monitor) GetStats() CacheStats {
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
|
||||
return m.stats
|
||||
}
|
||||
|
||||
// monitorRoutine 监控例程
|
||||
func (m *Monitor) monitorRoutine() {
|
||||
sampleTicker := time.NewTicker(m.options.SampleInterval)
|
||||
adjustTicker := time.NewTicker(m.options.AdjustInterval)
|
||||
|
||||
defer sampleTicker.Stop()
|
||||
defer adjustTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-m.stopChan:
|
||||
return
|
||||
case <-sampleTicker.C:
|
||||
m.collectSample()
|
||||
case <-adjustTicker.C:
|
||||
m.adjustCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collectSample 收集缓存样本
|
||||
func (m *Monitor) collectSample() {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
// 获取缓存统计信息
|
||||
hits, misses, hitRate := m.cache.Stats()
|
||||
size := m.cache.Size()
|
||||
|
||||
// 估算内存使用量(简化计算,实际应用中可能需要更精确的方法)
|
||||
memoryUsage := int64(size * 1024) // 假设每个缓存项平均占用1KB
|
||||
|
||||
// 创建新的统计样本
|
||||
newStat := CacheStats{
|
||||
Size: size,
|
||||
Hits: hits,
|
||||
Misses: misses,
|
||||
HitRate: hitRate,
|
||||
MemoryUsage: memoryUsage,
|
||||
LastAdjusted: time.Now(),
|
||||
}
|
||||
|
||||
// 添加到历史样本
|
||||
m.sampleHistory = append(m.sampleHistory, newStat)
|
||||
|
||||
// 限制历史样本数量,保留最近的100个样本
|
||||
if len(m.sampleHistory) > 100 {
|
||||
m.sampleHistory = m.sampleHistory[len(m.sampleHistory)-100:]
|
||||
}
|
||||
|
||||
// 更新当前统计信息
|
||||
m.stats = newStat
|
||||
m.stats.SamplesCount = len(m.sampleHistory)
|
||||
}
|
||||
|
||||
// adjustCache 根据统计信息调整缓存
|
||||
func (m *Monitor) adjustCache() {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
// 如果样本数量不足,不进行调整
|
||||
if len(m.sampleHistory) < 5 {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算平均命中率
|
||||
totalHitRate := 0.0
|
||||
for _, stat := range m.sampleHistory {
|
||||
totalHitRate += stat.HitRate
|
||||
}
|
||||
avgHitRate := totalHitRate / float64(len(m.sampleHistory))
|
||||
|
||||
// 获取当前缓存选项
|
||||
cacheOptions := m.cache.GetOptions()
|
||||
|
||||
// 根据命中率调整缓存大小
|
||||
newSize := cacheOptions.Size
|
||||
if avgHitRate < m.options.TargetHitRate {
|
||||
// 命中率低于目标,增加缓存大小
|
||||
newSize = int(float64(newSize) * m.options.SizeGrowthFactor)
|
||||
// 确保不超过最大大小
|
||||
if newSize > m.options.MaxSize {
|
||||
newSize = m.options.MaxSize
|
||||
}
|
||||
} else if avgHitRate > m.options.TargetHitRate+0.1 && m.stats.Size > m.options.MinSize {
|
||||
// 命中率远高于目标且缓存大小大于最小值,可以适当减小缓存
|
||||
newSize = int(float64(newSize) * m.options.SizeShrinkFactor)
|
||||
// 确保不小于最小大小
|
||||
if newSize < m.options.MinSize {
|
||||
newSize = m.options.MinSize
|
||||
}
|
||||
}
|
||||
|
||||
// 根据访问模式调整过期时间
|
||||
newExpiration := cacheOptions.Expiration
|
||||
if avgHitRate < m.options.TargetHitRate {
|
||||
// 命中率低,增加过期时间
|
||||
newExpiration = time.Duration(float64(newExpiration) * m.options.ExpirationFactor)
|
||||
} else if avgHitRate > m.options.TargetHitRate+0.1 {
|
||||
// 命中率高,可以适当减少过期时间
|
||||
newExpiration = time.Duration(float64(newExpiration) / m.options.ExpirationFactor)
|
||||
}
|
||||
|
||||
// 应用新的缓存选项
|
||||
if newSize != cacheOptions.Size || newExpiration != cacheOptions.Expiration {
|
||||
cacheOptions.Size = newSize
|
||||
cacheOptions.Expiration = newExpiration
|
||||
m.cache.UpdateOptions(cacheOptions)
|
||||
|
||||
// 更新最后调整时间
|
||||
m.stats.LastAdjusted = time.Now()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user