最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang中本地緩存庫cache2go的使用小結

 更新時間:2026年04月07日 10:32:31   作者:BoringRong  
本文介紹了Go語言本地緩存庫cache2go的使用方法和源碼分析,cache2go提供了并發(fā)安全的讀寫操作,支持過期時間控制,下面就來詳細的介紹cache2go如何創(chuàng)建、添加、讀取、刪除緩存項及設置回調函數(shù),感興趣的可以了解一下

1.背景

1.1.項目介紹

cache2go是一款由golang實現(xiàn)的本地緩存庫,提供并發(fā)安全的讀寫操作,具有過期時間控制等特性。項目地址:https://github.com/muesli/cache2go

1.2.使用方法

go get github.com/muesli/cache2go

  • 核心操作API:
    • Cache:創(chuàng)建Cache Table
    • Add:添加Cache
    • Value:讀取Cache
    • Delete:刪除指定Key Cache
    • Flush:清空整個Cache Table
package main
import (
  "github.com/muesli/cache2go"
  "log"
  "time"
)
type Item struct {
  Name   string `json:"name"`
  Prices int64  `json:"prices"`
  Stocks int64  `json:"stocks"`
}
func basicOpTest() {
  // 初始化itemCache本地緩存
  itemCache := cache2go.Cache("itemCache")
  item := &Item{
     Name:   "MacBookPro",
     Prices: 10000,
     Stocks: 1,
  }
  // 添加item1緩存,過期時間為5秒鐘
  itemCache.Add("item1", 5*time.Second, item)
  // 讀取item1緩存
  if v, err := itemCache.Value("item1"); err != nil {
     log.Printf("item1 err = %v", err)
  } else {
     log.Printf("讀取item1緩存:%#v", v.Data())
  }
  // 睡眠6s后讀取
  time.Sleep(6 * time.Second)
  if v, err := itemCache.Value("item1"); err != nil {
     log.Printf("item1 err = %v", err)
  } else {
     log.Printf("6s后讀取item1緩存:%#v", v.Data())
  }
  // 添加item2,不設置過期時間
  itemCache.Add("item2", 0, item)
  // 讀取item2緩存
  if v, err := itemCache.Value("item2"); err != nil {
     log.Printf("item2 err = %v", err)
  } else {
     log.Printf("讀取item2緩存:%#v", v.Data())
  }
  // 刪除掉item2緩存
  itemCache.Delete("item2")
  // 再讀取item2緩存
  if v, err := itemCache.Value("item2"); err != nil {
     log.Printf("item2 err = %v", err)
  } else {
     log.Printf("讀取item2緩存:%#v", v.Data())
  }
  // 添加item3緩存,并刪除所有緩存
  itemCache.Add("item3", 0, item)
  itemCache.Flush()
  // 讀取item3緩存
  if v, err := itemCache.Value("item3"); err != nil {
     log.Printf("item3 err = %v", err)
  } else {
     log.Printf("讀取item3緩存:%#v", v.Data())
  }
}

運行結果:

2022/10/17 20:52:00 讀取item1緩存:&main.Item{Name:"MacBookPro", Prices:10000, Stocks:1}
2022/10/17 20:52:06 item1 err = Key not found in cache
2022/10/17 20:52:06 讀取item2緩存:&main.Item{Name:"MacBookPro", Prices:10000, Stocks:1}
2022/10/17 20:52:06 item2 err = Key not found in cache
2022/10/17 20:52:06 item3 err = Key not found in cache 

  • 操作回調API:
    • AddAddedItemCallback:新增Cache回調函數(shù)
    • AddAboutToDeleteItemCallback:刪除Cache回調函數(shù)
    • AddAboutToExpireCallback:過期CacheItem回調函數(shù)
    • 以上三個方法分別再對應著RemoveXXX,表示刪去對應操作的全部回調函數(shù)
func callBackTest() {
   // 初始化itemCache本地緩存
   itemCache := cache2go.Cache("itemCache")
   // 設置各操作回調函數(shù)
   itemCache.AddAddedItemCallback(func(item *cache2go.CacheItem) {
      log.Printf("added callback, item = %#v", item)
   })
   itemCache.AddAboutToDeleteItemCallback(func(item *cache2go.CacheItem) {
      log.Printf("deleted callback, item = %#v", item)
   })
   item := itemCache.Add("expire_item", 1*time.Second, Item{
      Name:   "expire_item",
      Prices: 1,
      Stocks: 1,
   })
   item.AddAboutToExpireCallback(func(item interface{}) {
      log.Printf("expired callback, item = %#v", item)
   })
   // 執(zhí)行基本操作
   basicOpTest()
}

輸出結果

2022/10/17 21:12:09 added callback, item = &cache2go.CacheItem{RWMutex:sync.RWMutex{w:sync.Mutex{state:0, sema:0x0}, writerSem:0x0, readerSem:0x0, readerCount:0, readerWait:0}, key:"item1", data:(*main.Item)(0xc00008c040), lifeSpan:5000000000, createdOn:time.Time{wall:0xc0cb730a55e5a2f8, ext:426392, loc:(*time.Location)(0x1187880)}, accessedOn:time.Time{wall:0xc0cb730a55e5a2f8, ext:426392, loc:(*time.Location)(0x1187880)}, accessCount:0, aboutToExpire:[]func(interface {})(nil)}
2022/10/17 21:12:09 讀取item1緩存:&main.Item{Name:"MacBookPro", Prices:10000, Stocks:1}
2022/10/17 21:12:10 deleted callback, item = &cache2go.CacheItem{RWMutex:sync.RWMutex{w:sync.Mutex{state:0, sema:0x0}, writerSem:0x0, readerSem:0x0, readerCount:0, readerWait:0}, key:"expire_item", data:main.Item{Name:"expire_item", Prices:1, Stocks:1}, lifeSpan:1000000000, createdOn:time.Time{wall:0xc0cb730a55e4d7d8, ext:374551, loc:(*time.Location)(0x1187880)}, accessedOn:time.Time{wall:0xc0cb730a55e4d7d8, ext:374551, loc:(*time.Location)(0x1187880)}, accessCount:0, aboutToExpire:[]func(interface {}){(func(interface {}))(0x10a0530)}}
2022/10/17 21:12:10 expired callback, item = "expire_item"
2022/10/17 21:12:14 deleted callback, item = &cache2go.CacheItem{RWMutex:sync.RWMutex{w:sync.Mutex{state:0, sema:0x0}, writerSem:0x0, readerSem:0x0, readerCount:0, readerWait:0}, key:"item1", data:(*main.Item)(0xc00008c040), lifeSpan:5000000000, createdOn:time.Time{wall:0xc0cb730a55e5a2f8, ext:426392, loc:(*time.Location)(0x1187880)}, accessedOn:time.Time{wall:0xc0cb730a55eaa820, ext:755728, loc:(*time.Location)(0x1187880)}, accessCount:1, aboutToExpire:[]func(interface {})(nil)}
// ... 

  • 設置自定義緩存加載器:SetDataLoader
func dataLoaderTest() {
   // 初始化itemCache本地緩存
   redisItemCache := cache2go.Cache("redisItemCache")
   // 設置自定義的cache加載邏輯
   redisItemCache.SetDataLoader(func(key interface{}, args ...interface{}) *cache2go.CacheItem {
      // 如果是redis開頭的key,先從redis中獲取
      if strings.HasPrefix(key.(string), "redis") {
         return cache2go.NewCacheItem(key, 0, Item{
            Name: "redis_item",
         })
      }
      return nil
   })
   // 寫入一條數(shù)據(jù)
   redisItemCache.Add("item1", 0, Item{
      Name: "item1",
   })
   item1, _ := redisItemCache.Value("item1")
   log.Printf("item1 = %#v", item1)
   redisItem, _ := redisItemCache.Value("redis_item")
   log.Printf("redisItem = %#v", redisItem)
}

輸出結果

2022/10/17 21:59:37 item1 = &cache2go.CacheItem{RWMutex:sync.RWMutex{w:sync.Mutex{state:0, sema:0x0}, writerSem:0x0, readerSem:0x0, readerCount:0, readerWait:0}, key:"item1", data:main.Item{Name:"item1", Prices:0, Stocks:0}, lifeSpan:0, createdOn:time.Time{wall:0xc0cb75d2601954b8, ext:492934, loc:(*time.Location)(0x11858c0)}, accessedOn:time.Time{wall:0xc0cb75d260196840, ext:497913, loc:(*time.Location)(0x11858c0)}, accessCount:1, aboutToExpire:[]func(interface {})(nil)}
2022/10/17 21:59:37 redisItem = &cache2go.CacheItem{RWMutex:sync.RWMutex{w:sync.Mutex{state:0, sema:0x0}, writerSem:0x0, readerSem:0x0, readerCount:0, readerWait:0}, key:"redis_item", data:main.Item{Name:"redis_item", Prices:0, Stocks:0}, lifeSpan:0, createdOn:time.Time{wall:0xc0cb75d2601d34e8, ext:746274, loc:(*time.Location)(0x11858c0)}, accessedOn:time.Time{wall:0xc0cb75d2601d34e8, ext:746274, loc:(*time.Location)(0x11858c0)}, accessCount:0, aboutToExpire:[]func(interface {})(nil)}

2.源碼分析

2.1.項目結構

核心代碼文件為:

  • cachetable.go:封裝了CacheTable結構體,實現(xiàn)Cache列表相關操作API
  • cacheitem.go:封裝了CacheItem結構體,實現(xiàn)了Cache對象相關操作API
  • cache.go:提供cache全局map,存儲了CacheTable結構體與table名稱映射

2.2.數(shù)據(jù)結構

  • CacheTable
type CacheTable struct {
   sync.RWMutex
   // The table's name.
   name string
   // All cached items.
   items map[interface{}]*CacheItem
   // Timer responsible for triggering cleanup.
   cleanupTimer *time.Timer
   // Current timer duration.
   cleanupInterval time.Duration
   // The logger used for this table.
   logger *log.Logger
   // Callback method triggered when trying to load a non-existing key.
   loadData func(key interface{}, args ...interface{}) *CacheItem
   // Callback method triggered when adding a new item to the cache.
   addedItem []func(item *CacheItem)
   // Callback method triggered before deleting an item from the cache.
   aboutToDeleteItem []func(item *CacheItem)
}
  • CacheItem
type CacheItem struct {
   sync.RWMutex
   // The item's key.
   key interface{}
   // The item's data.
   data interface{}
   // How long will the item live in the cache when not being accessed/kept alive.
   lifeSpan time.Duration
   // Creation timestamp.
   createdOn time.Time
   // Last access timestamp.
   accessedOn time.Time
   // How often the item was accessed.
   accessCount int64
   // Callback method triggered right before removing the item from the cache
   aboutToExpire []func(key interface{})
}

2.3.API代碼流程

1.Cache

位于cache.go文件,維護了全局CacheTable Map。

var (
   // 全局cache map
   cache = make(map[string]*CacheTable)
   // cache map 讀寫鎖
   mutex sync.RWMutex
)
// 從cache map中獲取對應的CacheTable,不存在則創(chuàng)建新的
func Cache(table string) *CacheTable {
   // 先上讀鎖,獲取cacheTable
   mutex.RLock()
   t, ok := cache[table]
   mutex.RUnlock()
   // 不存在,則新建
   if !ok {
      // 寫操作需要上寫鎖
      mutex.Lock()
      t, ok = cache[table]
      // 雙重校驗是否存在
      if !ok {
         // 不存在則新建cacheTable
         t = &CacheTable{
            name:  table,
            items: make(map[interface{}]*CacheItem),
         }
         cache[table] = t
      }
      mutex.Unlock()
   }
   return t
}

2.Add

位于cachetable.go文件,是CacheTable結構體的方法之一,實現(xiàn)了添加KV緩存的邏輯。

func (table *CacheTable) Add(key interface{}, lifeSpan time.Duration, data interface{}) *CacheItem {
   // 封裝一個item
   item := NewCacheItem(key, lifeSpan, data)
   // 鎖表,將item添加進去
   table.Lock()
   table.addInternal(item)
   return item
}
func (table *CacheTable) addInternal(item *CacheItem) {
   // 添加kv值到map中
   table.items[item.key] = item
   expDur := table.cleanupInterval
   addedItem := table.addedItem
   // 添加完成解除寫鎖
   table.Unlock()
   // 觸發(fā)Add回調函數(shù)
   if addedItem != nil {
      for _, callback := range addedItem {
         callback(item)
      }
   }
   // 如果一個item有設置過期時間,且比檢查失效間隔小,則進行過期key清理(懶加載思想,只有存在這類Key才會啟動清理,而不是定時任務)
   if item.lifeSpan > 0 && (expDur == 0 || item.lifeSpan < expDur) {
      table.expirationCheck()
   }
}
func (table *CacheTable) expirationCheck() {
   table.Lock()
   if table.cleanupTimer != nil {
      table.cleanupTimer.Stop()
   }
   if table.cleanupInterval > 0 {
      table.log("Expiration check triggered after", table.cleanupInterval, "for table", table.name)
   } else {
      table.log("Expiration check installed for table", table.name)
   }
   now := time.Now()
   smallestDuration := 0 * time.Second
   for key, item := range table.items {
      // 遍歷該table下的所有items
      item.RLock()
      lifeSpan := item.lifeSpan
      accessedOn := item.accessedOn
      item.RUnlock()
      if lifeSpan == 0 {
         continue
      }
      if now.Sub(accessedOn) >= lifeSpan {
         // 該item已超出存活時間,刪除key
         table.deleteInternal(key)
      } else {
         // 找到最小的需要過期的item,計算最優(yōu)時間間隔
         if smallestDuration == 0 || lifeSpan-now.Sub(accessedOn) < smallestDuration {
            smallestDuration = lifeSpan - now.Sub(accessedOn)
         }
      }
   }
   // 在最優(yōu)時間間隔后啟動定時任務檢查table的過期key
   table.cleanupInterval = smallestDuration
   if smallestDuration > 0 {
      table.cleanupTimer = time.AfterFunc(smallestDuration, func() {
         go table.expirationCheck()
      })
   }
   table.Unlock()
}

3.Value

Value用于讀取Key匹配的CacheItem。

func (table *CacheTable) Value(key interface{}, args ...interface{}) (*CacheItem, error) {
   table.RLock()
   // 先嘗試從items中獲取該item
   r, ok := table.items[key]
   loadData := table.loadData
   table.RUnlock()
   if ok {
      // 如果存在,則更新該item的accessOn時間和accessCount計數(shù)
      r.KeepAlive()
      return r, nil
   }
   // 如果不存在,則從loadData自定義加載函數(shù)中嘗試獲取
   if loadData != nil {
      item := loadData(key, args...)
      if item != nil {
         // 如果自定義加載函數(shù)中存在該item,則添加到table中并返回
         table.Add(key, item.lifeSpan, item.data)
         return item, nil
      }
      return nil, ErrKeyNotFoundOrLoadable
   }
   return nil, ErrKeyNotFound
}

4.Delete

Delete函數(shù)用于刪除指定Key的Item。

func (table *CacheTable) Delete(key interface{}) (*CacheItem, error) {
   table.Lock()
   defer table.Unlock()
   return table.deleteInternal(key)
}
func (table *CacheTable) deleteInternal(key interface{}) (*CacheItem, error) {
   // 判斷key是否存在
   r, ok := table.items[key]
   if !ok {
      return nil, ErrKeyNotFound
   }
   aboutToDeleteItem := table.aboutToDeleteItem
   table.Unlock()
   // 先觸發(fā)刪除回調函數(shù)
   if aboutToDeleteItem != nil {
      for _, callback := range aboutToDeleteItem {
         callback(r)
      }
   }
   r.RLock()
   defer r.RUnlock()
   // 觸發(fā)item的過期回調函數(shù)
   if r.aboutToExpire != nil {
      for _, callback := range r.aboutToExpire {
         callback(key)
      }
   }
   table.Lock()
   table.log("Deleting item with key", key, "created on", r.createdOn, "and hit", r.accessCount, "times from table", table.name)
   // 將item從table中刪除
   delete(table.items, key)
   return r, nil
}

5.Flush

清空整個table的cache。

func (table *CacheTable) Flush() {
   table.Lock()
   defer table.Unlock()
   table.log("Flushing table", table.name)
   // 直接將items重新初始化
   table.items = make(map[interface{}]*CacheItem)
   table.cleanupInterval = 0
   if table.cleanupTimer != nil {
      // 如果此時還有清理過期定時器,則終止其運行
      table.cleanupTimer.Stop()
   }
}

3.總結

cache2go這個項目寫得很精簡,本質上就是使用到了map來作為本地緩存kv存儲結構,但是有一些值得學習的地方:

  • 使用map時,由于其是非線程安全的,所以在并發(fā)場景下需要上鎖,可以選擇RWMutex讀寫鎖來控制并發(fā)的讀和串行寫,避免panic
  • 對于需要清理過期Key的場景,如果使用定時任務定時遍歷整個集合來做清理,會耗費較多時間和資源,可以由寫入時判斷是否存在需要清理的Key,再啟動定時任務來做清理,避免頻繁遍歷
  • 可以利用golang函數(shù)式的特性,方便地實現(xiàn)各操作回調函數(shù),比如添加、刪除、失效操作回調等
  • 另外個人覺得這個項目可以優(yōu)化的空間:由于使用的是本地緩存,為了避免內存oom可以在創(chuàng)建時指定限制key的最大數(shù)量,以及在內存不足時的寫入策略(如直接報錯或者隨機清理掉一批Key等)。

到此這篇關于Golang中本地緩存庫cache2go的使用小結的文章就介紹到這了,更多相關Golang本地緩存庫cache2go內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Go?Wails開發(fā)桌面應用使用示例探索

    Go?Wails開發(fā)桌面應用使用示例探索

    這篇文章主要為大家介紹了Go?Wails的使用示例探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • Golang實現(xiàn)HTTP編程請求和響應

    Golang實現(xiàn)HTTP編程請求和響應

    本文主要介紹了Golang實現(xiàn)HTTP編程請求和響應,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • go:垃圾回收GC觸發(fā)條件詳解

    go:垃圾回收GC觸發(fā)條件詳解

    這篇文章主要介紹了go:垃圾回收GC觸發(fā)條件詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang flag包的具體使用

    Golang flag包的具體使用

    本文主要介紹了Golang flag包的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • 一文初探?Goroutine?與?channel基本用法

    一文初探?Goroutine?與?channel基本用法

    這篇文章主要為大家介紹了一文初探?Goroutine?與?channel基本用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • 一文帶你深入理解Golang Context包

    一文帶你深入理解Golang Context包

    在 Go 語言中,Context 包是一種非常常用的工具,它被用來管理 goroutine 之間的通信和取消。本文將深入探討Context 包的基本原理,包括使用場景、原理和一些最佳實踐,需要的可以參考下
    2023-05-05
  • Golang限流器time/rate設計與實現(xiàn)詳解

    Golang限流器time/rate設計與實現(xiàn)詳解

    在?Golang?庫中官方給我們提供了限流器的實現(xiàn)golang.org/x/time/rate,它是基于令牌桶算法(Token?Bucket)設計實現(xiàn)的,下面我們就來看看他的具體使用吧
    2024-03-03
  • 一文搞懂Go語言中defer關鍵字的使用

    一文搞懂Go語言中defer關鍵字的使用

    defer是golang中用的比較多的一個關鍵字,也是go面試題里經常出現(xiàn)的問題。今天就來整理一下關于defer的學習使用,希望對需要的朋友有所幫助
    2022-09-09
  • go語言如何導入和使用包示例詳解

    go語言如何導入和使用包示例詳解

    這篇文章主要為大家介紹了go語言如何導入和使用包示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • 使用Go調用OpenAI兼容API模型

    使用Go調用OpenAI兼容API模型

    本文介紹了使用Go語言調用兼容OpenAI API的模型(LMStudio/Ollama)的方法,主要涵蓋了非流式對話、流式打字機效果、多輪上下文對話以及圖片交互等內容,文章詳細解釋了如何準備環(huán)境,實現(xiàn)非流式對話的特點,需要的朋友可以參考下
    2026-05-05

最新評論

保靖县| 克什克腾旗| 翁源县| 东至县| 海宁市| 云龙县| 宿松县| 尉氏县| 黎平县| 隆子县| 岫岩| 廊坊市| 赣榆县| 石首市| 沅陵县| 曲麻莱县| 科技| 宜州市| 鞍山市| 获嘉县| 海丰县| 武城县| 江北区| 富蕴县| 马龙县| 汝城县| 建瓯市| 连山| 海兴县| 鄯善县| 鲁甸县| 遂平县| 呼和浩特市| 天祝| 合川市| 呼玛县| 内丘县| 麻城市| 辽宁省| 理塘县| 海伦市|