golang實現的自帶併發控制,服務降級的本地kv快取

simplejia發表於2016-12-09

lc (local cache)--http://github.com/simplejia/lc

實現初衷

  • 純用 redis 做快取,相比 lc,redis 有網路呼叫開銷,反覆呼叫多次,延時急劇增大,當網路偶爾出現故障時,我們的資料介面也就拿不到資料,但 lc 裡的資料就算是超過了設定的過期時間,我們一樣能拿到過期的資料做備用
  • 使用 mysql,當快取失效,有資料穿透的風險,lc 自帶併發控制,有且只允許同一時間同一個 key 的唯一一個 client 穿透到資料庫,其它直接返回 lc 快取資料

特性

  • 本地快取
  • 支援 Get,Set,Mget,Delete 操作
  • 當快取失效時,返回失效標誌同時,還返回舊的資料,如:v, ok := lc.Get(key),當 key 已經過了失效時間了,並且 key 還沒有被 lru 淘汰掉,v 是之前存的值,ok 返回 false
  • 實現程式碼沒有用到鎖
  • 使用到 lru,淘汰長期不用的 key
  • 結合lm使用更簡單快捷

demo

lc_test.go

package lc

import (
    "testing"
    "time"
)

func init() {
    Init(65536) // 使用lc之前必須要初始化
}

func TestGetValid(t *testing.T) {
    key := "k"
    value := "v"
    Set(key, value, time.Second)
    time.Sleep(time.Millisecond * 10) // 給非同步處理留點時間
    v, ok := Get(key)
    if !ok || v != value {
        t.Fatal("")
    }
}

同時再帖下我實現的另一個元件,搭配 lc 使用更方便。

lm (lc+redis+[mysql|http] glue)--http://github.com/simplejia/lm

實現初衷

> 寫 redis+mysql 程式碼時(還可能加上 lc),示意程式碼如下:

func orig(key string) (value string) {
    value = redis.Get(key)
    if value != "" {
        return
    }
    value = mysql.Get(key)
    redis.Set(key, value)
    return
}
// 如果再加上lc的話
func orig(key string) (value string) {
    value = lc.Get(key)
    if value != "" {
        return
    }
    value = redis.Get(key)
    if value != "" {
        lc.Set(key, value)
        return
    }
    value = mysql.Get(key)
    redis.Set(key, value)
    lc.Set(key, value)
    return
}

> 有了 lm,再寫上面的程式碼時,一切變的那麼簡單 lm_test.go

func tGlue(key, value string) (err error) {
    lmStru := &LmStru{
        Input:  key,
        Output: &value,
        Proc: func(p, r interface{}) error {
            _r := r.(*string)
            *_r = "test value"
            return nil
        },
        Key: func(p interface{}) string {
            return fmt.Sprintf("tGlue:%v", p)
        },
        Mc: &McStru{
            Expire: time.Minute,
            Pool:   pool,
        },
        Lc: &LcStru{
            Expire: time.Millisecond * 500,
            Safety: false,
        },
    }
    err = Glue(lmStru)
    if err != nil {
        return
    }
    return
}

功能

  • 自動新增快取程式碼,支援 lc, redis,減輕你的心智負擔,讓你的程式碼更加簡單可靠,少了大段的冗餘程式碼,複雜的事全交給 lm 自動幫你做了
  • 支援 Glue[Lc|Mc] 及相應批量操作 Glues[Lc|Mc],詳見 lm_test.go 示例程式碼

注意

  • lm.LcStru.Safety,當置為 true 時,對 lc 在併發狀態下返回的 nil 值不接受,因為 lc.Get 在併發狀態下,同一個 key 返回的 value 有可能是 nil,並且 ok 狀態為 true,Safety 置為 true 後,對以上情況不接受,會繼續呼叫下一層邏輯

案例分享

  • 一天一個使用者只容許投一次票 func f(uid string) (err error) { lmStru := &lm.LmStru{ Input: uid, Output: &struct{}{}, Proc: func(p, r interface{}) error { // 略掉這部分邏輯: 可以把投票入庫 // ... return nil }, Key: func(p interface{}) string { return fmt.Sprintf("pkg:f:%v", p) }, Mc: &lm.McStru{ Expire: time.Hour * 24, Pool: pool, }, } err = lm.GlueMc(lmStru) if err != nil { return } return }
更多原創文章乾貨分享,請關注公眾號
  • golang實現的自帶併發控制,服務降級的本地kv快取
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章