国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

腳本之家,腳本語言編程技術(shù)及教程分享平臺(tái)!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Golang - 泛型版 Singleflight:Go 中如何防止緩存擊穿?

泛型版 Singleflight:Go 中如何防止緩存擊穿?

2021-12-31 23:20polarisxu站長polaris Golang

并發(fā)是 Go 的優(yōu)勢,但并發(fā)也需要很好的進(jìn)行控制。標(biāo)準(zhǔn)庫中有 sync 包,經(jīng)常使用的功能有 sync.Mutex、sync.WaitGroup 等。其實(shí),除了標(biāo)準(zhǔn)庫,還有一個(gè)官方的擴(kuò)展庫,也叫 sync,其中有一個(gè)子包:sync/singleflight,專門做并發(fā)控制,比如防

泛型版 Singleflight:Go 中如何防止緩存擊穿?

大家好,我是 polarisxu。

并發(fā)是 Go 的優(yōu)勢,但并發(fā)也需要很好的進(jìn)行控制。標(biāo)準(zhǔn)庫中有 sync 包,經(jīng)常使用的功能有 sync.Mutex、sync.WaitGroup 等。其實(shí),除了標(biāo)準(zhǔn)庫,還有一個(gè)官方的擴(kuò)展庫,也叫 sync,其中有一個(gè)子包:sync/singleflight,專門做并發(fā)控制,比如防止緩存擊穿。

01 從例子說起

看一個(gè)模擬緩存的例子,有如下代碼:

  1. package main
  2. import (
  3. "errors"
  4. "flag"
  5. "log"
  6. "sync"
  7. )
  8. var errorNotExist = errors.New("not exist")
  9. var n int
  10. func init() {
  11. flag.IntVar(&n, "n", 5, "模擬的并發(fā)數(shù),默認(rèn) 5")
  12. }
  13. func main() {
  14. flag.Parse()
  15. var wg sync.WaitGroup
  16. wg.Add(n)
  17. // 模擬并發(fā)訪問
  18. for i := 0; i < n; i++ {
  19. go func() {
  20. defer wg.Done()
  21. // 假設(shè)都獲取 id = 1 這篇文章
  22. article := fetchArticle(1)
  23. log.Println(article)
  24. }()
  25. }
  26. wg.Wait()
  27. }
  28. type Article struct {
  29. ID int
  30. Content string
  31. }
  32. func fetchArticle(id int) *Article {
  33. article := findArticleFromCache(id)
  34. if article != nil && article.ID > 0 {
  35. return article
  36. }
  37. return findArticleFromDB(id)
  38. }
  39. var (
  40. cache = make(map[int]*Article)
  41. rwmutex sync.RWMutex
  42. )
  43. // 模擬從緩存獲取數(shù)據(jù)
  44. func findArticleFromCache(id int) *Article {
  45. rwmutex.RLock()
  46. defer rwmutex.RUnlock()
  47. return cache[id]
  48. }
  49. // 模擬從數(shù)據(jù)庫中獲取數(shù)據(jù)
  50. func findArticleFromDB(id int) *Article {
  51. log.Printf("SELECT * FROM article WHERE id=%d", id)
  52. article := &Article{ID: id, Content: "polarisxu"}
  53. rwmutex.Lock()
  54. defer rwmutex.Unlock()
  55. cache[id] = article
  56. return article
  57. }

我們模擬 5 個(gè)用戶并發(fā)訪問,同時(shí)獲取 ID=1 的文章,因?yàn)榫彺嬷胁淮嬖?,因此都到后?DB 獲取具體數(shù)據(jù)。從運(yùn)行結(jié)果可以看出這一點(diǎn):

  1. $ go run main.go
  2. 2021/12/30 10:32:36 SELECT * FROM article WHERE id=1
  3. 2021/12/30 10:32:36 SELECT * FROM article WHERE id=1
  4. 2021/12/30 10:32:36 &{1 polarisxu}
  5. 2021/12/30 10:32:36 &{1 polarisxu}
  6. 2021/12/30 10:32:36 SELECT * FROM article WHERE id=1
  7. 2021/12/30 10:32:36 &{1 polarisxu}
  8. 2021/12/30 10:32:36 SELECT * FROM article WHERE id=1
  9. 2021/12/30 10:32:36 &{1 polarisxu}
  10. 2021/12/30 10:32:36 SELECT * FROM article WHERE id=1
  11. 2021/12/30 10:32:36 &{1 polarisxu}

顯然這是我們不希望看到的。

02 使用 singleflight

官方的擴(kuò)展包 golang.org/x/sync 下面有一個(gè)子包 singleflight:

  1. Package singleflight provides a duplicate function call suppression mechanism.

它用來抑制函數(shù)的重復(fù)調(diào)用,這正好符合上面的場景:希望從數(shù)據(jù)庫獲取數(shù)據(jù)的函數(shù)只調(diào)用一次。

將 fetchArticle 函數(shù)改成這樣:

  1. var g singleflight.Group
  2. func fetchArticle(id int) *Article {
  3. article := findArticleFromCache(id)
  4. if article != nil && article.ID > 0 {
  5. return article
  6. }
  7. v, err, shared := g.Do(strconv.Itoa(id), func() (interface{}, error) {
  8. return findArticleFromDB(id), nil
  9. })
  10. // 打印 shared,看看都什么值
  11. fmt.Println("shared===", shared)
  12. if err != nil {
  13. log.Println("singleflight do error:", err)
  14. return nil
  15. }
  16. return v.(*Article)
  17. }

singleflight.Group 是一個(gè)結(jié)構(gòu)體類型,沒有導(dǎo)出任何字段,它代表一類工作并形成一個(gè)命名空間,在該命名空間中可以抑制工作單元的重復(fù)執(zhí)行。

該類型有三個(gè)方法,它們的功能見注釋:

  1. // 執(zhí)行并返回給定函數(shù)的結(jié)果,確保對于給定的鍵,fn 函數(shù)只會(huì)執(zhí)行一次。
  2. // 如果有重復(fù)的進(jìn)來,重復(fù)的調(diào)用者會(huì)等待最原始的調(diào)用完成并收到相同的結(jié)果。
  3. // 返回值 shared 指示是否將 v 提供給多個(gè)調(diào)用者。
  4. // 返回值 v 是 fn 的執(zhí)行結(jié)果
  5. // 返回值 err 是 fn 返回的 err
  6. func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool)
  7. // 和 Do 類似,但返回一個(gè) channel(只能接收),用來接收結(jié)果。Result 是一個(gè)結(jié)構(gòu)體,有三個(gè)字段,即 Do 返回的那三個(gè)。
  8. func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result
  9. func (g *Group) Forget(key string)

因此,改后的代碼,通過 Group.Do,即使并發(fā)多次調(diào)用,findArticleFromDB 也只會(huì)執(zhí)行一次,并且這一次的結(jié)果會(huì)被并發(fā)多次執(zhí)行共享。

運(yùn)行后,結(jié)果如下:

  1. $ go run main.go
  2. 2021/12/30 11:55:44 SELECT * FROM article WHERE id=1
  3. shared=== true
  4. 2021/12/30 11:55:44 &{1 polarisxu}
  5. shared=== true
  6. 2021/12/30 11:55:44 &{1 polarisxu}
  7. shared=== true
  8. 2021/12/30 11:55:44 &{1 polarisxu}
  9. shared=== true
  10. 2021/12/30 11:55:44 &{1 polarisxu}
  11. shared=== true
  12. 2021/12/30 11:55:44 &{1 polarisxu}

和預(yù)期一樣,findArticleFromDB 只執(zhí)行了一次,shared 的值也表示結(jié)果被多個(gè)調(diào)用者共享。

所以,使用 Go 后,再也不需要通過類似 Redis 中的 SETNX 這樣的命令來實(shí)現(xiàn)類似的功能了。

03 Forget 的用途

上面 Group 的方法中,有一個(gè)沒有給任何注釋,即 Forget。從名字猜到,用來忘掉什么,那具體什么意思呢?

通過上面的例子,我們知曉,通過 Do,可以實(shí)現(xiàn)多個(gè)并發(fā)調(diào)用只執(zhí)行回調(diào)函數(shù)一次,并共享相同的結(jié)果。而 Forget 的作用是:

Forget tells the singleflight to forget about a key. Future calls to Do for this key will call the function rather than waiting for an earlier call to complete.

即告訴 singleflight 忘記一個(gè) key,未來對此 key 的 Do 調(diào)用將調(diào)用 fn 回調(diào)函數(shù),而不是等待更早的調(diào)用完成,即相當(dāng)于廢棄 Do 原本的作用。

可以在上面例子中 Do 調(diào)用之前,調(diào)用 g.Forget,驗(yàn)證是否 Do 的調(diào)用都執(zhí)行 fn 函數(shù)即 findArticleFromDB 函數(shù)了。

04 泛型版本

細(xì)心的讀者可能會(huì)發(fā)現(xiàn),Do 方法返回的 v 是 interface{},在 fetchArticle 函數(shù)最后,我們做了類型斷言:v.(*Article)。

既然 Go1.18 馬上要來了,有了泛型,可以有泛型版本的 singleflight,不需要做類型斷言了。GitHub 已經(jīng)有人實(shí)現(xiàn)并開源:https://github.com/marwan-at-work/singleflight。

改成這個(gè)泛型版本,要改以下幾處:

  • 導(dǎo)入包 marwan.io/singleflight,而非 github.com/marwan-at-work/singleflight,同時(shí)移除 golang.org/x/sync/singleflight
  • g 的聲明改為:var g singleflight.Group[*Article]
  • Do 的調(diào)用,返回值由 interface{} 類型改為:*Article:
  1. article, err, shared := g.Do(strconv.Itoa(id), func() (*Article, error) {
  2. return findArticleFromDB(id), nil
  3. })
  • 最后返回時(shí),直接返回 article,不需要做類型斷言

05 總結(jié)

singleflight 很常用,你在 pkg.go.dev 搜索 singleflight,發(fā)現(xiàn)有很多輪子:https://pkg.go.dev/search?q=singleflight,好些項(xiàng)目不是使用官方的 golang.org/x/sync/singleflight,而是自己實(shí)現(xiàn)一個(gè),不過這些實(shí)現(xiàn)基本只實(shí)現(xiàn)了最常用的 Do 方法。感興趣的可以查看他們的實(shí)現(xiàn)。

下次項(xiàng)目中需要類似功能,記得使用 singleflight 哦!

原文鏈接:https://mp.weixin.qq.com/s/zwTErb_kiEEsurscYQ6eRw

延伸 · 閱讀

精彩推薦
  • Golanggo日志系統(tǒng)logrus顯示文件和行號(hào)的操作

    go日志系統(tǒng)logrus顯示文件和行號(hào)的操作

    這篇文章主要介紹了go日志系統(tǒng)logrus顯示文件和行號(hào)的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • Golanggolang如何使用struct的tag屬性的詳細(xì)介紹

    golang如何使用struct的tag屬性的詳細(xì)介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細(xì)介紹,從例子說起,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看...

    Go語言中文網(wǎng)11352020-05-21
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • Golanggolang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang的httpserver優(yōu)雅重啟方法詳解

    golang的httpserver優(yōu)雅重啟方法詳解

    這篇文章主要給大家介紹了關(guān)于golang的httpserver優(yōu)雅重啟的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,...

    helight2992020-05-14
  • GolangGolang通脈之?dāng)?shù)據(jù)類型詳情

    Golang通脈之?dāng)?shù)據(jù)類型詳情

    這篇文章主要介紹了Golang通脈之?dāng)?shù)據(jù)類型,在編程語言中標(biāo)識(shí)符就是定義的具有某種意義的詞,比如變量名、常量名、函數(shù)名等等,Go語言中標(biāo)識(shí)符允許由...

    4272021-11-24
  • GolangGolang中Bit數(shù)組的實(shí)現(xiàn)方式

    Golang中Bit數(shù)組的實(shí)現(xiàn)方式

    這篇文章主要介紹了Golang中Bit數(shù)組的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨(dú)尊11682021-06-09
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 久久久中文 | 午夜免费电影 | 91精品国产综合久久久久久 | 中文字幕亚洲一区二区va在线 | 午夜成人免费视频 | 亚洲欧美在线观看 | 激情久久综合网 | 中文字幕一区二区三区乱码在线 | 美女午夜影院 | 欧美视频在线一区 | 日本一本视频 | 美女视频一区二区三区 | 国产福利在线 | 一二三区视频 | 国产综合在线视频 | 一区二区三区久久久久久 | 操av网 | 国产在线视频网站 | 九九九久久久 | 免费色在线 | 一区二区不卡视频 | 精品国产乱码久久久久久影片 | 亚洲精品国精品久久99热 | 日韩一级免费观看 | 久久久毛片 | 国产综合久久久 | 免费观看毛片 | 日本一区二区三区免费观看 | 欧美一级片毛片免费观看视频 | 精品一级 | 污视频网站在线观看 | 日韩欧一区二区三区 | 国产羞羞视频在线观看 | 久久久精品免费视频 | 国产精品自产拍在线观看 | 久久99精品久久久久久久青青日本 | 色天天综合 | 久久精品国产一区二区三区 | 欧美视频精品 | 中文字幕在线一区二区三区 | 欧美精品亚洲精品日韩精品 |