原來Sync.Once還能這么用
介紹
sync.Once估計大家都不陌生,官方介紹中,
Once is an object that will perform exactly one action
正是因為這個特性,Once常常被用于單例對象的初始化場景。
也正是因為這個特性,其實它還能做一些其他的事情。
緩存擊穿
日常背誦八股文,我相信你們對緩存擊穿這個詞特別熟悉。
緩存擊穿一般代指熱點key緩存失效(到期|刪了),同一時刻大量對熱點key的并發(fā)請求。緩存找不到數據,所有請求都打入到DB層。此時,身為開發(fā)的你,明天和意外就不知道哪個先到了。
為了防止這種情況發(fā)生,針對相同key的請求,只需要一個請求(A)到達DB層取數據,其他請求等待A通知就行了。
就像這樣,
圖片來源:[1]
singleflight
Go里有很多防緩存擊穿的工具,比如singleflight庫。
通過上面簡單的代碼大概能看出,其實就是對key做了緩存。
把一個key對應call結構存儲在map中。保證只有一個key真正執(zhí)行fn()服務 ,其他請求則通過sync.waitGroup的wait等待結果。
至于g.docall(c,key,fn),
當帶著全村人希望的那個請求,獲取到數據,給對應key的call賦值,最終執(zhí)行done,通知等待這個key全村的村民獲取數據。
代碼并不復雜。
自定義singleflight
我們也可以實現一個簡易版本的。
代碼整體不難,主要的點在于我們是通過通道來實現通知自家兄弟取數據。
最后,讓我們使用Once來達到同樣的效果,不然標題不白起了嘛。
上面核心代碼都寫出來了,實際開發(fā)中需要對請求資源做一些超時控制等操作。
總結
平常對Once的使用只停留在初始化工作上,而弱化了它的使用場景。對于其他工具也是一個道理,這就需要去積累和挖掘了。
附錄
[1]https://medium.com/codex/caching-system-stability-766bf5fff69f
https://blog.chuie.io/posts/synconce/