首頁  >  文章  >  後端開發  >  go 中的多線程請求並且沒有獲得高 RPS

go 中的多線程請求並且沒有獲得高 RPS

WBOY
WBOY轉載
2024-02-09 16:48:10983瀏覽

go 中的多线程请求并且没有获得高 RPS

php小編西瓜注意到,使用Go語言進行多執行緒請求時,有時候並不能獲得高的請求每秒(RPS)速度。儘管Go語言在並發處理方面表現出色,但在某些情況下,多執行緒請求的效率並不高。這可能是由於網路延遲、資源競爭等因素導致的。在這篇文章中,我們將探討這個問題,並提供一些可能的解決方案來提高Go語言多執行緒請求的RPS。

問題內容

我正在嘗試編寫一個多執行緒客戶端來測試我的伺服器。當我使用 2 個 goroutine 時,一切都很好,我得到 50k rps 並且我的 cpu 負載正常,但是當我創建超過 2 個 goroutine 時,rps 下降到 3k,但我的 cpu 負載超過了。儘管當我多次運行客戶端程式碼時(例如同時在 3 個控制台上運行相同的程式碼),我獲得了更多 rps,例如 80k rps。

這是我的客戶端程式碼

package main

import (
    "fmt"
    "net/http"
    "os"
    "sync"
    "time"
)

func main() {
    requesturl := fmt.sprintf("http://localhost:%d/home", 3333)
    var wg sync.waitgroup
    wg.add(4)
    req, err := http.newrequest(http.methodget, requesturl, nil)
    if err != nil {
        fmt.printf("client: could not create request: %s\n", err)
        os.exit(1)
    }
    for i := 0; i < 4; i++ {
        go func() {
            defer wg.done()
            client := http.client{
                timeout: 30 * time.second,
            }
            for {
                client.do(req)
            }
        }()
    }
    wg.wait()
}

這是我的伺服器端程式碼

package main

import (
    "errors"
    "fmt"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "log"
    "net/http"
    "os"
    "sync"
)

// log handling
func openlogfile(path string) (*os.file, error) {
    logfile, err := os.openfile(path, os.o_wronly|os.o_append|os.o_create, 0644)
    if err != nil {
        return nil, err
    }
    return logfile, nil
}

// variables of counter in metric
var okstatuscounter = prometheus.newcounter(
    prometheus.counteropts{
        name: "ok_request_count",
        help: "number of 200",
    },
)

func listener(serverlog *log.logger) http.handlerfunc {
    return func(w http.responsewriter, r *http.request) {
        //metric 
        okstatuscounter.inc()
        
        w.writeheader(http.statusok)
    }
}

func main() {
    //metric
    prometheus.mustregister(okstatuscounter)

    //log handling
    filesimpleserverlog, err := openlogfile("simpleserver/simpleserverlog.log")
    if err != nil {
        log.fatal(err)
    }
    serverlog := log.new(filesimpleserverlog, "[simple server]", log.lstdflags|log.lshortfile|log.lmicroseconds)

    var wg sync.waitgroup
    wg.add(1)

    //server:
    go func() {
        defer wg.done()
        mux := http.newservemux()
        mux.handlefunc("/home", listener(serverlog))
        mux.handle("/metrics", promhttp.handler())
        server := http.server{
            addr:    fmt.sprintf(":%d", 3333),
            handler: mux,
        }
        if err := server.listenandserve(); err != nil {
            if !errors.is(err, http.errserverclosed) {
                serverlog.printf("error running http server: %s\n", err)
            }
        }
    }()
    wg.wait()
}

一開始我以為 go 可能會使用一個連接埠來進行所有客戶端連接,但是當我用 netstat 檢查它時,它使用了多個連接埠。我嘗試搜尋但找不到合適的答案

我嘗試了sync.mutex:

var mu sync.Mutex
...
for i := 0; i < 1000; i++ {
        go func() {
            defer wg.Done()
            client := http.Client{
                //Timeout: 30 * time.Second,
            }
            for {
                mu.Lock()
                _, err := client.Do(req)
                if err != nil {
                    clientLog.Printf("client: error making http request: %s\n", err)
                    os.Exit(1)
                }
                mu.Unlock()
            }
        }()
    }
    wg.Wait()
...

透過上述更改,我獲得了13k rps,並且我的cpu 負載正常,但這根本不夠

解決方法

由於您僅向一台主機發送請求,因此http傳輸的預設值不適合您。最好根據您的情況手動設定參數:

t := http.DefaultTransport.(*http.Transport).Clone()
t.MaxIdleConns = 100
t.MaxConnsPerHost = 100
t.MaxIdleConnsPerHost = 100
    
httpClient = &http.Client{
  Timeout:   10 * time.Second,
  Transport: t,
}

有關更多信息,您可以閱讀這裡

以上是go 中的多線程請求並且沒有獲得高 RPS的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除