Google Cloud HTTP(S) 负载平衡器不会取消与后端的连接

标签 go kubernetes google-kubernetes-engine http-1.1 google-cloud-load-balancer

我有一个 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/

相关文章:

go - 反射(reflect)接口(interface)列表

go - 当我编译一个程序并对其进行 cat 时,为什么我看不到 0's and 1' s

amazon-web-services - AWS EC2 实例中的 Kubernetes 仪表板?

spring-boot - 如何配置 Redis Kubernetes 部署,以便在 master 宕机时让 Slave redis pod 接管?

kubernetes - 如何将二进制文件注入(inject) v1.7 pod

linux - Kubernetes 无法在 Google Container Engine 上挂载 NFS FS

kubernetes - Google Kubernetes Engine - 如何使用标准网络层将集群部署到区域数据中心?

mysql - 我们如何使用 go 例程同时运行查询?

Golang测试报错子包版本

docker - 如何将 Container VM 上的 docker 容器与 list 链接?