您的位置:首页 > 技术中心 > 其他 >

golang并发请求接口

时间:2023-05-10 16:56

Go语言是一种非常适合并发编程的编程语言,在实现高并发的服务或应用时,它的效能得到了很好的发挥。在日常开发中,我们可能会遇到需要并发请求接口或者并发处理大量的数据的场景,本文将介绍golang中如何实现并发请求接口。

并发请求接口的场景

在实际开发中,我们可能会遇到需要请求某个接口并获取响应数据的场景,比如:

  • 获取某个网站上的商品数据。
  • 从不同的API接口获取数据并汇总呈现。
  • 同时请求多个数据源以便快速收集数据。

在单线程中,如果需要请求多个接口,那么需要一个接口请求完成之后再去请求另一个接口,这会导致整个流程变得缓慢。相反,使用并发请求接口则可以同时发起多个请求,大大提高请求效率。

goroutine并发处理

goroutine是go语言中的一种特殊的函数,它可以在与主线程并行的特殊线程中运行。多个goroutine同时运行可以同时请求多个接口,请求完成之后再进行数据整合处理。并发使用goroutine比较容易实现,通过go关键字实现即可。

WaitGroup控制goroutine

在实际开发中,我们可能会发现可能有一些协程可能比较耗时,可能需要更多的时间才能返回结果。在这种情况下,我们需要等待协程返回结果,进行后面的处理。这时候,我们需要使用sync.WaitGroup来控制goroutine数量,确保所有请求都得到响应结果。

package mainimport (    "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,控制并发数量为5for _, 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.DefaultClientresp, 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并发请求接口的详细内容,更多请关注Gxl网其它相关文章!

热门排行

今日推荐

热门手游