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


相关文章
|
3月前
|
Cloud Native Go 开发工具
不改一行代码轻松玩转 Go 应用微服务治理
为了更好的进行 Go 应用微服务治理,提高研发效率和系统稳定性,本文将介绍 MSE 微服务治理方案,无需修改业务代码,实现治理能力。
19848 9
|
14天前
|
安全 Go 开发者
代码之美:Go语言并发编程的优雅实现与案例分析
【10月更文挑战第28天】Go语言自2009年发布以来,凭借简洁的语法、高效的性能和原生的并发支持,赢得了众多开发者的青睐。本文通过两个案例,分别展示了如何使用goroutine和channel实现并发下载网页和构建并发Web服务器,深入探讨了Go语言并发编程的优雅实现。
28 2
|
2月前
|
关系型数据库 Go 数据处理
高效数据迁移:使用Go语言优化ETL流程
在本文中,我们将探索Go语言在处理大规模数据迁移任务中的独特优势,以及如何通过Go语言的并发特性来优化数据提取、转换和加载(ETL)流程。不同于其他摘要,本文不仅展示了Go语言在ETL过程中的应用,还提供了实用的代码示例和性能对比分析。
|
1月前
|
JSON 搜索推荐 Go
ZincSearch搜索引擎中文文档及在Go语言中代码实现
ZincSearch官网及开发文档均为英文,对非英语用户不够友好。GoFly全栈开发社区将官方文档翻译成中文,并增加实战经验和代码,便于新手使用。本文档涵盖ZincSearch在Go语言中的实现,包括封装工具库、操作接口、统一组件调用及业务代码示例。官方文档https://zincsearch-docs.zinc.dev;中文文档https://doc.goflys.cn/docview?id=41。
|
3月前
|
缓存 NoSQL Redis
go-zero微服务实战系列(七、请求量这么高该如何优化)
go-zero微服务实战系列(七、请求量这么高该如何优化)
|
3月前
|
缓存 NoSQL 数据库
go-zero微服务实战系列(五、缓存代码怎么写)
go-zero微服务实战系列(五、缓存代码怎么写)
|
3月前
|
Kubernetes 监控 Cloud Native
"解锁K8s新姿势!Cobra+Client-go强强联手,打造你的专属K8s监控神器,让资源优化与性能监控尽在掌握!"
【8月更文挑战第14天】在云原生领域,Kubernetes以出色的扩展性和定制化能力引领潮流。面对独特需求,自定义插件成为必要。本文通过Cobra与Client-go两大利器,打造一款监测特定标签Pods资源使用的K8s插件。Cobra简化CLI开发,Client-go则负责与K8s API交互。从初始化项目到实现查询逻辑,一步步引导你构建个性化工具,开启K8s集群智能化管理之旅。
53 2
|
3月前
|
测试技术 编译器 Go
依赖注入与控制反转:优化Go语言REST API客户端
依赖注入与控制反转:优化Go语言REST API客户端
|
3月前
|
Java 数据库连接 数据库
携手前行:在Java世界中深入挖掘Hibernate与JPA的协同效应
【8月更文挑战第31天】Java持久化API(JPA)是一种Java规范,为数据库数据持久化提供对象关系映射(ORM)方法。JPA定义了实体类与数据库表的映射及数据查询和事务控制方式,确保不同实现间的兼容性。Hibernate是JPA规范的一种实现,提供了二级缓存、延迟加载等丰富特性,提升应用性能和可维护性。通过结合JPA和Hibernate,开发者能编写符合规范且具有高度可移植性的代码,并利用Hibernate的额外功能优化数据持久化操作。
41 0
|
3月前
|
Go C语言
Go语言:新时代的编程英雄,让你的代码驾驭未来!
【8月更文挑战第29天】Go,或称Golang,是由谷歌开发的一种静态强类型的编译语言,旨在融合C语言的高效性和高级语言的易用性。它简洁、优雅,广泛应用于高性能服务器和网络应用开发。本文将通过环境搭建、Hello World示例、变量、常量、控制结构、函数、结构体、接口及错误处理等示例,带你快速入门Go语言,领略其简洁高效的魅力,激发你的编程热情。
39 0