random-api-go/service/cache_manager.go

177 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"log"
"random-api-go/database"
"random-api-go/model"
"sync"
"time"
)
// CacheManager 缓存管理器
type CacheManager struct {
memoryCache map[string]*CachedEndpoint
mutex sync.RWMutex
}
// 注意CachedEndpoint 类型定义在 endpoint_service.go 中
// NewCacheManager 创建缓存管理器
func NewCacheManager() *CacheManager {
cm := &CacheManager{
memoryCache: make(map[string]*CachedEndpoint),
}
// 启动定期清理过期缓存的协程
go cm.cleanupExpiredCache()
return cm
}
// GetFromMemoryCache 从内存缓存获取数据
func (cm *CacheManager) GetFromMemoryCache(key string) ([]string, bool) {
cm.mutex.RLock()
defer cm.mutex.RUnlock()
cached, exists := cm.memoryCache[key]
if !exists || len(cached.URLs) == 0 {
return nil, false
}
return cached.URLs, true
}
// SetMemoryCache 设置内存缓存duration参数保留以兼容现有接口但不再使用
func (cm *CacheManager) SetMemoryCache(key string, urls []string, duration time.Duration) {
cm.mutex.Lock()
defer cm.mutex.Unlock()
cm.memoryCache[key] = &CachedEndpoint{
URLs: urls,
}
}
// InvalidateMemoryCache 清理指定key的内存缓存
func (cm *CacheManager) InvalidateMemoryCache(key string) {
cm.mutex.Lock()
defer cm.mutex.Unlock()
delete(cm.memoryCache, key)
}
// GetFromDBCache 从数据库缓存获取URL
func (cm *CacheManager) GetFromDBCache(dataSourceID uint) ([]string, error) {
var cachedURLs []model.CachedURL
if err := database.DB.Where("data_source_id = ? AND expires_at > ?", dataSourceID, time.Now()).
Find(&cachedURLs).Error; err != nil {
return nil, err
}
var urls []string
for _, cached := range cachedURLs {
urls = append(urls, cached.FinalURL)
}
return urls, nil
}
// SetDBCache 设置数据库缓存
func (cm *CacheManager) SetDBCache(dataSourceID uint, urls []string, duration time.Duration) error {
// 先删除旧的缓存
if err := database.DB.Where("data_source_id = ?", dataSourceID).Delete(&model.CachedURL{}).Error; err != nil {
log.Printf("Failed to delete old cache for data source %d: %v", dataSourceID, err)
}
// 插入新的缓存
expiresAt := time.Now().Add(duration)
for _, url := range urls {
cachedURL := model.CachedURL{
DataSourceID: dataSourceID,
OriginalURL: url,
FinalURL: url,
ExpiresAt: expiresAt,
}
if err := database.DB.Create(&cachedURL).Error; err != nil {
log.Printf("Failed to cache URL: %v", err)
}
}
return nil
}
// UpdateDBCacheIfChanged 只有当数据变化时才更新数据库缓存,并返回是否需要清理内存缓存
func (cm *CacheManager) UpdateDBCacheIfChanged(dataSourceID uint, newURLs []string, duration time.Duration) (bool, error) {
// 获取现有缓存
existingURLs, err := cm.GetFromDBCache(dataSourceID)
if err != nil {
// 如果获取失败,直接设置新缓存
return true, cm.SetDBCache(dataSourceID, newURLs, duration)
}
// 比较URL列表是否相同
if cm.urlSlicesEqual(existingURLs, newURLs) {
// 数据没有变化,只更新过期时间
expiresAt := time.Now().Add(duration)
if err := database.DB.Model(&model.CachedURL{}).
Where("data_source_id = ?", dataSourceID).
Update("expires_at", expiresAt).Error; err != nil {
log.Printf("Failed to update cache expiry for data source %d: %v", dataSourceID, err)
}
return false, nil
}
// 数据有变化,更新缓存
return true, cm.SetDBCache(dataSourceID, newURLs, duration)
}
// InvalidateMemoryCacheForDataSource 清理与数据源相关的内存缓存
func (cm *CacheManager) InvalidateMemoryCacheForDataSource(dataSourceID uint) error {
// 获取数据源信息
var dataSource model.DataSource
if err := database.DB.Preload("Endpoint").First(&dataSource, dataSourceID).Error; err != nil {
return err
}
// 清理该端点的内存缓存
cm.InvalidateMemoryCache(dataSource.Endpoint.URL)
log.Printf("已清理端点 %s 的内存缓存(数据源 %d 数据发生变化)", dataSource.Endpoint.URL, dataSourceID)
return nil
}
// urlSlicesEqual 比较两个URL切片是否相等
func (cm *CacheManager) urlSlicesEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
// 创建map来比较
urlMap := make(map[string]bool)
for _, url := range a {
urlMap[url] = true
}
for _, url := range b {
if !urlMap[url] {
return false
}
}
return true
}
// cleanupExpiredCache 定期清理过期的数据库缓存(内存缓存不再自动过期)
func (cm *CacheManager) cleanupExpiredCache() {
ticker := time.NewTicker(10 * time.Minute)
defer ticker.Stop()
for range ticker.C {
now := time.Now()
// 内存缓存不再自动过期,只清理数据库中的过期缓存
if err := database.DB.Where("expires_at < ?", now).Delete(&model.CachedURL{}).Error; err != nil {
log.Printf("Failed to cleanup expired cache: %v", err)
}
}
}