golang實現的自帶併發控制,服務降級的本地kv快取
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
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 }
更多原創文章乾貨分享,請關注公眾號
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 快取架構中的服務詳解!SpringBoot中二級快取服務的實現快取架構Spring Boot
- [記]SAF 中快取服務的實現快取
- 「Golang成長之路」併發任務的控制Golang
- 用 Golang 實現百萬級 Websocket 服務GolangWeb
- golang實現簡單的併發任務消費Golang
- 基於 golang 開發的短鏈服務(自帶前臺頁面)Golang
- 探討下如何更好的使用快取 —— Redis快取的特殊用法以及與本地快取一起構建多級快取的實現快取Redis
- golang實現的長連線服務Golang
- Java後端分散式系統的服務降級:優雅降級與服務熔斷Java後端分散式
- Golang 併發,有快取通道,通道同步案例演示Golang快取
- 微服務架構—服務降級微服務架構
- 服務的熔斷和降級的區別
- Golang 實現 Redis(10): 本地原子性事務GolangRedis
- Dubbo服務降級設定
- 帶你瞭解Go怎樣實現二級快取Go快取
- Spring Cloud Alibaba系列(五)sentinel實現服務限流降級SpringCloud
- 利用localstorage實現本地訊息快取快取
- 兩級快取實現分析之快取設定快取
- NGINX如何實現rtmp推流服務--視訊快取Nginx快取
- 如何利用memcached和wcf實現一個快取服務快取
- 快取和併發快取
- localstorage實現帶過期時間的快取功能快取
- golang讀取java或者go的webservice服務GolangJavaWeb
- 用 golang 去實現類似 swoole 的 websocket 服務 ?GolangWeb
- Laravel 實現二級快取 提高快取的命中率和細粒化快取 keyLaravel快取
- 打造可降級的React服務端同構框架React服務端框架
- 通過Dapr實現一個簡單的基於.net的微服務電商系統(十八)——服務保護之多級快取微服務快取
- 微服務11:熔斷、降級的Hystrix實現(附原始碼)微服務原始碼
- Android清除本地快取資料的程式碼實現方案Android快取
- 面試官:Dubbo怎麼實現服務降級,他有什麼好處?面試
- Dubbo優雅服務降級之mockMock
- iOS開發-清理快取功能的實現iOS快取
- iOS m3u8本地快取播放(控制下載併發、暫停恢復)iOS快取
- myBatis原始碼解析-二級快取的實現方式MyBatis原始碼快取
- Java中的多級快取設計與實現Java快取
- Golang 併發程式設計(channel實現)Golang程式設計
- 極驗高併發驗證服務背後的技術實現
- 使用ThreadLocal來實現一個本地快取thread快取