亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.430618.com 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

go-zero微服務框架中提供了許多開箱即用的工具,好的工具不僅能提升服務的性能而且還能提升代碼的魯棒性避免出錯,實現代碼風格的統一方便他人閱讀等等。

本文主要講述進程內共享調用神器SharedCalls。

使用場景

并發場景下,可能會有多個線程(協程)同時請求同一份資源,如果每個請求都要走一遍資源的請求過程,除了比較低效之外,還會對資源服務造成并發的壓力。舉一個具體例子,比如緩存失效,多個請求同時到達某服務請求某資源,該資源在緩存中已經失效,此時這些請求會繼續訪問DB做查詢,會引起數據庫壓力瞬間增大。而使用SharedCalls可以使得同時多個請求只需要發起一次拿結果的調用,其他請求"坐享其成",這種設計有效減少了資源服務的并發壓力,可以有效防止緩存擊穿。

高并發場景下,當某個熱點key緩存失效后,多個請求會同時從數據庫加載該資源,并保存到緩存,如果不做防范,可能會導致數據庫被直接打死。針對這種場景,go-zero框架中已經提供了實現,具體可參看sqlc和mongoc等實現代碼。

為了簡化演示代碼,我們通過多個線程同時去獲取一個id來模擬緩存的場景。如下:

func main() {
  const round = 5
  var wg sync.WaitGroup
  barrier := syncx.NewSharedCalls()

  wg.Add(round)
  for i := 0; i < round; i++ {
    // 多個線程同時執行
    go func() {
      defer wg.Done()
      // 可以看到,多個線程在同一個key上去請求資源,獲取資源的實際函數只會被調用一次
      val, err := barrier.Do("once", func() (interface{}, error) {
        // sleep 1秒,為了讓多個線程同時取once這個key上的數據
        time.Sleep(time.Second)
        // 生成了一個隨機的id
        return stringx.RandId(), nil
      })
      if err != nil {
        fmt.Println(err)
      } else {
        fmt.Println(val)
      }
    }()
  }

  wg.Wait()
}

運行,打印結果為:

837c577b1008a0db
837c577b1008a0db
837c577b1008a0db
837c577b1008a0db
837c577b1008a0db

可以看出,只要是同一個key上的同時發起的請求,都會共享同一個結果,對獲取DB數據進緩存等場景特別有用,可以有效防止緩存擊穿。

關鍵源碼分析

  • SharedCalls interface提供了Do和DoEx兩種方法的抽象
// SharedCalls接口提供了Do和DoEx兩種方法
type SharedCalls interface {
  Do(key string, fn func() (interface{}, error)) (interface{}, error)
  DoEx(key string, fn func() (interface{}, error)) (interface{}, bool, error)
}
  • SharedCalls interface的具體實現sharedGroup
// call代表對指定資源的一次請求
type call struct {
  wg  sync.WaitGroup  // 用于協調各個請求goroutine之間的資源共享
  val interface{}     // 用于保存請求的返回值
  err error           // 用于保存請求過程中發生的錯誤
}

type sharedGroup struct {
  calls map[string]*call
  lock  sync.Mutex
}
  • sharedGroup的Do方法
    • key參數:可以理解為資源的唯一標識。
    • fn參數:真正獲取資源的方法。
    • 處理過程分析:
// 當多個請求同時使用Do方法請求資源時
func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
  // 先申請加鎖
  g.lock.Lock()

  // 根據key,獲取對應的call結果,并用變量c保存
  if c, ok := g.calls[key]; ok {
    // 拿到call以后,釋放鎖,此處call可能還沒有實際數據,只是一個空的內存占位
    g.lock.Unlock()
    // 調用wg.Wait,判斷是否有其他goroutine正在申請資源,如果阻塞,說明有其他goroutine正在獲取資源
    c.wg.Wait()
    // 當wg.Wait不再阻塞,表示資源獲取已經結束,可以直接返回結果
    return c.val, c.err
  }

  // 沒有拿到結果,則調用makeCall方法去獲取資源,注意此處仍然是鎖住的,可以保證只有一個goroutine可以調用makecall
  c := g.makeCall(key, fn)
  // 返回調用結果
  return c.val, c.err
}
  • sharedGroup的DoEx方法
    • 和Do方法類似,只是返回值中增加了布爾值表示值是調用makeCall方法直接獲取的,還是取的共享成果
func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val interface{}, fresh bool, err error) {
  g.lock.Lock()
  if c, ok := g.calls[key]; ok {
    g.lock.Unlock()
    c.wg.Wait()
    return c.val, false, c.err
  }

  c := g.makeCall(key, fn)
  return c.val, true, c.err
}
  • sharedGroup的makeCall方法
    • 該方法由Do和DoEx方法調用,是真正發起資源請求的方法。
// 進入makeCall的一定只有一個goroutine,因為要拿鎖鎖住的
func (g *sharedGroup) makeCall(key string, fn func() (interface{}, error)) *call {
  // 創建call結構,用于保存本次請求的結果
  c := new(call)
  // wg加1,用于通知其他請求資源的goroutine等待本次資源獲取的結束
  c.wg.Add(1)
  // 將用于保存結果的call放入map中,以供其他goroutine獲取
  g.calls[key] = c
  // 釋放鎖,這樣其他請求的goroutine才能獲取call的內存占位
  g.lock.Unlock()

  defer func() {
    // delete key first, done later. can't reverse the order, because if reverse,
    // another Do call might wg.Wait() without get notified with wg.Done()
    g.lock.Lock()
    delete(g.calls, key)
    g.lock.Unlock()

    // 調用wg.Done,通知其他goroutine可以返回結果,這樣本批次所有請求完成結果的共享
    c.wg.Done()
  }()

  // 調用fn方法,將結果填入變量c中
  c.val, c.err = fn()
  return c
}

最后

本文主要介紹了go-zero框架中的 SharedCalls工具,對其應用場景和關鍵代碼做了簡單的梳理,希望本篇文章能給大家帶來一些收獲。

分享到:
標簽:擊穿 緩存
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定