请求转发时http服务器性能不佳

时间:2017-10-05 07:37:45

标签: performance http go server

您好我开发了一个小服务器(目前)只执行任务,只是将请求转发到正在运行的计算机上的本地服务。 因此与nginx几乎相同,就像反向代理一样。

但是我观察到一个非常糟糕的性能,甚至耗尽了服务器的所有资源,并在进一步请求时遇到超时。

我知道这不能像nginx一样高效,但我不认为它应该那么慢。

以下是我用于转发请求的服务器:

  package main

import (
    "github.com/gorilla/mux"
    "net/http"
    "github.com/sirupsen/logrus"
    "bytes"
    "io/ioutil"
)

func main() {
    router := mux.NewRouter()
    router.HandleFunc("/", forwarder).Methods("POST")

    server := http.Server{
        Handler: router,
        Addr:    ":8443",
    }

    logrus.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))
}

var client = &http.Client{}

func forwarder(w http.ResponseWriter, r *http.Request) {
    // read request
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        logrus.Error(err.Error())
        ServerError(w, nil)
        return
    }

    // create forwarding request
    req, err := http.NewRequest("POST", "http://localhost:8000", bytes.NewReader(body))
    if err != nil {
        logrus.Error(err.Error())
        ServerError(w, nil)
        return
    }

    resp, err := client.Do(req)

    if err != nil {
        logrus.Error(err.Error())
        ServerError(w, nil)
        return
    }

    // read response
    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        logrus.Error(err.Error())
        ServerError(w, nil)
        return
    }
    resp.Body.Close()

    // return response
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.WriteHeader(resp.StatusCode)
    w.Write(respBody)
}

从客户端我只测量往返时间。当我每秒发出100个请求时,响应时间会非常快。

它的响应时间约为50毫秒。 10秒后,响应时间为500毫秒。再经过10秒钟,响应时间为8000毫秒,依此类推,直到我得到超时。

当我使用nginx而不是我的服务器时,每秒运行100个请求没有问题。使用nginx,每个请求保持40ms。

一些观察: 使用nginx:lsof -i | grep nginx 打开的连接数不超过2个。

使用我的服务器,连接数增加到500,然后状态SYN_SENT的连接增加,然后requets进入超时。

另一个发现:我测量了这段代码行的延迟:

resp, err := client.Do(req)

大部分时间都花在了哪里,但也可能只是因为常规节目正在挨饿!?

我也尝试过:

  • r.Close = true(或KeepAlive = false)
  • 我修改了服务器端的超时
  • 我修改了我的转发服务器使用的http客户端上的所有内容(keepalive false,request.Close = true)等。

我不知道为什么会有这么糟糕的表现。

我的猜测是因为大量的常规程序会遇到问题。也许大部分时间用于安排这个例程,因此延迟会增加?

我还尝试使用包含的httputil.NewSingleHostReverseProxy()。性能稍微好一点,但仍然是同样的问题。

更新:

现在我尝试了fasthttp:

package main

import (
    "github.com/sirupsen/logrus"
    "github.com/valyala/fasthttp"
)

func StartNodeManager() {
    fasthttp.ListenAndServeTLS(":8443", "cert.pem", "key.pem", forwarder)
}

var client = fasthttp.Client{}

func forwarder(ctx *fasthttp.RequestCtx) {

    resp := fasthttp.AcquireResponse()    

    req := fasthttp.AcquireRequest()
    req.Header.SetMethod("POST")
    req.SetRequestURI("http://127.0.0.1:8000")
    req.SetBody(ctx.Request.Body())


    err := client.Do(req, resp)

    if err != nil {
        logrus.Error(err.Error())
        ctx.Response.SetStatusCode(500)
        return
    }

    ctx.Response.SetBody(resp.Body())
    fasthttp.ReleaseRequest(req)
    fasthttp.ReleaseResponse(resp)
}

好一点但30秒后第一次超时到达,响应时间上升到5秒。

2 个答案:

答案 0 :(得分:0)

首先,您应该分析您的应用并找出瓶颈所在。

其次,我希望能够在堆中编写具有较少内存分配的代码,并在堆栈上编写更多内容。

很少有想法:

  1. 您是否需要所有请求的阅读请求正文?
  2. 您是否需要始终阅读回复正文?
  3. 您可以将客户端请求的主体传递给服务器请求吗? func NewRequest(method, url string, body io.Reader) (*Request, error)
  4. 使用sync.Pool
  5. 考虑使用fasthttp,因为它对垃圾收集器造成的压力较小
  6. 检查您的服务器是否使用与Nginx相同的优化。例如。 Keep-Alive,缓存等
  7. 再次对Nginx进行简介和比较。
  8. 似乎有很多优化空间。

答案 1 :(得分:0)

问题的根本原因是GO http模块未处理到上游的连接 一种人为管理的方式,时间在增加,因为许多连接正在打开,并且它们进入time_wait状态。 因此,随着连接数量的增加,性能将会降低。

您只需要设置

// 1000 what I am using
http.DefaultTransport.(*http.Transport).MaxIdleConns = 1000
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 1000
in your forwarder and this will solve your problem.

顺便说一句,使用go std库反向代理,这将消除很多麻烦。 但是,对于反向代理,您仍然需要在传输过程中设置MaxIdleConns和MaxIdleConnsPerHost。

关注下面的文章。