valyala / fasthttp

Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http
MIT License
21.94k stars 1.76k forks source link

best way to send a lot of requets #1628

Closed odnobit closed 1 year ago

odnobit commented 1 year ago

Hello! Package version: v1.5.0 This is code example (here i'm sending 10k reqs):

package main

import (
    "encoding/json"
    "fmt"
    "sync"
    "time"

    "github.com/valyala/fasthttp"
)

type sendBody struct {
    ID    string `json:"uuid"`
    Value int    `json:"value"`
}

func main() {
    var errReqCounter int
    var successReqCounter int
    var wg sync.WaitGroup
    var mu sync.Mutex

    reqCount := 10000
    hc := &fasthttp.Client{
        WriteTimeout:        time.Second,
        ReadTimeout:         time.Second,
        MaxIdleConnDuration: time.Hour,
        MaxConnsPerHost:     reqCount,
    }
    wg.Add(reqCount)
    for i := 0; i < reqCount; i++ {
        go func(index int) {
            defer wg.Done()
            defer mu.Unlock()
            req := fasthttp.AcquireRequest()
            resp := fasthttp.AcquireResponse()
            defer fasthttp.ReleaseRequest(req)
            defer fasthttp.ReleaseResponse(resp)

            req.SetRequestURI("http://localhost:8080/post")
            req.Header.SetMethod(fasthttp.MethodPost)
            req.Header.Set("Content-Type", "application/json")
            body, _ := json.Marshal(sendBody{ID: "10110101101", Value: index})
            req.SetBody(body)

            if err := hc.Do(req, resp); err != nil {
                fmt.Printf("request err: %v\n", err)
                mu.Lock()
                errReqCounter++
            } else {
                if resp.StatusCode() == fasthttp.StatusOK {
                    mu.Lock()
                    successReqCounter++
                } else {
                    mu.Lock()
                    errReqCounter++
                }
            }
        }(i)
    }
    wg.Wait()
    fmt.Printf("Statistics\nError: %v\nSuccess: %v\n", errReqCounter, successReqCounter)
}

And i got so many errors like

* request err: dial tcp4 127.0.0.1:8080: i/o timeout
* request err: dialing to the given TCP address timed out
* dial tcp4 127.0.0.1:8080: i/o timeout

Statistics:

Error: 9857
Success: 143

Can you please provide best way to resolve this task? I also was trying to use RateLimiter by time (eg 250 requets per second) and got same result.

odnobit commented 1 year ago

P.S. wrk utility showed ~850 RPS when im was testing my server

klaza2 commented 1 year ago

P.S. wrk utility showed ~850 RPS when im was testing my server

i want instal the tool can u hellp me ? @

odnobit commented 1 year ago

P.S. wrk utility showed ~850 RPS when im was testing my server

i want instal the tool can u hellp me ? @

https://github.com/wg/wrk

erikdubbelboer commented 1 year ago

Why are you using v1.5.0 if we are already at v1.49.0?

With your code you're trying to send 10000 at exactly the same time. You should instead make for example 32 workers and have those send the requests one by one. That way you'll only send 32 at the same time and you shouldn't get as many errors (depending on the limitations of the receiving server). wrk will for example use 10 "workers" if you don't specify this on the command line.

odnobit commented 1 year ago

Why are you using v1.5.0 if we are already at v1.49.0?

With your code you're trying to send 10000 at exactly the same time. You should instead make for example 32 workers and have those send the requests one by one. That way you'll only send 32 at the same time and you shouldn't get as many errors (depending on the limitations of the receiving server). wrk will for example use 10 "workers" if you don't specify this on the command line.

Thanks for your answer. Actually im using v1.50.0, I made mistake in the message. What is the best way to do 32 or more workers with fasthttp? Im really need an example :)

erikdubbelboer commented 1 year ago

Something like this:

package main

import (
    "encoding/json"
    "fmt"
    "sync"
    "sync/atomic"
    "time"

    "github.com/valyala/fasthttp"
)

type sendBody struct {
    ID    string `json:"uuid"`
    Value int    `json:"value"`
}

func main() {
    var errReqCounter atomic.Int64
    var successReqCounter atomic.Int64
    var wg sync.WaitGroup

    hc := &fasthttp.Client{
        WriteTimeout:        time.Second,
        ReadTimeout:         time.Second,
        MaxIdleConnDuration: time.Hour,
        MaxConnsPerHost:     reqCount,
    }

    work := make(chan int, workers)
    workers := 32

    wg.Add(workers)
    for i := 0; i < workers; i++ {
        go func() {
            defer wg.Done()

            for index := range work {
                req := fasthttp.AcquireRequest()
                resp := fasthttp.AcquireResponse()
                defer fasthttp.ReleaseRequest(req)
                defer fasthttp.ReleaseResponse(resp)

                req.SetRequestURI("http://localhost:8080/post")
                req.Header.SetMethod(fasthttp.MethodPost)
                req.Header.Set("Content-Type", "application/json")
                body, _ := json.Marshal(sendBody{ID: "10110101101", Value: index})
                req.SetBody(body)

                if err := hc.Do(req, resp); err != nil {
                    fmt.Printf("request err: %v\n", err)
                    errReqCounter.Add(1)
                } else {
                    if resp.StatusCode() == fasthttp.StatusOK {
                        successReqCounter.Add(1)
                    } else {
                        errReqCounter.Add(1)
                    }
                }
            }
        }()
    }

    reqCount := 10000

    for i := 0; i < reqCount; i++ {
        work <- i
    }

    close(work)

    wg.Wait()
    fmt.Printf("Statistics\nError: %v\nSuccess: %v\n", errReqCounter.Load(), successReqCounter.Load())
}
odnobit commented 1 year ago

thx for help :)

lunarxploit commented 1 year ago

with proxies?

erikdubbelboer commented 1 year ago

With proxies you can use this: https://pkg.go.dev/github.com/valyala/fasthttp@v1.50.0/fasthttpproxy and set one of those as Dial function for fasthttp.Client.