dongtaogu8510 2019-03-09 08:34
浏览 84

如何计算资源大小以防止Golang http服务器达到操作系统资源限制

I have a simple golang rest application that answer to an echo request like this:

// Return echo message
func echoHandler(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    message := params["message"]

    fmt.Fprintf(w, "%s", message)
}


func main() {


    // Config data
    port := ":9596"



    router := mux.NewRouter() //.StrictSlash(true)

    router.HandleFunc("/echo/{message}", echoHandler).Methods("GET")

    srv := &http.Server{
        Addr:    port,
        // Good practice to set timeouts to avoid Slowloris attacks.
        // Using just the read parameter due to this article
        // https://stackoverflow.com/questions/29334407/creating-an-idle-timeout-in-go
        //WriteTimeout: time.Second * 60,
        ReadTimeout:  time.Second * 15,
        ////:  time.Second * 120,
        //Handler: router,
        Handler: router,
    }

    // Run our server in a goroutine so that it doesn't block.
    go func() {
        log.Println("Running server....")

        //log.Fatal(http.ListenAndServe(port, router))
        //log.Println(s.ListenAndServe())
        if err := srv.ListenAndServe(); err != nil {
            log.Println(err)
        }
    }()

    c := make(chan os.Signal, 1)
    // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
    // SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
    signal.Notify(c, os.Interrupt)

    // Block until we receive our signal.
    <-c

    // Create a deadline to wait for.
    ctx, cancel := context.WithTimeout(context.Background(), wait)
    defer cancel()
    // Doesn't block if no connections, but will otherwise wait
    // until the timeout deadline.
    srv.Shutdown(ctx)
    // Optionally, you could run srv.Shutdown in a goroutine and block on
    // <-ctx.Done() if your application should wait for other services
    // to finalize based on context cancellation.
    fmt.Println("

")
    log.Println("shutting down")
    log.Println("Goddbye!....")
    os.Exit(0)


}

This program is called by another service through a GET request:

// Return default message for root routing
func Index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}

// Handle iterative path and calls iterative calculation service
func echoHandler(calledServiceURL string) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {

        params := mux.Vars(r)

        url := calledServiceURL + "/echo/" + params["message"]

        tr := &http.Transport{
            //MaxIdleConns:       500,
            //MaxIdleConnsPerHost:  500,
        }

        netClient := &http.Client{Transport: tr}

        req, reqErr := http.NewRequest("GET", url, nil)
        if reqErr != nil {
            log.Fatal("Error en response: ", reqErr)
            fmt.Fprintf(w, "Error en response: %s", reqErr)
            return
        }

        //req.Header.Set("Connection", "close")
        //req.Header.Set("Connection", "Keep-Alive")


        //resp, err := netClient.Get(url)
        resp, err := netClient.Do(req)
        if err != nil {
            log.Fatal("Error en response: ", err)
            fmt.Fprintf(w, "Error en response: %s", err)
            return
        }

        respData, errResp := ioutil.ReadAll(resp.Body)
        defer resp.Body.Close()
        if errResp != nil {
            log.Fatal("Error en RespData", errResp)
            fmt.Fprintf(w, "Error en respData: %s", err)
            return
        }

        fmt.Fprintf(w, "%s", respData)

    }
}


// Main function
func main() {



    // Set default values
    port := ":9296"
    calledServiceURL := "http://localhost:9596"


    router := mux.NewRouter() //.StrictSlash(true)

    router.HandleFunc("/", Index).Methods("GET")
    router.HandleFunc("/echo/{message}", echoHandler(calledServiceURL)).Methods("GET")


    srv := &http.Server{
        Addr:         port,
        // Good practice to set timeouts to avoid Slowloris attacks.
        // Using just the read parameter due to this article
        // https://stackoverflow.com/questions/29334407/creating-an-idle-timeout-in-go
        //WriteTimeout: time.Second * 60,
        ReadTimeout:  time.Second * 15,
        //IdleTimeout:  time.Second * 120,
        //Handler: router, // Pass our instance of gorilla/mux in.
        Handler: router,
    }

    // Run our server in a goroutine so that it doesn't block.
    go func() {
        log.Println("Running server....")

        if err := srv.ListenAndServe(); err != nil {
            log.Println(err)
        }
    }()

    c := make(chan os.Signal, 1)
    // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
    // SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
    signal.Notify(c, os.Interrupt)

    // Block until we receive our signal.
    <-c

    // Create a deadline to wait for.
    ctx, cancel := context.WithTimeout(context.Background(), wait)
    defer cancel()
    // Doesn't block if no connections, but will otherwise wait
    // until the timeout deadline.
    srv.Shutdown(ctx)
    // Optionally, you could run srv.Shutdown in a goroutine and block on
    // <-ctx.Done() if your application should wait for other services
    // to finalize based on context cancellation.
    fmt.Println("

")
    log.Println("shutting down")
    log.Println("Goddbye!....")
    os.Exit(0)

}

I have been having problems of connections closed on load when tested with ab tool for 500 concurrent connections with 50000 requests on my mac that I solved by sending a header close connection in the GET request and now the test is successful on my mac. However, when I tried to run the test in a windows 10 box I am again receiving messages like this:

2019/03/08 20:47:47 Error en response: Get http://localhost:9596/echo/javier: dial tcp [::1]:9596: bind: An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.

After googling for a while looking for a windows alternative to ulimit in order to change resource limits I have found messages like this:

I don't believe that current Windows O/S have a limit on the total number of file descriptors, but the MS runtime library (msvcrt.dll) has a per process limit of 2048, albeit as far as I know that's not enforced by the O/S.

It can allegedly be increased only by building your own version of the MS runtime library from source.

Then I am curious because I have the same kind of service made with Java and NodeJS and both can run the test successfuly in the same machines where my go app fails because the errors mentioned above.

So now my questions/doubts are:

  • what are Java and NodeJS doing different from go http server to be able to pass my test without reach OS limits?

  • What can I do to get my go services to be successful in any system as Java and NodeJS did?

  • Finally, is there any way to let my go services know the OS limits to prevent it to reach them and fail the tests?

This is issue is driving me crazy and even when it allowed me to learn a lot about http protocol is getting more dark everyday as I could not find a solution and I will love to have my app developed in go rather than in Java because it seems to be far better when runs well.

Thanks in advance J

  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥15 sqlite 附加(attach database)加密数据库时,返回26是什么原因呢?
    • ¥88 找成都本地经验丰富懂小程序开发的技术大咖
    • ¥15 如何处理复杂数据表格的除法运算
    • ¥15 如何用stc8h1k08的片子做485数据透传的功能?(关键词-串口)
    • ¥15 有兄弟姐妹会用word插图功能制作类似citespace的图片吗?
    • ¥200 uniapp长期运行卡死问题解决
    • ¥15 请教:如何用postman调用本地虚拟机区块链接上的合约?
    • ¥15 为什么使用javacv转封装rtsp为rtmp时出现如下问题:[h264 @ 000000004faf7500]no frame?
    • ¥15 乘性高斯噪声在深度学习网络中的应用
    • ¥15 关于docker部署flink集成hadoop的yarn,请教个问题 flink启动yarn-session.sh连不上hadoop,这个整了好几天一直不行,求帮忙看一下怎么解决