- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
您好,我开发了一个小型 go 服务器,它(目前)除了将请求转发到它正在运行的机器上的本地服务外什么都不做。所以几乎和 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,每个请求保持在 40 毫秒。
一些观察:使用 nginx:lsof -i | grep nginx打开的连接不超过 2 个。
使用我的服务器,连接数增加到 500,然后状态为 SYN_SENT 的连接增加,然后请求超时。
另一个发现:我测量了这条代码行的延迟:
resp, err := client.Do(req)
大多数时间都花在了那里,但也可能只是因为 go routines 正在挨饿!?
我也尝试过:
我不知道为什么我的表现这么差。
我的猜测是,由于 go 例程的数量巨大,go 会遇到问题。也许大部分时间都花在了调度这个 go routines 上,所以延迟增加了?
我还尝试使用包含的 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 秒。
最佳答案
问题的根本原因是 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 library 反向代理,这将消除很多头痛。但对于反向代理,您仍然需要在其传输中设置 MaxIdleConns 和 MaxIdleConnsPerHost。
按照下面给出的文章进行操作。
关于performance - Go http 服务器在请求转发时性能不佳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46580313/
我是一名优秀的程序员,十分优秀!