【譯】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