介绍
有很多方法可以优化代码以达到提高程序运行效率,减少进程数就是其中之一。在这篇文章中,我们将学习如何通过使用 Go 语言的 Singleflight 包减少重复的进程来优化 Go 代码。
假设你有一个 web 应用,每秒钟处理 10 个请求。根据数据观察其中一些请求是重复的,意味着可以减少请求的进程。
Singleflight 是可以解决这种问题的 Go 语言包之一,正如文档描述的一样,它可以避免函数重复调用。
这就好办了,既然已经知道函数会被重复调用,那我们实际上只需要减少重复调用的次数就可以,我们一起来看下实际场景中该如何使用。
我们将创建两个程序,server.go 和 client.go
server.go 作为服务端,模拟接受网络请求,接口地址 /api/v1/get_something
,参数为 name
// server.gopackage 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.gopackage main
import (
"io/ioutil"
"log"
"net/http"
"sync"
)
func main() {
var wg sync.WaitGroup
endpoint := "http://localhost:8080/api/v1/get_something?name=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.gopackage 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
推荐阅读