0

我有一个 Google Kubernetes Engine 集群,在几个带有NodePorts 的 pod 中,所有的都通过 暴露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 版本如何。

4

1 回答 1

1

从您的描述看来,问题可能出在GFE和后端之间,因为GFE 可能会保留连接以供重用

我的看法是,您会看到协议版本之间的这种差异,因为两者都是如何处理连接持久性的。

对于 HTTP2,连接将打开,直到其中一方发送终止信号并且最早的一方获得优先权。但是对于 HTTP1.1,它可能会被延长,直到一个明确的连接头被发送,指定终止:

HTTP/1.1 服务器可以假设 HTTP/1.1 客户端打算保持持久连接,除非在请求中发送了包含连接令牌“close”的 Connection 标头。如果服务器选择在发送响应后立即关闭连接,它应该发送一个包含连接令牌关闭的连接头。

这可以解释为什么 HTTP1.1 遵循与 LB 相同的超时配置而 HTTP2 没有。

我建议您在想要终止连接时尝试主动发送终止标头。取自 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 连接管理的等式。

最后,您可能处于一个非常特殊的情况,值得单独评估。您可能想尝试使用问题跟踪器向 GKE 团队发送一些复制步骤。

我希望这有帮助。

于 2020-06-04T16:26:40.847 回答