你使用的Go版本是什么( go version
)?
go1.10.2 darwin/amd64
这个问题在最新版本的发布中是否重现?
是的
你正在使用什么操作系统和处理器架构( go env
)?
GOARCH="amd64"
GOBIN=""
GOCACHE="..."
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="..."
GORACE=""
GOROOT="/usr/local/Cellar/go/1.10.2/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.10.2/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/_j/6wqhr8r57g34l96w0vt17bww0000gp/T/go-build671837991=/tmp/go-build -gno-record-gcc-switches -fno-common"
你做了什么?
我设置了一个HTTP2客户端和服务器。客户端向服务器发起几个并发请求。客户端的http.Transport有一个代理函数,用于修改请求的上下文。http.Transport.DisableKeepAlives标志也被设置为true。
收到响应后,打印出上下文的内容。
我的代码在playground: https://play.golang.org/p/bx4GvEZ-suw
由于HTTP/2包和所需的证书和密钥,无法在playground中运行。
要在文件所在目录中获取证书+密钥,请运行: openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.pem -days 365 -nodes
。
你期望看到什么?
每个请求的上下文都被修改,如下所示:(如果http.Transport没有配置HTTP2会发生什么)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
context.Background.WithValue("Proxy", 1)
你看到了什么?
几乎所有的请求都没有通过http.Transport.Proxy函数,尽管已经设置了no keepalives标志:
context.Background.WithValue("Proxy", 1)
context.Background
context.Background.WithValue("Proxy", 1)
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
context.Background
在客户端不使用HTTP2似乎可以完全解决这个问题。尝试在服务器上调整10ms的处理时间和启动goroutine之间的10ms等待时间,可以解决这个问题或者使问题变得更糟。然而,这并不是你在生产环境中应该控制的事情。
这可能与: #25620 有关,但是那个问题是在TCP级别上发生的(在那里调用了http.Transport.Proxy,但没有被尊重),而在这里函数根本没有被调用。
7条答案
按热度按时间e5njpo681#
(CC: @bradfitz 和 @tombergan 为 x/net/http2。)
vc6uscn92#
tl;dr net/http在RoundTrip未委托给备用round tripper时执行Proxy。当备用RT来自x/net/http2时,似乎从未调用过Proxy(没有引用到
Proxy
),我认为这需要修复。如果你使用
GODEBUG=http2debug=1
,你看到"http2:没有可用的缓存连接"的次数应该等于你看到期望的"context.Background.WithValue("Proxy", 1)"的次数。当还没有缓存的h2连接匹配时,net/http会在创建连接之前执行connectMethodForRequest
。connectMethodForRequest
是我唯一看到Proxy
被调用的地方——我看不到在x/net/http2中调用Proxy。如果h2连接池确实有一个连接匹配,它将运行自己的RoundTrip。更多详情请查看:幻灯片A,幻灯片B,Video。此外,我认为DisableKeepAlive对使用长寿命连接的http2没有任何影响。
az31mfrm3#
@meirf
此外,我认为DisableKeepAlive对使用长连接的http2没有影响。实际上,这里已经实现了这个功能,尽管连接在空闲时立即关闭:#14008
zkure5ic4#
cc: @odeke-em
thigvfpy5#
这可能很难用当前两个包之间的接口来修复。需要一些思考。最坏的情况是我们至少可以记录下来,但我更愿意去修复它。
92vpleto6#
这个问题仍然存在(go1.20.1版本)。
我们正在使用不同的代理向相同的目标服务器(例如https://api.myip.com/)发送请求。在HTTP/1.1中,这种方式可以正常工作,但在使用HTTP/2时,所有请求都只使用一个代理(因为net/http会重用已经打开到目标服务器的连接,并忽略代理配置之间的差异)。
此外,https://pkg.go.dev/net/http@go1.20.1#Transport 中的“ForceAttemptHTTP2”描述中提到,如果提供了“非零的Dial、DialTLS或DialContext func或TLSClientConfig”,HTTP/2将被“谨慎地禁用”,但是我提供了TLSClientConfig和DialContext,而net/http仍然在使用HTTP/2(我应该为此开一个单独的问题吗?)。
可能的解决方法(禁用HTTP/2):
puruo6ea7#
顺便问一下,有没有其他方法可以在不禁用HTTP/2的情况下解决这个问题?