你正在使用的Go版本是什么( go version
)?
golang:1.10.0 linux
这个问题在最新版本的发布中是否重现?
是的
你正在使用什么操作系统和处理器架构( go env
)?
容器:rhel
kubenet:
你做了什么?
func (sc *serverConn) wroteFrame(res frameWriteResult) {
...
sc.closeStream(st, errHandlerComplete) <=== _code1_
}
...
wr.replyToWriter(res.err) <=== _code2_
...
}
在某些情况下,服务器goroutine将在code1和code2之间切换执行上下文。如果处理程序goroutine在这些情况下切换回执行上下文,这将使writeDataFromHandler()(writeHeaders()也一样?)失败并将responseWriterState设置为脏。
func (sc *serverConn) writeDataFromHandler(stream *stream, data []byte, endStream bool) error {
...
case <-stream.cw:
// If both ch and stream.cw were ready (as might
// happen on the final Write after an http.Handler
// ends), prefer the write result. Otherwise this
// might just be us successfully closing the stream.
// The writeFrameAsync and serve goroutines guarantee
// that the ch send will happen before the stream.cw
// close.
select {
case err = <-ch:
frameWriteDone = true
default:
return errStreamClosed
}
}
...
}
func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
...
if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil {
rws.dirty = true
return 0, err
}
...
}
**如果在负载性能测试中在code1和code2之间添加sc.logf(),这个问题可以很容易地重现。
PS:我认为在writeFrame()中,当我们用错误关闭流时,我们不应该调用"wr.replyToWriter(res.err)",这会导致writeDataFromHandler()退出没有错误。但实际上,这个流/responseWriterState已经处于错误状态。**
你期望看到什么?
负载测试中没有连接重置
你看到了什么?
连接重置和在新连接建立之前流量失败
8条答案
按热度按时间7qhs6swi1#
@bradfitz @tombergan
yi0zb3m42#
获取复制品?
lvjbypge3#
/cc @fraenkel
fkaflof64#
我尝试了使用h2load作为客户端和返回100-16k字节数据的服务器。无论在服务器端开启调试,我都无法引起任何连接重置问题,导致显著的减速。
roqulrg35#
你好Fraenkel,
这个问题在低负载性能测试中很容易重现。如果在code1和code2之间添加sc.logf(),就可以轻松重现这个问题。你需要大量的流,并且在一个连接中传输较少的数据才能重现它。例如,创建一个每秒3000个或更多的流的连接,然后传输并关闭它。
B.R
7dl7o3gd6#
在您的代码1和代码2之间的多个地方,我已经添加了sc.logf()。但是在使用h2load驱动时,我仍然没有遇到任何错误。它显示我实现了7150 req/s的速率。我也尝试了有调试和没有调试来减慢速度,但这也没有导致任何错误。
6tqwzwtp7#
请问7150 req/s是否属于一个连接到Go语言HTTP2服务器的请求?7150响应/s的响应体中是否有一些内容?
i7uq4tfw8#
是的,有超过10k个流被创建。我尝试了较小和较大的有效载荷,但都是恒定大小。
如果你能捕获http2debug,那可能会有助于缩小问题范围,因为我似乎无法在最新的代码库上本地进行操作。