Go语言是一种非常适合并发编程的编程语言,在实现高并发的服务或应用时,它的效能得到了很好的发挥。在日常开发中,我们可能会遇到需要并发请求接口或者并发处理大量的数据的场景,本文将介绍golang中如何实现并发请求接口。
并发请求接口的场景
在实际开发中,我们可能会遇到需要请求某个接口并获取响应数据的场景,比如:
- 获取某个网站上的商品数据。
- 从不同的API接口获取数据并汇总呈现。
- 同时请求多个数据源以便快速收集数据。
在单线程中,如果需要请求多个接口,那么需要一个接口请求完成之后再去请求另一个接口,这会导致整个流程变得缓慢。相反,使用并发请求接口则可以同时发起多个请求,大大提高请求效率。
goroutine并发处理
goroutine是go语言中的一种特殊的函数,它可以在与主线程并行的特殊线程中运行。多个goroutine同时运行可以同时请求多个接口,请求完成之后再进行数据整合处理。并发使用goroutine比较容易实现,通过go关键字实现即可。
WaitGroup控制goroutine
在实际开发中,我们可能会发现可能有一些协程可能比较耗时,可能需要更多的时间才能返回结果。在这种情况下,我们需要等待协程返回结果,进行后面的处理。这时候,我们需要使用sync.WaitGroup来控制goroutine数量,确保所有请求都得到响应结果。
package main import ( "fmt" "io/ioutil" "net/http" "sync" ) var wg sync.WaitGroup // 声明一个sync.WaitGroup实例,用于协程控制 func main() { urls := []string{"https://www.baidu.com", "https://www.qq.com", "https://www.taobao.com", "https://www.jd.com", "https://www.mi.com"} // 通过遍历urls,启动goroutine for _, url := range urls { wg.Add(1) // 添加一个goroutine go getBody(url) } wg.Wait() // 等待所有goroutine结束 } // getBody用于获取传入url的响应结果,并打印。 func getBody(url string) { resp, err := http.Get(url) // 发起http GET请求 if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) return } fmt.Printf("url: %s, contents: %s ", url, string(body)) wg.Done() // 相当于wg.Add(-1),标志该goroutine已经结束 }
在上面的代码中,我们先声明了一个sync.WaitGroup实例,用于协程数量控制。然后,在main()
函数中,通过遍历urls启动了多个协程,同时每次启动协程时,都会调用wg.Add(1)方法,表示需要等待一个协程完成。这样的话,WaitGroup中记录的等待的协程数量就会变成urls中url数量。然后在go getBody(url)
这一行,我们启动了请求url的协程,然后在协程结束的时候调用了wg.Done()
方法,表示该协程已经结束。
最后,wg.Wait()
调用使主协程等待所有协程结束。
并发请求的最佳实践
在实际开发中,我们需要注意一些细节,这些细节可以帮助我们更好地使用并发请求接口。
一、并发数量的控制
在并发请求接口的时候,我们需要控制并发的数量,特别是当接口请求数量比较大时,避免一次性请求使服务器受到太大压力。我们可以设立一个最大值,这样可以保证并发的最高数量。我们可以使用golang中的缓冲通道实现最大并发数的控制。
ch := make(chan struct{}, 5) // 声明一个缓冲通道,大小为5,控制并发数量为5 for _, url := range urls { ch <- struct{}{} // 把协程数量放在通道里 wg.Add(1) // 添加一个goroutine go func(url string) { defer wg.Done() getBody(url) <-ch // 从通道里取出一个值,表示这个协程已经结束 }(url) }
在声明缓冲通道的过程中,我们设置缓冲大小为5,表示最多同时运行5个goroutine,接着我们遍历urls,向通道中加入结构体值。
在启动goroutine的时候,我们声明了一个func(url string)
为处理函数,避免同时运行goroutine的最大数量超过5个,然后调用getBody(url)
方法。在goroutine结束的时候,我们通过通道释放一个信号,表示有一个goroutine结束了——<-ch
。
二、避免请求阻塞
在进行并发请求接口的时候,我们需要避免请求阻塞,通常出现在在一个请求长时间没有相应时。我们可以使用Golang中的context.Context解决这个问题。如果请求超时,则取消阻塞的请求。
url := "https://httpstat.us/200?sleep=8000" ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*5000) // 告诉请求,5秒之后自动取消 defer cancel() req, err := http.NewRequestWithContext(ctx, "GET", url, nil) // 使用请求上下文 if err != nil { log.Fatal(err) } client := http.DefaultClient resp, err := client.Do(req) // 发起请求 if err != nil { log.Fatal(err) } if resp.StatusCode == http.StatusOK { contents, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s ", contents) }
在上面的代码中,我们使用了context.WithTimeout
方法创建了一个请求上下文,其timeout设置为5秒,例如http://httpstat.us/200?sleep=8000,这个请求需要8秒才能返回数据。然后我们使用http.NewRequestWithContext方法创建一个使用请求上下文的请求。在发送请求时,我们使用http.DefaultClient
发起请求。最后,如果响应状态码是200,则输出响应数据。
当请求超时时,请求链路就会被直接关掉。这时我们会受到“context deadline exceeded”错误的提示。
三、避免请求重复
在请求接口时,可能会遇到重复请求同一个接口的情况,在这种情况下,我们应该避免重复请求同一个接口,这会浪费宝贵的时间和资源。我们可以使用Golang中的sync.Map解决这个问题。
var m = sync.Map{} url := "https://httpbin.org/get" wg.Add(2) go doGet(url, &m, &wg) go doGet(url, &m, &wg) wg.Wait() func doGet(url string, m *sync.Map, wg *sync.WaitGroup) { _, loaded := m.LoadOrStore(url, true) // 表示url已经被请求过,如果已存在,则直接返回,否则返回false并储存 if loaded { fmt.Printf("url %s already requested. ", url) wg.Done() return } resp, err := http.Get(url) if err != nil { log.Fatal(err) } defer resp.Body.Close() contents, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s ", contents) wg.Done() }
在上面的代码中,我们使用了一个sync.Map来保证url只被请求一次。在doGet
协程中,我们使用m.LoadOrStore(url, true)来判断url是否已经被请求过,如果请求过了,就return
直接退出协程。否则,我们发起http.Get请求并在log中打印响应数据。最后,我们通过wg.Done()
方法标志协程已经结束。
总结
本文介绍了使用golang实现并发请求接口的方法。通过使用goroutine并发处理,WaitGroup协程控制,以及缓冲通道控制并发数量。通过在请求上下文中设置超时来避免请求阻塞,并且使用sync.Map避免请求重复。通过使用这些技术,我们可以大大提高请求接口的效率,提升编码效率和编程体验。
以上是golang并发请求接口的详细内容。更多信息请关注PHP中文网其他相关文章!

OpenSSL,作为广泛应用于安全通信的开源库,提供了加密算法、密钥和证书管理等功能。然而,其历史版本中存在一些已知安全漏洞,其中一些危害极大。本文将重点介绍Debian系统中OpenSSL的常见漏洞及应对措施。DebianOpenSSL已知漏洞:OpenSSL曾出现过多个严重漏洞,例如:心脏出血漏洞(CVE-2014-0160):该漏洞影响OpenSSL1.0.1至1.0.1f以及1.0.2至1.0.2beta版本。攻击者可利用此漏洞未经授权读取服务器上的敏感信息,包括加密密钥等。

本文演示了创建模拟和存根进行单元测试。 它强调使用接口,提供模拟实现的示例,并讨论最佳实践,例如保持模拟集中并使用断言库。 文章

本文探讨了GO的仿制药自定义类型约束。 它详细介绍了界面如何定义通用功能的最低类型要求,从而改善了类型的安全性和代码可重复使用性。 本文还讨论了局限性和最佳实践

本文讨论了GO的反思软件包,用于运行时操作代码,对序列化,通用编程等有益。它警告性能成本,例如较慢的执行和更高的内存使用,建议明智的使用和最佳

本文讨论了GO中使用表驱动的测试,该方法使用测试用例表来测试具有多个输入和结果的功能。它突出了诸如提高的可读性,降低重复,可伸缩性,一致性和A

本文使用跟踪工具探讨了GO应用程序执行流。 它讨论了手册和自动仪器技术,比较诸如Jaeger,Zipkin和Opentelemetry之类的工具,并突出显示有效的数据可视化


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。