简体   繁体   中英

Correct use of fasthttp.Client combined with goroutines

I am new to Go and am looking for the correct way of using net/http or fasthttp with goroutines. Unfortunately there are not many fasthttp client examples out there.

I found the following code: ( Example1 )

package main

import (
    "bufio"
    "fmt"
    "github.com/valyala/fasthttp"
    "log"
    "net"
    "os"
    "sync"
    "time"
)

func grabPage(fastClient *fasthttp.Client, i int, wg *sync.WaitGroup) {
    defer wg.Done()
    _, body, err := fastClient.GetTimeout(nil, "https://en.wikipedia.org/wiki/Immanuel_Kant", time.Duration(time.Second*20))
    if err != nil {
        log.Fatal(err)
    }
    f, err := os.Create(fmt.Sprintf("./data/%d.txt", i))
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    w := bufio.NewWriter(f)
    w.Write(body)
}

func main() {
    var wg sync.WaitGroup
    total := 500

    c := &fasthttp.Client{
        Dial: func(addr string) (net.Conn, error) {
            return fasthttp.DialTimeout(addr, time.Second*10)
        },
        MaxConnsPerHost: total,
    }

    wg.Add(total)
    for index := 0; index < total; index++ {
        go grabPage(c, index, &wg)
    }
    wg.Wait()
}

In this code the developer creates a fasthttp.Client instance in the main() function and passes it to the goroutine using go grabPage(c, ...) . For my understanding, this way you create one instance and all the requests use this one instance to do the job.

On another page, a developer uses something like that: ( Example2 )

func grabPage(i int, wg *sync.WaitGroup) {
    defer wg.Done()

    fastClient := &fasthttp.Client{
        Dial: func(addr string) (net.Conn, error) {
            return fasthttp.DialTimeout(addr, time.Second*10)
        },
        MaxConnsPerHost: 500,
    }

    _, body, err := fastClient.GetTimeout(nil, "https://en.wikipedia.org/wiki/Immanuel_Kant", time.Duration(time.Second*20))
    if err != nil {
        log.Fatal(err)
    }
    f, err := os.Create(fmt.Sprintf("./data/%d.txt", i))
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    w := bufio.NewWriter(f)
    w.Write(body)
}

The big question is, are both solutions correct? Or does the Example2 solution really create a new instance and use a lot of memory for every goroutine?

I made the examples out of snippets for my question, in the Example2 for sure defer are missing. This is not part of the question.

A small side question: (fastClient *fasthttp.Client, i int, wg *sync.WaitGroup) -> fastClient and wg are pointers, so why call grabPage(c, index, &wg) and not grabPage(&c, index, &wg) ?

The big answer: both are correct (as in they work just fine), just different.

Per the docs , a fasthttp.Client is safe for concurrent use so sharing one instance is fine. It may run into concurrent connection limits but that might not be an issue.

The second example does have some overhead and will not be able to reuse connections or parameters, but again this could be a use case where it does not matter (if I only perform two operations, saving on the overhead might not be worth optimizing for).

For the second part of the question:

  • c is already a *fasthttp.Client , so there's not need to take its address ( &fasthttp.Client returns a pointer to a new fasthttp.Client )
  • wg is a plain sync.WaitGroup so the address must be taken

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM