gone是可以高效開發Web服務的Golang依賴注入框架
github地址:https://github.com/gone-io/gone
文檔地址:https://goner.fun/zh/
請幫忙在github上點個 ??吧,這對我很重要 ;萬分感謝!!
文章目錄
- 利用redis提供分布式鎖和分布式緩存
- 第一步:將redis相關Goner埋葬到Cemetery
- 第二步:在配置文件中增加redis相關配置
- 第三步,使用redis
- 注入接口
- 使用分布是緩存
- 使用分布時鎖
- 上面例子完整代碼
利用redis提供分布式鎖和分布式緩存
在本文中,我們將分享在gone中如何使用分布式緩存和分布式鎖,其中分布式鎖中實現了一種較為自由的處理方式———“智能鎖”,對一個處理函數進行上鎖,函數執行中會周期性檢測鎖過期的剩余時間并自動給鎖續期,函數執行完后會自動解鎖。
第一步:將redis相關Goner埋葬到Cemetery
什么是 Goner?
什么是 埋葬?
什么是 Cemetery?
參考 Gone的核心概念
在Priest函數中增加_ = goner.RedisPriest(cemetery)
,如下:
func priest(cemetery gone.Cemetery) error {//使用 goner.RedisPriest 函數,將 redis 相關的Goner 埋葬到 Cemetery 中_ = goner.RedisPriest(cemetery)cemetery.Bury(&redisUser{})return nil
}
第二步:在配置文件中增加redis相關配置
創建配置文件 config/default.properties
,內容如下:
# redis服務地址,格式為 `host:port`
redis.server=localhost:6379# redis服務密碼,不配置默認為空
redis.password=
其中,redis服務地址需要改你能訪問到的redis服務地址。
更多配置:
redis.max-idle
:最大空閑鏈接數,不配置默認為2redis.max-active
:最大活躍鏈接數,不配置默認為10redis.db
:使用的db,不配置默認為0redis.cache.prefix
:key前綴,如果設置了,對redis的增刪改查都會拼接該前綴,拼接方式${prefix}#${key}
;默認為空
關于配置文件,更多參考:通過內置Goners支持配置文件
第三步,使用redis
注入接口
在需要使用的結構體中注入 接口redis.redis.Cache
和 redis.Locker
,他們的GonerId
分別為:gone-redis-cache
和gone-redis-locker
:
type redisUser struct {gone.Flagcache redis.Cache `gone:"gone-redis-cache"`locker redis.Locker `gone:"gone-redis-locker"`
}
使用分布是緩存
請看下面代碼中的注釋:
func (r *redisUser) UseCache() {key := "gone-address"value := "https://github.com/gone-io/gone"//設置緩存err := r.cache.Put(key, //第一個參數為 緩存的key,類型為 `string`value, // 第二參數為 需要緩存的值,類型為any,可以是任意類型;傳入的值會被編碼為 `[]byte` 發往redis10*time.Second, // 第三個參數為 過期時間,類型為 `time.Duration`;省略,表示不設置過期時間)if err != nil {fmt.Printf("err:%v", err)return}//獲取緩存var getValue stringerr = r.cache.Get(key, //第一個參數為 緩存的key,類型為 `string`&getValue, //第二參數為指針,接收獲取緩存的值,類型為any,可以是任意類型;從redis獲取的值會被解碼為傳入的指針類型)if err != nil {fmt.Printf("err:%v", err)return}fmt.Printf("getValue:%v", getValue)
}
接口上的其他方法:
Remove(key string) (err error)
:用于刪除redis某個key,支持通配符Keys(key string) ([]string, error)
:使用前綴或者通配符查詢存在哪些key,??該方法慎用Prefix() string
:獲取當前緩存配置的key前綴
使用分布時鎖
- 鎖定一段時間
func (r *redisUser) UseLock() {lockKey := "gone-lock-key"//嘗試獲取鎖 并 鎖定一段時間//返回的第一個參數為一個解鎖的函數unlock, err := r.locker.TryLock(lockKey, //第一個參數為 鎖的key,類型為 `string`10*time.Second, //第二參數為 鎖的過期時間,類型為 `time.Duration`)if err != nil {fmt.Printf("err:%v", err)return}//操作完后,需要解鎖defer unlock()//獲取鎖成功后,可以進行業務操作//todo
}
這種方式,使用鎖需要保證在鎖定的時間內能夠執行完所有操作,否則由于鎖過期可能會導致問題。
- 鎖定一個操作,操作沒結束會自動給鎖續期,操作結束自動解鎖
func (r *redisUser) LockFunc() {lockKey := "gone-lock-key"err := r.locker.LockAndDo(lockKey, //第一個參數為 鎖的key,類型為 `string`func() { //第二個參數為 需要執行的函數,類型為 `func()`,代表一個操作//獲取鎖成功后,可以進行業務操作//todoprintln("do some options")},100*time.Second, //第三個參數為 鎖的過期時間,類型為 `time.Duration`;第一次加鎖和后續鎖續期都將使用該值5*time.Second, //第四個參數為 鎖續期的間隔時間,類型為 `time.Duration`;周期性檢查所是否將過期,如果在下個周期內會過期則對鎖續期)if err != nil {fmt.Printf("err:%v", err)}
}
這種方式比較智能,姑且將其稱為“智能鎖”吧!
推薦使用這種方式,可以無腦使用,降低使用的心智負擔。
上面例子完整代碼
例子的源代碼可以在這里找到
package mainimport ("fmt""github.com/gone-io/gone""github.com/gone-io/gone/goner""github.com/gone-io/gone/goner/redis""time"
)func priest(cemetery gone.Cemetery) error {//使用 goner.RedisPriest 函數,將 redis 相關的Goner 埋葬到 Cemetery 中_ = goner.RedisPriest(cemetery)cemetery.Bury(&redisUser{})return nil
}type redisUser struct {gone.Flagcache redis.Cache `gone:"gone-redis-cache"`locker redis.Locker `gone:"gone-redis-locker"`
}func (r *redisUser) UseCache() {key := "gone-address"value := "https://github.com/gone-io/gone"//設置緩存err := r.cache.Put(key, //第一個參數為 緩存的key,類型為 `string`value, // 第二參數為 需要緩存的值,類型為any,可以是任意類型;傳入的值會被編碼為 `[]byte` 發往redis10*time.Second, // 第三個參數為 過期時間,類型為 `time.Duration`;省略,表示不設置過期時間)if err != nil {fmt.Printf("err:%v", err)return}//獲取緩存var getValue stringerr = r.cache.Get(key, //第一個參數為 緩存的key,類型為 `string`&getValue, //第二參數為指針,接收獲取緩存的值,類型為any,可以是任意類型;從redis獲取的值會被解碼為傳入的指針類型)if err != nil {fmt.Printf("err:%v", err)return}fmt.Printf("getValue:%v", getValue)
}func (r *redisUser) LockTime() {lockKey := "gone-lock-key"//嘗試獲取鎖 并 鎖定一段時間//返回的第一個參數為一個解鎖的函數unlock, err := r.locker.TryLock(lockKey, //第一個參數為 鎖的key,類型為 `string`10*time.Second, //第二參數為 鎖的過期時間,類型為 `time.Duration`)if err != nil {fmt.Printf("err:%v", err)return}//操作完后,需要解鎖defer unlock()//獲取鎖成功后,可以進行業務操作//todo
}func (r *redisUser) LockFunc() {lockKey := "gone-lock-key"err := r.locker.LockAndDo(lockKey, //第一個參數為 鎖的key,類型為 `string`func() { //第二個參數為 需要執行的函數,類型為 `func()`,代表一個操作//獲取鎖成功后,可以進行業務操作//todoprintln("do some options")},100*time.Second, //第三個參數為 鎖的過期時間,類型為 `time.Duration`;第一次加鎖和后續鎖續期都將使用該值5*time.Second, //第四個參數為 鎖續期的間隔時間,類型為 `time.Duration`;周期性檢查所是否將過期,如果在下個周期內會過期則對鎖續期)if err != nil {fmt.Printf("err:%v", err)}
}func main() {gone.Prepare(priest).AfterStart(func(in struct {r *redisUser `gone:"*"`}) {in.r.UseCache()in.r.LockTime()}).Run()
}
上一篇:Gone框架介紹17 - 創建一個可運行在生產環境的Web項目
下一篇:Gone框架介紹19 -如何進行單元測試?