【发布时间】:2015-09-25 10:55:24
【问题描述】:
我正在尝试计算发出并发请求所需的时间。我的计时结果比ab2 报告的慢四倍左右。
我尝试了两种不同的计时请求,这两种方法都导致相似的结果(与 ab2 的结果相差甚远)。举个例子,ab2 将在本地服务器上报告最长的请求持续 2 毫秒,而此代码将报告最多 4.5 毫秒。顺便说一句,整个代码库都可用here。
我如何正确地为 go 中的请求计时?
方法一:计时不仅仅包括请求
来自this commit。
// Let's spawn all the requests, with their respective concurrency.
wg.Add(r.Repeat)
r.doneWg.Add(r.Repeat)
for rno := 1; rno <= r.Repeat; rno++ {
go func(no int, greq goreq.Request) {
r.ongoingReqs <- struct{}{} // Adding sentinel value to limit concurrency.
startTime := time.Now()
greq.Uri = r.URL.Generate()
gresp, err := greq.Do()
if err != nil {
log.Critical("could not send request to #%d %s: %s", no, r.URL, err)
} else if no < r.Repeat {
// We're always using the last response for the next batch of requests.
gresp.Body.Close()
}
<-r.ongoingReqs // We're done, let's make room for the next request.
resp := Response{Response: gresp, duration: time.Now().Sub(startTime)}
// Let's add that request to the list of completed requests.
r.doneChan <- &resp
runtime.Gosched()
}(rno, greq)
}
方法2:使用带有defer语句的内部函数
来自this commit。
// Let's spawn all the requests, with their respective concurrency.
wg.Add(r.Repeat)
r.doneWg.Add(r.Repeat)
for rno := 1; rno <= r.Repeat; rno++ {
go func(no int, greq goreq.Request) {
r.ongoingReqs <- struct{}{} // Adding sentinel value to limit concurrency.
greq.Uri = r.URL.Generate()
var duration time.Duration
gresp, err := func(dur *time.Duration) (gresp *goreq.Response, err error) {
defer func(startTime time.Time) { *dur = time.Now().Sub(startTime) }(time.Now())
return greq.Do()
}(&duration)
if err != nil {
log.Critical("could not send request to #%d %s: %s", no, r.URL, err)
} else if no < r.Repeat {
// We're always using the last response for the next batch of requests.
gresp.Body.Close()
}
<-r.ongoingReqs // We're done, let's make room for the next request.
resp := Response{Response: gresp, duration: duration}
// Let's add that request to the list of completed requests.
r.doneChan <- &resp
runtime.Gosched()
}(rno, greq)
}
我查看了this question,但没有帮助。
【问题讨论】:
-
如果你想创建一个基准,用“goreq”在 http 包上添加另一层似乎适得其反。首先尝试使用标准的 http 包,然后再配置文件(如果还有时间无法计算)。已经用 Go 编写了许多 http 负载生成器,效果很好。
-
服务器是否在响应正文中返回 any 内容?
-
@JimB,是的,服务器可能会返回内容。而这个负载生成器实际上可以将返回的数据用于后续请求。
-
我没有看到您在哪里阅读正文,但如果您不阅读该内容并尽快关闭正文,则该连接将被阻止发出更多请求,需要您创建新的连接。 (这也被额外的 goreq 包所掩盖)
-
您需要阅读完整的回复并尽快关闭正文。在您阅读整个响应之前,该请求尚未完成。我不确定回答第二部分,如果你以后想要身体,你需要把它存放在某个地方。