使用Singleflight优化Go代码

TimLiuDream · · 695 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

# 介绍 有许多方法可以优化代码以提高效率,减少运行进程就是其中之一。在本文中,我们将看到如何通过使用一个Go包Singleflight来减少重复进程,从而优化Go代码。 # 问题 假设你有一个web应用,它每秒有10个请求(RPS)。根据您所知道的数据,其中一些请求具有相同的模式,实际上可以生成相同的结果,这意味着实际上存在冗余流程。 ![1.png](https://static.golangjob.cn/230905/789b1f05a62851a5f4a16285f2e95c20.png) 从上面的插图中,我们知道用户1和用户2想要相同的东西,但最终,我们(大多数情况下)分别处理这两个请求。 # 解决方案 Singleflight是可以解决这类问题的Go包之一,如文档中所述,它提供了重复函数调用抑制机制。 很酷,如果我们知道我们要调用的函数是重复的,我们就可以减少处理的函数的数量,让我们看看在现实世界中如何使用它。 # 实现 我们将创建两个程序,`server.go` 和 `client.go`。 server.go — 将作为web服务,可以接收 `/api/v1/get_something` 的请求,参数名为`name` ```go // 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(":15001", 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 — 将作为一个客户端,向web服务发出5个并发请求(你可以在变量`totalRequests`中设置这个数字)。 ```go // client.go package main import ( "io" "log" "net/http" "sync" ) func main() { var wg sync.WaitGroup endpoint := "http://localhost:15001/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 := io.ReadAll(resp.Body) if err != nil { log.Fatalln(err) } result := string(body) log.Printf(result) } ``` 首先,我们可以运行 `server.go`,然后继续执行 `client.go`。我们将在服务器脚本的终端中看到如下内容: ```go [DEBUG] processing request.. [DEBUG] processing request.. [DEBUG] processing request.. [DEBUG] processing request.. [DEBUG] processing request.. ``` 客户端的输出是这样的: ```go 2023/09/05 10:29:34 Hi there! You requested something 2023/09/05 10:29:34 Hi there! You requested something 2023/09/05 10:29:34 Hi there! You requested something 2023/09/05 10:29:34 Hi there! You requested something 2023/09/05 10:29:34 Hi there! You requested something ``` 这是正确的,因为我们从客户端发送了五个请求,并在服务器中处理了这五个请求。 现在让我们在代码中实现`Singleflight`,这样它会更有效率。 ```go // 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(":15001", nil) if err != nil { fmt.Println(err) } } func processingRequest(name string) string { fmt.Println("[DEBUG] processing request..") return "Hi there! You requested " + name } ``` 重新启动服务器并再次运行客户端程序后,服务器的终端显示如下: ```go [DEBUG] processing request.. [DEBUG] processing request.. ``` 客户端的输出还是没有变化: ```go 2023/09/05 10:32:49 Hi there! You requested something 2023/09/05 10:32:49 Hi there! You requested something 2023/09/05 10:32:49 Hi there! You requested something 2023/09/05 10:32:49 Hi there! You requested something 2023/09/05 10:32:49 Hi there! You requested something ``` 太好了!所有客户端都得到了预期的响应,但是现在我们的服务器只处理了两个请求。想象一下,如果您处理数千个类似的请求,您将带来多大的效率,这是惊人的! # 结论 在本文中,我们了解了`Singleflight`在优化代码方面的强大功能。不仅仅是处理一个`web`请求,你还可以将它的用例扩展到其他事情上,比如从数据库中获取数据等等。 还有一些我在本文中没有涉及的内容,例如`Singleflight`的过程失败会怎样,以及我们如何缓存它。 ![Snipaste_2023-09-05_22-36-27.png](https://static.golangjob.cn/230905/2237d5c7cdb607ebd3e9e7394aa9c7db.png)

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

695 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传