GO 自定義Cache
阿新 • • 發佈:2021-10-16
GO 自定義Cache
DEMO
package main import ( "fmt" "sync" "time" ) // 快取物件 type CacheItem struct { Value interface{} // 實際快取的物件 TTL time.Duration // 存活時間 CreatedAt time.Time // 建立時間,和 TTL 一起決定是否過期 } // 快取是否過期 func (c *CacheItem) Expired() bool { return time.Now().Sub(c.CreatedAt) > c.TTL } // 本地快取實現類 type LocalCache struct { sync.RWMutex // 繼承讀寫鎖,用於併發控制 Items map[string]*CacheItem // K-V儲存 GCDuration time.Duration // 惰性刪除, 後臺執行時間間隔 } // 新建本地快取 func NewLocalCache(gcDuration time.Duration) *LocalCache { localCache := &LocalCache{Items: map[string]*CacheItem{}, GCDuration: gcDuration} // 啟動協程,定期掃描過期鍵,進行刪除 go localCache.GC() return localCache } // 存入物件 func (lc *LocalCache) Set(key string, value interface{}, ttl time.Duration) { lc.Lock() defer lc.Unlock() lc.Items[key] = &CacheItem{ Value: value, TTL: ttl, CreatedAt: time.Now(), } } // 查詢物件 key不存在或過期返回: nil 正常返回: 實際儲存的物件 CacheItem.Value func (lc *LocalCache) Get(key string) interface{} { lc.RLock() defer lc.RUnlock() if item, ok := lc.Items[key]; ok { if !item.Expired() { return item.Value } else { // 鍵已過期, 直接刪除 // 需要注意的是,這裡不能呼叫lc.Del()方法,因為go的讀寫鎖是不支援鎖升級的 delete(lc.Items, key) } } return nil } // 刪除快取 func (lc *LocalCache) Del(key string) { lc.Lock() defer lc.Unlock() if _, ok := lc.Items[key]; ok { delete(lc.Items, key) } } // 非同步執行,掃描過期鍵並刪除 func (lc *LocalCache) GC() { for { select { case <-time.After(lc.GCDuration): keysToExpire := []string{} lc.RLock() for key, item := range lc.Items { if item.Expired() { keysToExpire = append(keysToExpire, key) } } lc.RUnlock() for _, k := range keysToExpire { lc.Del(k) } } } } func main() { // 建立一個每 10 秒鐘惰性清理過期物件的 cache cache := NewLocalCache(10 * time.Second) // 獲取一個不存在的 key get, _ := cache.Get("todo").(string) fmt.Printf("get = %v\n", get) // 設定 過期時間為 5秒 的 k-v cache.Set("todo", "something", 5*time.Second) // 獲取存在且未過期的 k-v get2, _ := cache.Get("todo").(string) fmt.Printf("get2 = %v\n", get2) // 獲取過期的 k-v, 並檢視是否被清理 time.Sleep(5 * time.Second) get3, _ := cache.Get("todo").(string) fmt.Printf("get3 = %v\n", get3) fmt.Printf("cache = %v\n", cache) // ---------------------------- // 設定一個5秒過期的 k-v 等待惰性回收時間後 檢視是否被清理 cache.Set("test", "hello", 5*time.Second) get4, _ := cache.Get("test").(string) fmt.Printf("get4 = %v\n", get4) fmt.Printf("cache = %v\n", cache) time.Sleep(10*time.Second) fmt.Printf("等待10s後cache = %v\n", cache) }