細聊 Go 高級併發用法 - singleflight

什麼是 singleflight

Singleflight 是 Google 開源的一個 Go 語言庫,主要用於控制對共享資源的重複請求,防止服務器被同時高併發訪問所引發的 "雪崩" 問題。

具體來說,當多個相同的請求(例如訪問同一個數據或地址)在同一時間發生時,Singleflight 會將這些請求合併爲一個。實際上,只有一個請求會被執行,然後這個請求的結果將被所有其他的請求共享。

這個庫的主要應用場景是緩存系統。例如,當有大量用戶同時請求同一資源時,如果沒有 Singleflight,服務器可能需要執行大量的數據庫查詢或其他高負荷操作。但有了 Singleflight,服務器只需執行一次查詢或操作,然後將結果返回給所有請求。這大大減輕了服務器的負載。

主要作用

     Singleflight 的主要作用是防止對共享資源的多次重複請求,特別是在高併發的場景中。這樣可以避免不必要的重複工作,減輕服務器負載,提高系統的性能和效率。

    當有多個相同的請求同時發生時,Singleflight 會把這些請求合併爲一個,只執行一次請求操作(如數據庫查詢,HTTP 請求等),然後把這個結果共享給所有的請求。這就避免了相同的請求被重複執行多次,提高了系統的性能。

        此外,Singleflight 還可以幫助抑制雪崩效應,當系統中某一部分因過載而出現問題時,不會影響到整個系統的運行。

Go 如何使用

var g singleflight.Group
func someExpensiveOperation() (interface{}, error) {
   time.Sleep(1 * time.Second)
   return "foo", nil
}
func main() {
   for i := 0; i < 5; i++ {
      go func() {
         v, err, _ := g.Do("some_key", someExpensiveOperation)
         if err != nil {
            fmt.Printf("error: %v\n", err)
            return
         }
         fmt.Printf("got: %v\n", v)
      }()
   }
   time.Sleep(2 * time.Second)
}

        在這個例子中,someExpensiveOperation 代表一項消耗很大的操作,比如一個複雜的數據庫查詢或者一個網絡請求。當這個函數被併發調用多次時,singleflight 會保證這個函數只執行一次,然後各個併發的調用者將獲得相同的返回結果。

        注意 "some_key" 這個參數,singleflight 使用這個 key 去區分不同的請求,只有在相同的 key 的訪問中,纔會合併請求。

僞代碼示例

func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
   g.mu.Lock()
   if g.m == nil {
      g.m = make(map[string]*call)
   }
   if c, ok := g.m[key]; ok {
      g.mu.Unlock()
      c.wg.Wait()
      return c.val, c.err
   }
   c := new(call)
   c.wg.Add(1)
   g.m[key] = c
   g.mu.Unlock()
   c.val, c.err = fn()
   c.wg.Done()
   g.mu.Lock()
   delete(g.m, key)
   g.mu.Unlock()
   return c.val, c.err
}

        以上僞代碼中,Group 是 Singleflight 的主要結構,它有一個鎖和一個 map,用於存儲所有的請求(call)。鎖保證了對同一資源的訪問在同一時間只能由一個進程進行。

        Do 方法是實現請求合併的核心,它首先會檢查當前的請求(key 表示的請求)是否正在被處理,如果是則等待處理結束,然後返回結果,如果不是則執行請求(fn),並將結果存儲在 call 結構中,供之後的請求使用。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/-7946ApjpoQa4-kb6opHiw