最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang使用sync.singleflight解決熱點(diǎn)緩存穿透問題

 更新時(shí)間:2023年07月30日 09:08:03   作者:了跡奇有沒  
在go的sync包中,有一個(gè)singleflight包,里面有一個(gè)?singleflight.go文件,代碼加注釋,一共200行出頭,通過?singleflight可以很容易實(shí)現(xiàn)緩存和去重的效果,避免重復(fù)計(jì)算,接下來我們就給大家詳細(xì)介紹一下sync.singleflight如何解決熱點(diǎn)緩存穿透問題

在 go 的 sync 包中,有一個(gè) singleflight 包,里面有一個(gè) singleflight.go 文件,代碼加注釋,一共 200 行出頭。內(nèi)容包括以下幾塊兒:

  1. Group 結(jié)構(gòu)體管理一組相關(guān)的函數(shù)調(diào)用工作,它包含一個(gè)互斥鎖和一個(gè) map,map 的 key 是函數(shù)的名稱,value 是對(duì)應(yīng)的 call 結(jié)構(gòu)體。
  2. call 結(jié)構(gòu)體表示一個(gè) inflight 或已完成的函數(shù)調(diào)用,包含等待組件 WaitGroup、調(diào)用結(jié)果 val 和 err、調(diào)用次數(shù) dups 和通知通道 chans
  3. Do 方法接收一個(gè) key 和函數(shù) fn,它會(huì)先查看 map 中是否已經(jīng)有這個(gè) key 的調(diào)用在 inflight,如果有則等待并返回已有結(jié)果,如果沒有則新建一個(gè) call 并執(zhí)行函數(shù)調(diào)用。
  4. DoChan 類似 Do 但返回一個(gè) channel 來接收結(jié)果。
  5. doCall 方法包含了具體處理調(diào)用的邏輯,它會(huì)在函數(shù)調(diào)用前后添加 defer 來 recover panic 和區(qū)分正常 return 與 runtime.Goexit
  6. 如果發(fā)生 panic,會(huì)將 panicwraps 成錯(cuò)誤返回給等待的 channel,如果是 goexit 會(huì)直接退出。正常 return 時(shí)會(huì)將結(jié)果發(fā)送到所有通知 channel。
  7. Forget 方法可以忘記一個(gè) key 的調(diào)用,下次 Do 時(shí)會(huì)重新執(zhí)行函數(shù)。

這個(gè)包通過互斥鎖和 map 實(shí)現(xiàn)了對(duì)相同 key 的函數(shù)調(diào)用去重,可以避免對(duì)已有調(diào)用的重復(fù)計(jì)算,同時(shí)通過 channel 機(jī)制可以通知調(diào)用者函數(shù)執(zhí)行結(jié)果。在一些需要確保單次執(zhí)行的場(chǎng)景中,可以使用這個(gè)包中的方法。

通過 singleflight 可以很容易實(shí)現(xiàn)緩存和去重的效果,避免重復(fù)計(jì)算,接下來,我們來模擬一下并發(fā)請(qǐng)求可能導(dǎo)致的緩存穿透場(chǎng)景,以及如何用 singleflight 包來解決這個(gè)問題:

package main
import (
   "context"
   "fmt"
   "golang.org/x/sync/singleflight"
   "sync/atomic"
   "time"
   )
type Result string
// 模擬查詢數(shù)據(jù)庫(kù)
func find(ctx context.Context, query string) (Result, error) {
   return Result(fmt.Sprintf("result for %q", query)), nil
}
func main() {
   var g singleflight.Group
   const n = 200
   waited := int32(n)
   done := make(chan struct{})
   key := "this is key"
   for i := 0; i < n; i++ {
      go func(j int) {
         v, _, shared := g.Do(key, func() (interface{}, error) {
            ret, err := find(context.Background(), key)
            return ret, err
         })
         if atomic.AddInt32(&waited, -1) == 0 {
            close(done)
         }
         fmt.Printf("index: %d, val: %v, shared: %v\n", j, v, shared)
      }(i)
   }
   select {
   case <-done:
   case <-time.After(time.Second):
      fmt.Println("Do hangs")
   }
   time.Sleep(time.Second * 4)
}

在這段程序中,如果重復(fù)使用查詢結(jié)果,shared 會(huì)返回 true,穿透查詢會(huì)返回 false

上面的設(shè)計(jì)中還有一個(gè)問題,就是在 Do 阻塞時(shí),所有請(qǐng)求都會(huì)阻塞,內(nèi)存可能會(huì)出現(xiàn)大的問題。

此時(shí),Do 可以更換為DoChan,兩者實(shí)現(xiàn)上完全一樣,不同的是,DoChan() 通過 channel 返回結(jié)果。因此可以使用 select 語(yǔ)句實(shí)現(xiàn)超時(shí)控制

ch := g.DoChan(key, func() (interface{}, error) {
   ret, err := find(context.Background(), key)
   return ret, err
})
// Create our timeout
timeout := time.After(500 * time.Millisecond)
var ret singleflight.Result
select {
case <-timeout: // Timeout elapsed
   fmt.Println("Timeout")
   return
case ret = <-ch: // Received result from channel
   fmt.Printf("index: %d, val: %v, shared: %v\n", j, ret.Val, ret.Shared)
}

在超時(shí)時(shí)主動(dòng)返回,不阻塞。

此時(shí)又引入了另一個(gè)問題,這樣的每一次的請(qǐng)求,并不是高可用的,成功率是無法保證的。這時(shí)候可以增加一定的請(qǐng)求飽和度來保證業(yè)務(wù)的最終成功率,此時(shí)一次請(qǐng)求還是多次請(qǐng)求,對(duì)于下游服務(wù)而言并沒有太大區(qū)別,此時(shí)使用  singleflight  只是為了降低請(qǐng)求的數(shù)量級(jí),那么可以使用 Forget() 來提高下游請(qǐng)求的并發(fā)。

ch := g.DoChan(key, func() (interface{}, error) {
   go func() {
      time.Sleep(10 * time.Millisecond)
      fmt.Printf("Deleting key: %v\n", key)
      g.Forget(key)
   }()
   ret, err := find(context.Background(), key)
   return ret, err
})

當(dāng)然,這種做法依然無法保證100%的成功,如果單次的失敗無法容忍,在高并發(fā)的場(chǎng)景下需要使用更好的處理方案,比如犧牲一部分實(shí)時(shí)性、完全使用緩存查詢 + 異步更新等。

到此這篇關(guān)于golang使用sync.singleflight解決熱點(diǎn)緩存穿透問題的文章就介紹到這了,更多相關(guān)golang sync.singleflight緩存穿透內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang errors包快速上手

    Golang errors包快速上手

    errors 包是用于處理錯(cuò)誤的標(biāo)準(zhǔn)庫(kù), errors 包提供的功能比較簡(jiǎn)單,使用起來非常方便,下面就來介紹一下,感興趣的可以了解一下
    2025-05-05
  • Golang使用Gin實(shí)現(xiàn)文件上傳的示例代碼

    Golang使用Gin實(shí)現(xiàn)文件上傳的示例代碼

    本文我們主要介紹了Golang如何使用Gin實(shí)現(xiàn)文件上傳,Go標(biāo)準(zhǔn)庫(kù)net/http對(duì)文件上傳已經(jīng)提供了非常完善的支持,而Gin框架在其基礎(chǔ)上進(jìn)一步封裝,因此使用Gin開發(fā)文件上傳功能時(shí),只需要簡(jiǎn)單幾行代碼便可以實(shí)現(xiàn),需要的朋友可以參考下
    2024-02-02
  • Go語(yǔ)言中validation庫(kù)不能校驗(yàn)零值問題的解決方法

    Go語(yǔ)言中validation庫(kù)不能校驗(yàn)零值問題的解決方法

    在使用 Gin 框架的時(shí)候,前后端傳遞數(shù)據(jù)的時(shí)候,比如使用 JSON 格式,通常會(huì)使用 ShouldBindJSON 去用結(jié)構(gòu)體打 tag 綁定前端傳來的 JSON 格式數(shù)據(jù),本文給大家介紹了Go語(yǔ)言中validation庫(kù)不能校驗(yàn)零值問題的解決方法,需要的朋友可以參考下
    2024-08-08
  • golang中package?is?not?in?GOROOT報(bào)錯(cuò)的真正解決辦法

    golang中package?is?not?in?GOROOT報(bào)錯(cuò)的真正解決辦法

    這篇文章主要給大家介紹了關(guān)于golang中package?is?not?in?GOROOT報(bào)錯(cuò)的真正解決辦法,文中通過圖文介紹的非常詳細(xì),對(duì)同樣遇到這個(gè)問題的朋友具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-03-03
  • golang對(duì)自定義類型進(jìn)行排序的解決方法

    golang對(duì)自定義類型進(jìn)行排序的解決方法

    學(xué)習(xí)一門編程語(yǔ)言,要掌握原子數(shù)據(jù)類型,還需要掌握自定義數(shù)據(jù)類型。下面這篇文章主要給大家介紹了關(guān)于golang如何對(duì)自定義類型進(jìn)行排序的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2017-12-12
  • Go 語(yǔ)言入門學(xué)習(xí)之正則表達(dá)式

    Go 語(yǔ)言入門學(xué)習(xí)之正則表達(dá)式

    這篇文章主要介紹了Go 語(yǔ)言入門學(xué)習(xí)之正則表達(dá)式,文章基于GO語(yǔ)言的相關(guān)資料展開詳細(xì)內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-04-04
  • Go語(yǔ)言判斷文件或文件夾是否存在的方法

    Go語(yǔ)言判斷文件或文件夾是否存在的方法

    這篇文章主要介紹了Go語(yǔ)言判斷文件或文件夾是否存在的方法,結(jié)合具體實(shí)例形式對(duì)比分析了Go語(yǔ)言針對(duì)文件與目錄判斷的操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-05-05
  • Go語(yǔ)言非main包編譯為靜態(tài)庫(kù)并使用的示例代碼

    Go語(yǔ)言非main包編譯為靜態(tài)庫(kù)并使用的示例代碼

    本文以Windows為例,介紹一下如何將Go的非main包編譯為靜態(tài)庫(kù),用戶又將如何使用。通過實(shí)際項(xiàng)目創(chuàng)建常規(guī)工程,通過示例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-07-07
  • Go語(yǔ)言實(shí)現(xiàn)分布式鎖

    Go語(yǔ)言實(shí)現(xiàn)分布式鎖

    分布式鎖是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。如果不同的系統(tǒng)或是同一個(gè)系統(tǒng)的不同主機(jī)之間共享了一個(gè)或一組資源,那么訪問這些資源時(shí),需要通過一些互斥手段來防止彼此之間的干擾以保證一致性,在這種情況下,就需要使用分布式鎖了
    2023-01-01
  • 在Golang中正確的修改HTTPRequest的Host的操作方法

    在Golang中正確的修改HTTPRequest的Host的操作方法

    我們工作中經(jīng)常需要通過HTTP請(qǐng)求Server的服務(wù),比如腳本批量請(qǐng)求接口跑數(shù)據(jù),由于一些網(wǎng)關(guān)策略,部分Server會(huì)要求請(qǐng)求中Header里面附帶Host參數(shù),所以本文給大家介紹了如何在Golang中正確的修改HTTPRequest的Host,需要的朋友可以參考下
    2023-12-12

最新評(píng)論

丹凤县| 郸城县| 班玛县| 五指山市| 洛宁县| 茶陵县| 兴安县| 皮山县| 广宗县| 肇东市| 浑源县| 南安市| 鄂托克前旗| 兴安县| 合作市| 遂平县| 鄢陵县| 若羌县| 同江市| 漳平市| 澄迈县| 萝北县| 永州市| 邯郸县| 陇川县| 太谷县| 乌拉特后旗| 芜湖县| 都匀市| 绥芬河市| 临沧市| 都昌县| 大化| 体育| 松滋市| 历史| 伽师县| 民权县| 金阳县| 大田县| 阜新市|