我正在尝试计算并发请求所需的时间。我的时间结果比ab2报告的结果慢了四倍。
我已经尝试过两种不同的计时请求,这两种方式都会产生类似的结果(远远超过ab2的结果)。举一个例子,ab2将在本地服务器上报告最长2毫秒的请求,而此代码将报告最多4.5毫秒。顺便说一下,整个代码库都可用here。
如何正确计时请求?
来自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)
}
来自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,这没有帮助。
答案 0 :(得分:1)
当你的goroutine进入系统调用(写入套接字)时,它们会被抢占。这意味着他们被打断了,另一个goroutine会在他们的位置上运行。最终,您的抢先goroutine将再次安排,它将继续在它停止的地方运行。但是,这并不一定就在系统调用完成后发生。
goroutine中的时间很难,因为即使你按顺序执行所有操作,Go 1.5的垃圾收集器也会偶尔运行,从而中断理论上的串行循环。
唯一真正的解决方案有点复杂:
//go:nosplit
注释您的功能,以防止它被抢占。即便如此,我可能会忘记一些事情。