【发布时间】:2020-09-27 21:39:33
【问题描述】:
我尝试在本地机器上测量 Go 默认 HTTP 服务器实现的带宽。服务器只是接受任何 HTTP 请求,使用 sync.atomic 递增计数器并发送 200 OK 响应。此外,服务器每秒收集请求数量,将其打印出来并将计数器重置为零:
type hand struct {
cnt int32
}
func (h *hand) ServeHTTP(rsp http.ResponseWriter, req *http.Request) {
atomic.AddInt32(&h.cnt, 1)
rsp.WriteHeader(200)
if req.Body != nil {
req.Body.Close()
}
}
func main() {
h := new(hand)
s := &http.Server{
Addr: ":8080",
Handler: h,
}
ticker := time.NewTicker(1 * time.Second)
go func() {
for tick := range ticker.C {
val := atomic.SwapInt32(&h.cnt, 0)
fmt.Printf("(%v) %d RPS\n", tick, val)
}
}()
log.Fatal(s.ListenAndServe())
}
目标客户端正在尝试同时发送 100000 个GET 请求:
const total = 100000
func main() {
var r int32
var rsp int32
r = total
rsp = r
for r > 0 {
go func() {
p, err := http.Get("http://localhost:8080")
atomic.AddInt32(&rsp, -1)
if err != nil {
fmt.Printf("error: %s\n", err)
return
}
if p.StatusCode != 200 {
fmt.Printf("status %d\n", p.StatusCode)
}
}()
r--
}
for {
x := atomic.LoadInt32(&rsp)
fmt.Printf("sent : %d\n", total-x)
if x == 0 {
return
}
time.Sleep(1 * time.Second)
}
}
我正在使用带有5.3.2-gentoo 内核的Linux 机器。我将nofile 的ulimits(soft 和hard)更改为100000。当我运行这个测试时,所有其他用户应用程序都停止了。
我不希望得到准确的结果,但只需要知道这个阈值的水平,例如X000 或X0000 或X00000。
但是服务器每秒处理的请求数不能超过4000,看起来太低了:
# removed timestamps
0 RPS
0 RPS
0 RPS
3953 RPS
3302 RPS
387 RPS
37 RPS
1712 RPS
如何提高 HTTP 服务器的带宽?还是我的测试方法或本地配置有问题?
【问题讨论】:
-
计算带宽的方式不是最优的。自动收报机不是时间的可靠度量。您应该让计数器值递增并将其值除以经过的时间,以获得每秒的平均滴答数。另一个问题是您的客户端中缺少
p.Body.Close()。不确定它是否会影响性能,但肯定会影响内存管理。 -
开始 10000 个 goroutine 不好。主意。你的电脑有10000核吗?它们将按顺序执行并减慢执行速度。您应该开始少量 goroutine 并增加数量,直到您看不到性能变化为止。
-
尝试使用现有的基准测试工具,例如wrk,这样您只需要担心改进服务器。
-
看在上帝的份上,请使用正确的设置。在服务器和客户端都在开发机器上运行的情况下“测量”服务器实现的性能(它并行执行各种事情,并且很可能会遇到打开文件数量等限制)就像测量你的身体将您的自己的手放在额头上来测量温度。将服务器放到目标平台上,测量它可以可靠地满足您的用例的请求数量。这个数字减去一点是水平缩放的缩放阈值。问题解决了。
-
以上所有和
fmt.Println可能不会在收到报价后立即发生。
标签: performance http go