使用Singleflight优化Go代码

简介: 使用Singleflight优化Go代码

介绍


有许多方法可以优化代码以提高效率,减少运行进程就是其中之一。在本文中,我们将看到如何通过使用一个Go包Singleflight来减少重复进程,从而优化Go代码。


问题


假设你有一个web应用,它每秒有10个请求(RPS)。根据您所知道的数据,其中一些请求具有相同的模式,实际上可以生成相同的结果,这意味着实际上存在冗余流程。


从上面的插图中,我们知道用户1和用户2想要相同的东西,但最终,我们(大多数情况下)分别处理这两个请求。


解决方案


Singleflight是可以解决这类问题的Go包之一,如文档中所述,它提供了重复函数调用抑制机制。


很酷,如果我们知道我们要调用的函数是重复的,我们就可以减少处理的函数的数量,让我们看看在现实世界中如何使用它。


实现


我们将创建两个程序,server.goclient.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。我们将在服务器脚本的终端中看到如下内容:

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


客户端的输出是这样的:

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,这样它会更有效率。

// 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
}


重新启动服务器并再次运行客户端程序后,服务器的终端显示如下:

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


客户端的输出还是没有变化:

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的过程失败会怎样,以及我们如何缓存它。


相关文章
|
6月前
|
监控 Java 编译器
限流、控并发、减GC!一文搞懂Go项目资源优化的正确姿势
本章介绍Go语言项目在构建与部署阶段的性能调优和资源控制策略,涵盖编译优化、程序性能提升、并发与系统资源管理、容器化部署及自动化测试等内容,助力开发者打造高效稳定的生产级应用。
|
6月前
|
存储 安全 算法
Go语言泛型-泛型对代码结构的优化
Go语言自1.18版本引入泛型,极大提升了代码的通用性与可维护性。通过泛型,开发者可以减少重复代码、提高类型安全性,并增强程序的复用性和可读性。本文详细介绍了泛型在数据结构、算法及映射功能中的应用,展示了其在优化代码结构方面的优势。同时,Go编译器对泛型代码进行类型推导,确保运行时性能不受影响。合理使用泛型,有助于构建更加灵活高效的程序。
|
8月前
|
机器学习/深度学习 存储 监控
上网管理监控软件的 Go 语言流量特征识别算法实现与优化
本文探讨基于Go语言的流量特征识别算法,用于上网管理监控软件。核心内容涵盖AC自动机算法原理、实现及优化,通过路径压缩、哈希表存储和节点合并策略提升性能。实验表明,优化后算法内存占用降低30%,匹配速度提升20%。在1000Mbps流量下,CPU利用率低于10%,内存占用约50MB,检测准确率达99.8%。未来可进一步优化高速网络处理能力和融合机器学习技术。
223 10
|
存储 负载均衡 监控
如何利用Go语言的高效性、并发支持、简洁性和跨平台性等优势,通过合理设计架构、实现负载均衡、构建容错机制、建立监控体系、优化数据存储及实施服务治理等步骤,打造稳定可靠的服务架构。
在数字化时代,构建高可靠性服务架构至关重要。本文探讨了如何利用Go语言的高效性、并发支持、简洁性和跨平台性等优势,通过合理设计架构、实现负载均衡、构建容错机制、建立监控体系、优化数据存储及实施服务治理等步骤,打造稳定可靠的服务架构。
303 1
|
安全 Go 开发者
代码之美:Go语言并发编程的优雅实现与案例分析
【10月更文挑战第28天】Go语言自2009年发布以来,凭借简洁的语法、高效的性能和原生的并发支持,赢得了众多开发者的青睐。本文通过两个案例,分别展示了如何使用goroutine和channel实现并发下载网页和构建并发Web服务器,深入探讨了Go语言并发编程的优雅实现。
256 2
|
Go 数据安全/隐私保护 UED
优化Go语言中的网络连接:设置代理超时参数
优化Go语言中的网络连接:设置代理超时参数
|
SQL 监控 算法
为Go应用无侵入地添加任意代码
这篇文章旨在提供技术深度和实践指南,帮助开发者理解并应用这项创新技术来提高Golang应用的监控与服务治理能力。在接下来的部分,我们将通过一些实际案例,进一步展示如何在不同场景中应用这项技术,提供更多实践启示。
|
关系型数据库 Go 数据处理
高效数据迁移:使用Go语言优化ETL流程
在本文中,我们将探索Go语言在处理大规模数据迁移任务中的独特优势,以及如何通过Go语言的并发特性来优化数据提取、转换和加载(ETL)流程。不同于其他摘要,本文不仅展示了Go语言在ETL过程中的应用,还提供了实用的代码示例和性能对比分析。
|
缓存 NoSQL Redis
go-zero微服务实战系列(七、请求量这么高该如何优化)
go-zero微服务实战系列(七、请求量这么高该如何优化)
|
缓存 NoSQL 数据库
go-zero微服务实战系列(五、缓存代码怎么写)
go-zero微服务实战系列(五、缓存代码怎么写)