Go每日一库之使用 Singleflight 优化你的代码
2023-3-16 08:52:39 Author: Go语言中文网(查看原文) 阅读量:27 收藏

介绍

有很多方法可以优化代码以达到提高程序运行效率,减少进程数就是其中之一。在这篇文章中,我们将学习如何通过使用 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?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.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


推荐阅读

福利
我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。


文章来源: http://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651454180&idx=1&sn=6424a857db48e545566a4bbe9117d6f4&chksm=80bb2416b7ccad0099f8d70a58810adf289aa20f5ccfdf08ee8fc3d6c9f2d9364b306660aa8d#rd
如有侵权请联系:admin#unsafe.sh