请在提交问题之前回答以下问题。谢谢!
您正在使用的Go版本是什么(go version
)?
go version go1.10.2 darwin/amd64
最新版本的发行版是否会重现此问题?
是的
您正在使用什么操作系统和处理器架构(go env
)?
Container Linux by CoreOS稳定版(1745.5.0)
您做了什么?
附加了NginX客户端,该客户端复制了此问题。它需要对测试HTTP2服务器进行一些最小的重新配置。客户端<->NginX跳转是HTTP2,而NginX<->Service跳转是HTTP1.1。这是一个典型的配置。我已使用h2c实用程序验证NginX是否公开了一个带有HTTP2虚拟服务器端点的HTTP2客户端。
任何用于NginX HTTP2反向代理的HTTP2客户端都应复制此行为。
NginX默认将http2_max_concurrent_streams设置为128,因此它应该接受在此测试中进行的3个并发流。
我还包含了一个等效的HTTP2客户端,用于http2.golang.org。httptrace报告此客户端每个请求执行一次TLS握手;然而,所有三个请求都使用相同的连接。
因此,由于httptrace无法通过某种原因可见,客户端将多个请求同时发送到Go HTTP2服务器;但是,它没有将请求同时发送到NginX HTTP2反向代理。
您期望看到什么?
我期望看到go http客户端在单个HTTP2连接上同时处理这些并发的HTTP2请求到NginX HTTP2反向代理。
我期望只进行一次TLS握手。
您看到了什么?
相反,除了在单个NginX反向代理连接上同时处理这些请求之外,客户端还在每个请求上发送单独的连接。
httptrace报告的http2.golang.org的重复TLS握手对于所有使用相同连接的请求来说是没有意义的。
9条答案
按热度按时间uinbv5nw1#
如果你立即在一个新程序中并行启动3个HTTP请求,没有打开任何缓存连接,net/http不知道你的对等方是否支持HTTP/2,所以它会执行一个新的TCP连接和TLS握手,直到它发现"h2" ALPN响应。
如果这就是全部内容,听起来像是按预期工作的。
你可能对#6785 / #13957(有点重复)感兴趣,以及https://go-review.googlesource.com/c/go/+/71272,它为每个主机添加了连接数限制,包括处于拨号状态的连接。但这两者都不是专门针对发现主机是否支持HTTP/2的目的来限制每个主机的初始TLS拨号。
默认情况下这样做的问题是,在结果表明对等方不支持HTTP/2的情况下,你会牺牲延迟。现在一些请求不得不在所有关联的TCP+TLS设置往返之后进行TCP连接。
c3frrgcw2#
你好,Brad,
感谢你的快速回复。正如我在我的问题中所提到的,立即执行三个并发请求似乎与http2.golang.org服务器的工作方式相符。它确实在单个连接上复用请求。客户端只有在指向NginX HTTP2服务器时才会失败。早些时候,我认为你的观点可能是问题所在,所以我首先尝试发送一个同步请求以“预热”连接缓存;然后再跟随这三个并发请求。这并没有改变NginX情况下的结果。缓存的连接被用于其中一个请求,而其他两个则创建了新的连接。正确的HTTP2语义是让一个主机的并发请求等待其对HTTP2的支持确定。然后要么为HTTP2复用它们,要么创建HTTP1.1连接。由于这正是客户端在http2.golang.org上所做的,因此似乎逻辑已经存在于客户端中来处理这个问题。
似乎在go http客户端和NginX HTTP2反向代理之间发生了一些HTTP2协议问题。此外,正如我在我的问题中所提到的,http2.golang.org的情况似乎在进行多个TLS握手时只进行了一个。实际上这样做可能是一个TLS协议错误,因此这似乎可能是一个httptrace问题,而不是客户端问题。
关于延迟与HTTP2正确操作之间的权衡,如果非HTTP2情况下的延迟是一个问题,那么需要提供两种选择。正确的HTTP2操作应该是默认的。让HTTP2客户端无法正确复用并发请求是不可接受的。底线是,这里存在一个或多个HTTP2协议问题需要解决。go http客户端的文档需要非常清楚地说明何时以及何时不复用主机的并发请求。如果流行的HTTP2服务器与go HTTP2客户端的工作方式有所不同,这一点需要得到体现......
-- Mark
On Wed, Jun 6, 2018 at 4:19 PM, Brad Fitzpatrick ***@***.***> wrote: If you immediately start 3 HTTP requests in parallel with no cached connections open in a fresh program, net/http doesn't know that your peer supports HTTP/2, so it does a new TCP connection & TLS handshake until it discovers the "h2" ALPN response. If that's all this is about, it sounds like working as intended. You might be interested in #6785 < #6785 > / #13957 < #13957 > (kinda dups), though, and https://go-review.googlesource.com/c/go/+/71272 which adds a limit on the number of connections per host, which includes those in dialing state. But neither of those is specifically about limiting per-host initial TLS dials for the purpose of discovering whether a host supports HTTP/2. The problem with doing that by default is that you sacrifice latency in the case where it turns out the peer did NOT support HTTP/2. Now some of the requests have to then do the TCP connection after all with the associated TCP+TLS setup round trips. — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub <#25761 (comment)>, or mute the thread < https://github.com/notifications/unsubscribe-auth/AAySh_gdOIOhdWm79IHIAIXte27iNeBJks5t6GOMgaJpZM4UdMKA > .
ao218c7q3#
立即执行三个并发请求似乎在http2.golang.org服务器上运行正常,正如我在我的issue中提到的。我认为你可能只是运气好。
底线是,这里存在一个或多个HTTP2协议问题需要解决。
你指的是规范的哪个部分?我不记得有关于这个的内容。
关于延迟与HTTP2正确操作之间的权衡,如果非HTTP2情况下的延迟是一个问题,那么提供两种选择是必要的。正确的HTTP2操作应该是默认的。
也许吧。但我认为很多人可能对他们的偏好有不同意见。总的来说,Go风格尽量避免使用旋钮,但这可能是我们需要一个旋钮的情况。我不确定默认值会是什么。
让HTTP2客户端无法正确地复用并发请求是不可接受的。
好奇:为什么?我们不会向net/http用户公开HTTP/1连接管理细节。当底层协议发生变化时,为什么这个优化细节突然对net/http用户变得如此重要?net/http API保持不变。
carvr3hs4#
正在使用HTTP2进行高级REST服务的应用程序依赖于正确地复用请求。复用不是可选的“优化”。我正在设计这样的协议。如果并发请求在许多连接上分散,将严重影响性能。这相当于在一个环境中让goroutine成为轻量级上下文,而在另一个环境中让它们成为重量级上下文。它会杀死为轻量级情况设计的软件。我在阐述我的观点吗?>)...
On Wed, Jun 6, 2018 at 10:38 PM, Brad Fitzpatrick ***@***.***> wrote: 立即执行三个并发请求似乎与http2.golang.org服务器的工作方式相符,正如我在我的issue中提到的那样。我认为你可能只是碰巧赶上了时机。底线是,这里有一个或多个HTTP2协议问题需要解决。你指的是规范的哪个部分?我不记得有关于这个的内容。关于延迟与HTTP2正确操作之间的权衡,如果非HTTP2情况下的延迟是一个问题,那么提供两种选择是必要的。正确的HTTP2操作应该是默认的。也许吧。但我认为许多人可能不同意他们的偏好。总的来说,Go风格尽量避免使用旋钮,但这可能是一个我们需要一个旋钮的情况。我不确定默认值会是什么。让HTTP2客户端无法正确复用并发请求是不可接受的。好奇:为什么?我们不会向net/http用户公开HTTP/1连接管理细节。当底层协议发生变化时,为什么这个优化细节突然对net/http用户变得如此重要?net/http API保持不变。——你收到这封邮件是因为你创建了这个线程。直接回复此邮件,查看GitHub上的<#25761 (comment)>,或者静音此线程 < https://github.com/notifications/unsubscribe-auth/AAySh8yWbobHBmUebyJck0e1Z7q-8mwqks5t6LxSgaJpZM4UdMKA > 。
gc0ot86w5#
实际上,就规范和net/http而言,这是一个优化。你没有引用规范中说明它是强制性的部分(我认为不是)。
如果你的特定应用程序依赖于这样的属性,你需要自己安排去做。如果需要,你可以直接使用golang.org/x/net/http2包。
d4so4syb6#
你好,布拉德。我很清楚,应对这类问题的第一步骤是反弹回去。在这种情况下,我不打算接受这个。说真的,我觉得这对go社区很重要,需要考虑。如果你不打算进一步追究,请告诉我需要升级的人/团体。背景提议认为,由于HTTP2规范不要求客户端流复用,因此http客户端可以忽略它或提供一个次要的实现,这是技术上正确的——但错误的。没有规范要求http客户端提供连接缓存;然而,大多数人会认为不提供连接缓存的客户端是有问题。事实是这样的:- 客户端流复用是HTTP2的一个独特特性
就像go社区期望http客户端提供高效的连接缓存一样,它也期望它能有效地实现客户端流复用。目前,http客户端文档中没有任何关于如何将HTTP2客户端流发送到主机进行复用的信息——这需要修复。HTTP2效率是所有平台的核心要求——我希望go在这方面表现出色,我知道go团队也是这样想的。告诉用户期望客户端流复用,并使用golang.org/x/net/http2编写自己的客户端,就像告诉他们自己编写连接缓存客户端一样。这低于go社区的期望。go团队需要认真对待这个问题。因为客户端流复用在某些情况下似乎确实有效,解决这个问题可能很简单,只需正确记录如何使用其现有功能即可。— Mark...
2018年6月7日星期四上午8:04,布拉德·菲茨帕特里克(Brad Fitzpatrick)***@***.***>写道:实际上,从规范和net/http的Angular 来看,这是一个优化。你没有引用规范中说它是强制性的(我认为不是)。如果你的特定应用程序依赖于这样一个属性,你需要自己安排做这件事。如果需要,你可以直接使用golang.org/x/net/http2包。—你收到这封邮件是因为你创建了这个主题。直接回复此电子邮件,查看GitHub上的<#25761 (comment)>或者静音线程 < https://github.com/notifications/unsubscribe-auth/AAySh4hzb3XexK5_3_-1ptc5AUBxFbkRks5t6UDjgaJpZM4UdMKA > 。
bvn4nwqk7#
你好,Brad,你对我之前的邮件有什么回应吗?
-- Mark
2018年6月7日,星期四,下午5:45,Mark Hapner ***@***.***>写道:
你好,Brad,我很清楚在处理类似这样的问题时,第一步是反弹回去。在这种情况下,我不打算接受这个说法。说真的,我觉得这对go社区很重要,需要考虑。如果你不打算进一步追究,那就告诉我需要将这个问题升级到哪个人/团体。
背景:
提议认为,由于HTTP2规范不要求客户端流复用,因此http客户端可以忽略它或提供一个次要的实现方式,从技术上讲这是正确的——但错误。没有规范要求http客户端提供连接缓存;然而,大多数人会认为不提供连接缓存的客户端是有问题。事实是这样的:
就像go社区期望http客户端提供高效的连接缓存一样,它也期望它能有效地实现客户端流复用。目前,http客户端文档中没有任何关于如何将HTTP2客户端流发送到主机进行复用的信息——这需要解决。HTTP2效率是所有平台的核心要求——我希望go在这方面表现出色,我知道go团队也是这样想的。告诉用户期望客户端流复用并使用golang.org/x/net/http2编写自己的客户端就像是告诉他们自己编写连接缓存的客户端一样。这低于go社区的期望。go团队需要认真对待这个问题。因为客户端流复用在某些情况下似乎确实有效,解决这个问题可能很简单,只需正确地记录如何使用其现有功能即可。
-- Mark
2018年6月7日,星期四,上午8:04,Brad Fitzpatrick @***. >写道:
实际上,就规范和net/http而言,这是一个优化。你没有引用规范中哪里说它是强制性的(我不认为是这样)。
如果你的特殊应用程序依赖于这样一个属性,你需要自己安排这样做。如果需要的话,你可以直接使用golang.org/x/net/http2包。
-- 你收到这封邮件是因为你是发帖者。
直接回复此电子邮件,查看GitHub上的#25761(评论),或者静音线程< https://github.com/notifications/unsubscribe-auth/AAySh4hzb3XexK5_3_-1ptc5AUBxFbkRks5t6UDjgaJpZM4UdMKA >。
z9gpfhce8#
我们会以某种方式来控制这个,但不是现在。Go 1.11目前处于冻结状态( https://github.com/golang/go/wiki/Go-Release-Cycle ),所以当前的优先事项是修复Go 1.11的错误。
z0qdvdin9#
好的,太好了。只要它能进入流程,我就很满意。Go团队从头到尾都做得非常出色......
2018年6月12日星期二,上午9:04,Brad Fitzpatrick ***@***.***>写道:我们会以某种方式控制这个问题,但现在不是时候。Go 1.11目前处于冻结状态(https://github.com/golang/go/wiki/Go-Release-Cycle),因此当前的优先事项是修复Go 1.11的bug。——您收到此邮件是因为您创建了这个线程。直接回复此邮件,在GitHub上查看<#25761 (comment)>,或者静音此线程< https://github.com/notifications/unsubscribe-auth/AAySh45YcoQeVl0hJsKZr_GDJ5CDcS7eks5t7-aXgaJpZM4UdMKA >。