Как рассчитать размер ресурсов, чтобы http-сервер golang не достиг предела ресурсов ОС - PullRequest
0 голосов
/ 09 марта 2019

У меня есть простое приложение отдыха golang, которое отвечает на эхо-запрос, как это:

// 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("\n\n")
    log.Println("shutting down")
    log.Println("Goddbye!....")
    os.Exit(0)


}

Эта программа вызывается другой службой через запрос GET:

// 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("\n\n")
    log.Println("shutting down")
    log.Println("Goddbye!....")
    os.Exit(0)

}

У меня были проблемы с закрытыми соединениями под нагрузкой при тестировании с помощью инструмента ab для 500 одновременных подключений с 50000 запросами на моем Mac, которые я решил, отправив соединение с закрытием заголовка в запросе GET, и теперь проверка на моем компьютере прошла успешно , Однако, когда я попытался запустить тест в окне Windows 10, я снова получаю сообщения, подобные этому:

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.

После поисков в течение некоторого времени в поисках окон, альтернативных ulimit, чтобы изменить ограничения ресурсов, я нашел сообщения, подобные этому:

Я не верю, что текущие операционные системы Windows имеют ограничение на общее количество количество файловых дескрипторов, но библиотека времени выполнения MS (msvcrt.dll) имеет ограничение на процесс 2048, хотя, насколько я знаю, это не применяется О / С.

Якобы его можно увеличить, только создав собственную версию Библиотека времени выполнения MS из источника.

Тогда мне любопытно, потому что у меня один и тот же сервис, созданный с помощью Java и NodeJS, и оба могут успешно выполнить тест на тех же компьютерах, где мое приложение go не работает из-за упомянутых выше ошибок.

Так что теперь мои вопросы / сомнения:

  • Чем Java и NodeJS отличаются от go http-сервера, чтобы пройти мой тест без ограничений операционной системы?

  • Что я могу сделать, чтобы мои сервисы go были успешными в любой системе, как это делали Java и NodeJS?

  • Наконец, есть ли способ сообщить моим службам go предельные значения ОС, чтобы они не достигли их и не прошли тесты?

Эта проблема сводит меня с ума, и даже когда она позволяет мне многое узнать о протоколе http, становится все темнее с каждым днем, так как я не могу найти решение, и я буду рад, если мое приложение будет разрабатываться на ходу, а не на Java. потому что, кажется, намного лучше, когда работает хорошо.

Заранее спасибо J * * тысяча тридцать восемь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...