我有一个 Google Kubernetes Engine 集群,在几个带有 NodePort
的 pod 内s,并且所有内容都通过 Ingress
公开,它会创建一个 HTTP 负载平衡器 (LB)。我正在为 LB 使用带有 Google 托管 SSL 证书的自定义域。
我的后端是一个用 Go 编写的 HTTP 服务器,使用它的 "net/http"
包裹。它使用带有 LB 的 mTLS 的自签名证书(Google 的 HTTP LB 接受任何 mTLS 证书)。
一切正常,除了一种情况,即客户端创建与 LB 的 HTTP 1.1 连接,然后取消请求。这会取消客户端和 LB 之间的连接,但 LB 会与我的后端保持打开连接,直到服务器超时。
我的用例要求打开请求甚至几个小时,所以我的服务器有很大的超时值。请求中的业务逻辑正确使用了请求的 Context
并考虑请求是否被客户端取消。
如果客户端发出 HTTP2 请求并取消它,即取消到我后端的整个连接,一切都会按预期工作。
这是一个模拟可取消的长时间运行任务的示例 Go 处理程序:
func handleLongRunningTask(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
t := time.Now()
select {
case <-ctx.Done():
log.Println("request canceled")
case <-time.After(30 * time.Second):
log.Println("request finished")
}
log.Printf("here after: %v\n", time.Since(t))
w.WriteHeader(http.StatusOK)
}
case <-ctx.Done():
永远不会为取消的 HTTP 1.1 请求调用。为了方便测试,我使用 curl 和
Ctrl+C
;这有效:curl -v --http2 'https://example.com/long_running_task'
这不会:
curl -v --http1.1 'https://example.com/long_running_task'
NodePort
是否无关紧要是 HTTPS 或 HTTP2,LB 对客户端取消请求的行为完全相同。我尝试用 Go 1.14.4 和 1.13.12 编译服务器,结果是一样的。
这是 Kubernetes、Ingress、Google Kubernetes Engine、Google 的 HTTP 负载均衡器、Go 的 HTTP 服务器中的错误吗?还是我缺少 HTTP 1.1 的东西?有什么问题,我该如何解决?
...不可能知道后端的 HTTP 版本,所以我可以拒绝所有 HTTP 1.1 请求。 LB 在与其后端通信时始终使用相同的 HTTP 版本,无论客户端的 HTTP 版本如何。
最佳答案
从您的描述看来,问题可能出在 GFE 之间。和后端,因为 GFE might hold the connections for reuse .
我的看法是,您会看到协议(protocol)版本之间的这种差异,因为两者都是如何处理连接持久性的。
对于 HTTP2,连接将打开 until one of the parties send a termination signal and the earliest takes preference .但是对于 HTTP1.1,it might be prolonged until an explicit connection header is sent ,指定终止:
An HTTP/1.1 server MAY assume that a HTTP/1.1 client intends to maintain a persistent connection unless a Connection header including the connection-token "close" was sent in the request. If the server chooses to close the connection immediately after sending the response, it SHOULD send a Connection header including the connection-token close.
这或许可以解释为什么 HTTP1.1 遵循 same timeout configuration as the LB而 HTTP2 没有。
我建议您在想要终止连接时尝试主动发送终止 header 。一个 example taken from Github :
func (m *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("HTTP request from %s", r.RemoteAddr)
// Add this header to force to close the connection after serving the request.
w.Header().Add("Connection", "close")
fmt.Fprintf(w, "%s", m.hostname)
}
此外,似乎有一些成功案例将您的集群切换为 VPC Native。 ,因为它排除了 kube-proxy 连接管理的等式。
最后,您可能处于一个非常特殊的情况,值得单独评估。您可能想尝试使用 Issue Tracker 将一些复制步骤发送给 GKE 团队。 .
我希望这有帮助。
关于Google Cloud HTTP(S) 负载平衡器不会取消与后端的连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62178698/