beego解讀Cache之MemoryCache

来源:https://www.cnblogs.com/asp-poor-as-john/archive/2019/01/19/go_beego_cache_memorycache.html
-Advertisement-
Play Games

beego 框架 cache 目錄是 Go 實現的一個緩存管理器,是beego框架自帶工具之一,當然,如果你只想使用 cache 而不是整個 beego 框架,可以選擇性安裝: go get github.com/astaxie/beego/cache 在我寫這篇博文時, beego 版本是 v1. ...


beego 框架 cache 目錄是 Go 實現的一個緩存管理器,是beego框架自帶工具之一,當然,如果你只想使用 cache 而不是整個 beego 框架,可以選擇性安裝:

go get github.com/astaxie/beego/cache

在我寫這篇博文時, beego 版本是 v1.11.1 , cache 支持記憶體、文件、 memcache 、 redis、ssdb 。

使用例子:

package main

import (
    // 導入cache包
    "github.com/astaxie/beego/cache"
    "time"
)

func  main()  {
    /*
    bm, err := cache.NewCache("memcache", `{"conn":"127.0.0.1:11211"}`)//memcache
    bm, err := cache.NewCache("redis", `{"conn":":6039"}`)//redis
    bm, err := cache.NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)

     */
    // 初始化一個記憶體的緩存管理器
    bm, err := cache.NewCache("memory", `{"interval":60}`)
    bm.Put("user", "張三", 10 * time.Second) //
    bm.Get("user")
    bm.IsExist("user")
    bm.Delete("user")
}

cache 包定義了Cache介面,如下:

// Cache interface contains all behaviors for cache adapter.
// usage:
//  cache.Register("file",cache.NewFileCache) // this operation is run in init method of file.go.
//  c,err := cache.NewCache("file","{....}")
//  c.Put("key",value, 3600 * time.Second)
//  v := c.Get("key")
//
//  c.Incr("counter")  // now is 1
//  c.Incr("counter")  // now is 2
//  count := c.Get("counter").(int)
type Cache interface {
    // Get 函數通過鍵獲取值。
    Get(key string) interface{}
    // GetMulti 是 Get 的一個批處理版本。
    GetMulti(keys []string) []interface{}
    // Put 函數設置存入一對鍵值和到期時間。
    Put(key string, val interface{}, timeout time.Duration) error
    // 通過鍵刪除值.
    Delete(key string) error
    // 讓鍵對應的值加1.
    Incr(key string) error
    // 讓鍵對應的值減1.
    Decr(key string) error
    // 檢查一個鍵是否存在.
    IsExist(key string) bool
    // 清空所有緩存.
    ClearAll() error
    // 根據配置啟動一個gc協程。
    StartAndGC(config string) error
}

cache.go文件定義了 Instance , Instance 是一個 function ,功能是創建一個Cache介面的實例。adapters是一個 map 用於註冊實現了 Cache 的適配器。Register註冊可用的適配器,如果被調用兩次或者驅動為空,將 panic 。代碼如下:

type Instance func() Cache
var adapters = make(map[string]Instance)
func Register(name string, adapter Instance) {
    if adapter == nil {
        panic("cache: Register adapter is nil")
    }
    if _, ok := adapters[name]; ok {
        panic("cache: Register called twice for adapter " + name)
    }
    adapters[name] = adapter
}

我們來先看記憶體緩存的實現方式:

1.MemoryCache memory,go

全局變數及結構

DefaultEvery 表示在記憶體中回收過期緩存項的時間,預設為 1 分鐘。

var DefaultEvery = 60

MemoryItem 用於保存基本記憶體緩存元素.

type MemoryItem struct {
    val         interface{}
    createdTime time.Time
    lifespan    time.Duration
}

MemoryCache 是一個記憶體管理適配器,它包含了一個讀寫鎖(sync.RWMutex),使得 MemoryCache 具備併發安全性。

type MemoryCache struct {
    sync.RWMutex
    dur   time.Duration
    items map[string]*MemoryItem
    Every int // run an expiration check Every clock time
}

方法及函數

init function 註冊驅動名,其實就是一個map存取操作,見上面的 Register 函數。

func init() {
    Register("memory", NewMemoryCache)
}

NewMemoryCache 初始化一個記憶體緩存適配器,返回 Cache 介面,NewMemoryCache 實現了 Cache 介面。

func NewMemoryCache() Cache {
    cache := MemoryCache{items: make(map[string]*MemoryItem)}
    return &cache
}

Get 返回一個鍵在 MemoryCache 中的值, 如果該鍵不存在或到期,返回nil。

func (bc *MemoryCache) Get(name string) interface{} {
    bc.RLock()
    defer bc.RUnlock()
    
    if itm, ok := bc.items[name]; ok {
        if itm.isExpire() {
            return nil
        }
        return itm.val
    }
    return nil
}

GetMulti 傳入一個鍵的 slice,返回slice中存在於緩存且沒有過期的值.

func (bc *MemoryCache) GetMulti(names []string) []interface{} {
    var rc []interface{}
    for _, name := range names {
        rc = append(rc, bc.Get(name))
    }
    return rc
}

Put 向 MemoryCache 中存入一個鍵名位name ,值為 item 的緩存鍵值對並設置時間為 lifespan ,如果時間為 0 ,那麼 item 將永遠存在,除非用戶主動刪除或。

func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error {
    bc.Lock()
    defer bc.Unlock()
    bc.items[name] = &MemoryItem{
        val:         value,
        createdTime: time.Now(),
        lifespan:    lifespan,
    }
    return nil
}

Delete 刪除 MemoryCache 中鍵名為 name 的緩存.

func (bc *MemoryCache) Delete(name string) error {
    bc.Lock()
    defer bc.Unlock()
    if _, ok := bc.items[name]; !ok {
        return errors.New("key not exist")
    }
    delete(bc.items, name)
    if _, ok := bc.items[name]; ok {
        return errors.New("delete key error")
    }
    return nil
}

Incr 增加鍵名為 name 緩存的值,支持類型:int,int32,int64,uint,uint32,uint64.

func (bc *MemoryCache) Incr(key string) error {
    bc.RLock()
    defer bc.RUnlock()
    itm, ok := bc.items[key]
    if !ok {
        return errors.New("key not exist")
    }
    switch itm.val.(type) {
    case int:
        itm.val = itm.val.(int) + 1
    case int32:
        itm.val = itm.val.(int32) + 1
    case int64:
        itm.val = itm.val.(int64) + 1
    case uint:
        itm.val = itm.val.(uint) + 1
    case uint32:
        itm.val = itm.val.(uint32) + 1
    case uint64:
        itm.val = itm.val.(uint64) + 1
    default:
        return errors.New("item val is not (u)int (u)int32 (u)int64")
    }
    return nil
}

Decr 減少鍵名為 name 緩存的值,支持類型:int,int32,int64,uint,uint32,uint64,如果類型為 uint,uint32,uint64 且值為 0 時,會返回值小於0錯誤。

func (bc *MemoryCache) Decr(key string) error {
    bc.RLock()
    defer bc.RUnlock()
    itm, ok := bc.items[key]
    if !ok {
        return errors.New("key not exist")
    }
    switch itm.val.(type) {
    case int:
        itm.val = itm.val.(int) - 1
    case int64:
        itm.val = itm.val.(int64) - 1
    case int32:
        itm.val = itm.val.(int32) - 1
    case uint:
        if itm.val.(uint) > 0 {
            itm.val = itm.val.(uint) - 1
        } else {
            return errors.New("item val is less than 0")
        }
    case uint32:
        if itm.val.(uint32) > 0 {
            itm.val = itm.val.(uint32) - 1
        } else {
            return errors.New("item val is less than 0")
        }
    case uint64:
        if itm.val.(uint64) > 0 {
            itm.val = itm.val.(uint64) - 1
        } else {
            return errors.New("item val is less than 0")
        }
    default:
        return errors.New("item val is not int int64 int32")
    }
    return nil
}

IsExist 檢查鍵為 name 的緩存是否存在.

func (bc *MemoryCache) IsExist(name string) bool {
    bc.RLock()
    defer bc.RUnlock()
    if v, ok := bc.items[name]; ok {
        return !v.isExpire()
    }
    return false
}

ClearAll 會清除所有緩存.


func (bc *MemoryCache) ClearAll() error {
    bc.Lock()
    defer bc.Unlock()
    bc.items = make(map[string]*MemoryItem)
    return nil
}

StartAndGC 開始周期性對緩存進行檢查,如果緩存鍵值對過期,會被刪除。

func (bc *MemoryCache) StartAndGC(config string) error {
    var cf map[string]int
    json.Unmarshal([]byte(config), &cf)
    if _, ok := cf["interval"]; !ok {
        cf = make(map[string]int)
        cf["interval"] = DefaultEvery
    }
    dur := time.Duration(cf["interval"]) * time.Second
    bc.Every = cf["interval"]
    bc.dur = dur
    go bc.vacuum()
    return nil
}

未導出函數及方法

// 檢查是否超時.
func (bc *MemoryCache) vacuum() {
    bc.RLock()
    every := bc.Every
    bc.RUnlock()

    if every < 1 {
        return
    }
    for {
        <-time.After(bc.dur)
        if bc.items == nil {
            return
        }
        if keys := bc.expiredKeys(); len(keys) != 0 {
            bc.clearItems(keys)
        }
    }
}

// expiredKeys 返回到期的鍵名 slice.
func (bc *MemoryCache) expiredKeys() (keys []string) {
    bc.RLock()
    defer bc.RUnlock()
    for key, itm := range bc.items {
        if itm.isExpire() {
            keys = append(keys, key)
        }
    }
    return
}

// clearItems 清空鍵名在 keys 內的緩存.
func (bc *MemoryCache) clearItems(keys []string) {
    bc.Lock()
    defer bc.Unlock()
    for _, key := range keys {
        delete(bc.items, key)
    }
}

後記

file、ssdb、memcache和redis實現都相似,file以文件方式儲存每個鍵對應一個文件,文件內容為值的數據,用gob編碼持久化。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 多線程 基本實現: 第一種,函數方式 # -*- coding:utf-8 -*- import thread import time def print_time(threadName, delay): count = 0 while count < 5: time.sleep(delay) co ...
  • 最近開髮網關服務的過程當中,需要用到kafka轉發消息與保存日誌,在進行壓測的過程中由於是多線程併發操作kafka producer 進行非同步send,發現send耗時有時會達到幾十毫秒的阻塞,很大程度上上影響了併發的性能,而在後續的測試中發現單線程發送反而比多線程發送效率高出幾倍。所以就對kafk ...
  • SSM框架基礎配置文件包括:applicationContext.xml(Spring框架核心配置文件)、dispatcher-servlet.xml(SpringMVC框架核心配置文件)、mybatis-config.xml(Mybatis配置文件)、database.properties(數據源 ...
  • 迷宮問題 迷宮問題一直是電腦工作者感興趣的問題,因為它可以展現棧的巧妙應用, 這裡將利用棧開發一個走迷宮程式,雖然在發現正確路徑前,程式要嘗試許多 錯誤路徑,但是,一旦發現,就能夠重新走出迷宮,而不會再去嘗試任何錯誤路徑。 迷宮問題求解 電腦中可以用如圖所示的方塊圖表示迷宮。圖中空白方塊為通道, ...
  • 在項目中,很多地方需要根據時間獲取相應的數據,將時間格式化,或者時間比較等相關操作。一個良好的工具類不僅可以減少代碼冗餘,還能促進業務處理,加快進度。 輸出結果: ...
  • Java包是一個相關類的集合,Java標準類庫是一組支持基本編輯任務的包(即Java標準庫是按包分組的)。 下麵是Java標準類庫中的部分包: (https://www.oracle.com/technetwork/java/api-141528.html為線上Java API文檔的網頁,選擇適應的 ...
  • 題目鏈接 給出一個有N個數的序列,編號0 - N - 1。進行Q次查詢,查詢編號i至j的所有數中,最大的數是多少。 例如: 1 7 6 3 1。i = 1, j = 3,對應的數為7 6 3,最大的數為7。(該問題也被稱為RMQ問題) 輸入 輸出 輸入樣例 輸出樣例 Sparse Table解決Ra ...
  • 1、安裝matplotlib 在 cmd 中鍵入 python -m pip install matplotlib,系統將自動安裝,需要等一段時間,待完成後 python -m pip list ,顯示 敲黑板劃重點:一定通過 cdm 指定具體安裝文件夾。 cd 文件夾名 可進入指定文件夾。 2、簡 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...