我有一个Google Kubernetes Engine集群,它在几个带有NodePort
的Pod中,并且全部通过Ingress
公开,这创建了一个HTTP负载平衡器(LB)。我正在使用具有LB的Google托管SSL证书的自定义域。
我的后端是使用其"net/http"
包以Go语言编写的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)
}
对于取消的HTTP 1.1请求,永远不会调用case <-ctx.Done():
。
为了便于测试,我使用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请求。无论客户端的HTTP版本如何,LB与后端进行通信时始终使用相同的HTTP版本。
答案 0 :(得分:1)
从您的描述看来,问题出在GFE和后端之间,因为GFE might hold the connections for reuse。
我的理解是,您看到协议版本之间存在这种差异,因为两者都处理连接持久性的方式。
对于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,指定终止:
HTTP / 1.1服务器可以假定HTTP / 1.1客户端打算维持持久连接,除非在请求中发送了包含连接令牌“ close”的Connection标头。如果服务器选择在发送响应后立即关闭连接,则应发送包含连接令牌关闭的连接标头。
这也许可以解释为什么HTTP1.1遵循same timeout configuration as the LB而HTTP2没有遵循。
无论何时要终止连接,我都建议尝试主动发送终止标头。 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小组发送一些复制步骤。
我希望这会有所帮助。