【譯】Go 語言:使用 Singleflight 優化你的代碼

你好,我是小四,你情商高,也可以叫我四哥~

介紹

有很多方法可以優化代碼以達到提高程序運行效率,減少進程數就是其中之一。在這篇文章中,我們將學習如何通過使用 Go 語言的 Singleflight 包減少重複的進程來優化 Go 代碼。

問題

假設你有一個 web 應用,每秒鐘處理 10 個請求。根據數據觀察其中一些請求是重複的,意味着可以減少請求的進程。

從上圖可以看出,user1 和 user2 請求相同的數據,但是最後我們還是分別處理了這兩個請求。

解決方法

Singleflight 是可以解決這種問題的 Go 語言包之一,正如文檔描述的一樣,它可以避免函數重複調用。

這就好辦了,既然已經知道函數會被重複調用,那我們實際上只需要減少重複調用的次數就可以,我們一起來看下實際場景中該如何使用。

實現

我們將創建兩個程序,server.go 和 client.go

server.go 作爲服務端,模擬接受網絡請求,接口地址 /api/v1/get_something,參數爲 name

// server.go

package main

import (
 "fmt"
 "net/http"
)

func main() {
 http.HandleFunc("/api/v1/get_something", func(w http.ResponseWriter, r *http.Request) {
  name := r.URL.Query().Get("name")
  response := processingRequest(name)
  fmt.Fprint(w, response)
 })

 err := http.ListenAndServe(":8080", nil)
 if err != nil {
  fmt.Println(err)
 }
}

func processingRequest(name string) string {
 fmt.Println("[DEBUG] processing request..")
 return "Hi there! You requested " + name
}

client.go 作爲客戶端,模擬請求服務端,併發數爲 5,併發數可以通過變量 totalRequests 設置。

// client.go

package main

import (
 "io/ioutil"
 "log"
 "net/http"
 "sync"
)

func main() {

 var wg sync.WaitGroup

 endpoint := "http://localhost:8080/api/v1/get_something?
 totalRequests := 5

 for i := 0; i < totalRequests; i++ {
  wg.Add(1)
  go func(i int) {
   defer wg.Done()
   makeAPICall(endpoint)
  }(i)
 }
 wg.Wait()

}

func makeAPICall(endpoint string) {
 resp, err := http.Get(endpoint)
 if err != nil {
  log.Fatalln(err)
 }

 body, err := ioutil.ReadAll(resp.Body)
 if err != nil {
  log.Fatalln(err)
 }

 result := string(body)
 log.Printf(result)
}

我們先執行 server.go,接着執行 client.go,服務端的終端輸出:

[DEBUG] processing request..
[DEBUG] processing request..
[DEBUG] processing request..
[DEBUG] processing request..
[DEBUG] processing request..

客戶端終端輸出:

Hi there! You requested something
Hi there! You requested something
Hi there! You requested something
Hi there! You requested something
Hi there! You requested something

這與我們預期是一致的,客戶端發送了 5 次請求,服務端處理了這 5 次請求。

現在,我們使用 Singleflight 包優化下代碼,使程序更有效率。

// server.go

package main

import (
 "fmt"
 "net/http"

 "golang.org/x/sync/singleflight"
)

var g = singleflight.Group{}

func main() {
 http.HandleFunc("/api/v1/get_something", func(w http.ResponseWriter, r *http.Request) {
  name := r.URL.Query().Get("name")
  response, _, _ := g.Do(name, func() (interface{}, error) {
   result := processingRequest(name)
   return result, nil
  })

  fmt.Fprint(w, response)
 })

 err := http.ListenAndServe(":8080", nil)
 if err != nil {
  fmt.Println(err)
 }
}

func processingRequest(name string) string {
 fmt.Println("[DEBUG] processing request..")
 return "Hi there! You requested " + name
}

現在重啓服務端,並重新執行客戶端程序,服務端輸出如下:

[DEBUG] processing request..
[DEBUG] processing request..

客戶端終端輸出:

Hi there! You requested something
Hi there! You requested something
Hi there! You requested something
Hi there! You requested something
Hi there! You requested something

從輸出可以看出,所有的客戶端都得到了預期的響應,但現在服務端只需要處理兩個請求。想象一下,假如你要處理成千上萬個類似的請求,那將帶來多大的效率,太不可思議了!

總結

在這篇文章中,我們瞭解了 Singleflight 包在優化代碼方面提供了極大的便利。不僅在處理網絡請求方面,你還可以將它擴展到其他方面,比如控制請求數據庫的併發數等。

還有一些其他知識點在本文中沒有涉及到,比如如果 Singleflight 包在處理過程中失敗了該怎麼處理。我將在下篇文章中討論。

via: https://levelup.gitconnected.com/optimize-your-go-code-using-singleflight-3f11a808324
作者:Dzaky Widya Putra



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