原來sync.Once還能這么用
介紹
sync.Once估計(jì)大家都不陌生,官方介紹中,
Once is an object that will perform exactly one action
正是因?yàn)檫@個(gè)特性,Once常常被用于單例對象的初始化場景。
也正是因?yàn)檫@個(gè)特性,其實(shí)它還能做一些其他的事情。
緩存擊穿
日常背誦八股文,我相信你們對緩存擊穿這個(gè)詞特別熟悉。
緩存擊穿一般代指熱點(diǎn)key緩存失效(到期|刪了),同一時(shí)刻大量對熱點(diǎn)key的并發(fā)請求。緩存找不到數(shù)據(jù),所有請求都打入到DB層。此時(shí),身為開發(fā)的你,明天和意外就不知道哪個(gè)先到了。
為了防止這種情況發(fā)生,針對相同key的請求,只需要一個(gè)請求(A)到達(dá)DB層取數(shù)據(jù),其他請求等待A通知就行了。
就像這樣,

圖片來源:[1]
singleflight
Go里有很多防緩存擊穿的工具,比如singleflight庫。


通過上面簡單的代碼大概能看出,其實(shí)就是對key做了緩存。
把一個(gè)key對應(yīng)call結(jié)構(gòu)存儲在map中。保證只有一個(gè)key真正執(zhí)行fn()服務(wù) ,其他請求則通過sync.waitGroup的wait等待結(jié)果。
至于g.docall(c,key,fn),

當(dāng)帶著全村人希望的那個(gè)請求,獲取到數(shù)據(jù),給對應(yīng)key的call賦值,最終執(zhí)行done,通知等待這個(gè)key全村的村民獲取數(shù)據(jù)。
代碼并不復(fù)雜。
自定義singleflight
我們也可以實(shí)現(xiàn)一個(gè)簡易版本的。

代碼整體不難,主要的點(diǎn)在于我們是通過通道來實(shí)現(xiàn)通知自家兄弟取數(shù)據(jù)。
最后,讓我們使用Once來達(dá)到同樣的效果,不然標(biāo)題不白起了嘛。

上面核心代碼都寫出來了,實(shí)際開發(fā)中需要對請求資源做一些超時(shí)控制等操作。
總結(jié)
平常對Once的使用只停留在初始化工作上,而弱化了它的使用場景。對于其他工具也是一個(gè)道理,這就需要去積累和挖掘了。
附錄
[1]https://medium.com/codex/caching-system-stability-766bf5fff69f
https://blog.chuie.io/posts/synconce/
推薦閱讀
