在 Go 1.24 中使用 HTTP/2 Cleartext 服务器
摘要
Go 1.24 在 net/http 包中引入了对 HTTP/2 Cleartext (h2c) 的原生支持,消除了之前使用外部包装包的需求。本文介绍了如何配置 Go HTTP 服务器以使用 h2c 与 Google Cloud Run 等服务配合工作。
暂无内容
查看缓存全文
缓存时间: 2026/05/24 21:42
# 配置 Go HTTP 服务器以支持未加密的 HTTP/2 (h2c) | ClarityBoss 博客
来源:https://www.clarityboss.com/blog/go-http2-cleartext-h2c-cloud-run
在我们的应用中,我们使用了长寿命的服务器推送事件流(SSE)。这些连接的超时时间和生命周期都被设置得很长——在我们的配置中为 15 分钟。然而,Google Cloud Run 有一个已知问题(https://docs.cloud.google.com/run/docs/troubleshooting#client-disconnect),即当使用 HTTP/1.1 与后端服务通信时,客户端的断开连接事件不会传播到 Cloud Run。因此,我开始研究为服务使用 HTTP/2(https://docs.cloud.google.com/run/docs/configuring/http2)。
Cloud Run 在前端终止 TLS,但可以以 HTTP/1.1 或未加密的 HTTP/2(`h2c`)流量形式转发流量。通常,HTTP/2 始终使用 TLS,但 HTTP 客户端和服务器通常可以配置为使用该协议的未加密版本。Cloud Run 还要求你选择要使用的协议。这基本上就是 RFC 9113 第 3.3 节中所描述的“具有先验知识的 HTTP/2”设置。
## 为 Go 服务器配置未加密的 HTTP/2
我们对 `h2c` 支持的首次实现早于 Go 1.24 的变更,我将在下面详细介绍这些变更。互联网上的大多数帖子和指南都会指向旧的过时方法,但我在下面已经包含了旧方法,以便更容易看到前后对比,并在需要时迁移你自己的代码。
### Go 1.24 之前(旧方法)
要使用 `h2c`,你必须使用 `golang.org/x/net/http` 包,并进行复杂的设置。
``
import (
"net/http"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
handler := ...
// 现有的 `handler` 必须被包装,附加到服务器,然后进行配置
h2s := &http2.Server{}
handler = h2c.NewHandler(handler, h2s)
srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", "", 9888),
Handler: handler,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 35 * time.Second,
// https://cloud.google.com/load-balancing/docs/https/request-distribution#timeout-bes
IdleTimeout: 620 * time.Second,
}
err = http2.ConfigureServer(srv, h2s)
if err != nil {
...
}
``
### Go 1.24+(新方法)
从 Go 1.24 开始,不再需要 `x/net/http2/h2c` 包装器,设置也更具可读性。你可以直接在 `http.Server` 上配置协议。
参考:Go 1.24 net/http 发布说明(https://go.dev/doc/go1.24#nethttppkgnethttp)。
``
handler := ...
srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", "", 9888),
Handler: handler,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 35 * time.Second,
// https://cloud.google.com/load-balancing/docs/https/request-distribution#timeout-bes
IdleTimeout: 620 * time.Second,
}
srv.Protocols = new(http.Protocols)
srv.Protocols.SetHTTP1(true)
srv.Protocols.SetUnencryptedHTTP2(true)
``
### 在部署前进行本地测试
测试非常简单。如果未正确设置未加密的 HTTP/2,以下命令将失败。
``
curl -i --http2-prior-knowledge http://localhost:9888
``
## Terraform Cloud Run 配置
一旦你的 Go 服务能够使用未加密的 HTTP/2,就需要在 Cloud Run 中进行适当配置。如果你通过 Cloud Run 控制台操作,请遵循 HTTP/2 服务文档(https://docs.cloud.google.com/run/docs/configuring/http2)。我们通过 terraform 进行操作——以下代码片段提供了配置的总体思路。
``
# 许多与本帖无关的设置已省略。
resource "google_cloud_run_v2_service" "api" {
name = "api"
location = var.primary_region
invoker_iam_disabled = true
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
template {
containers {
...
ports {
name = "h2c"
container_port = 9888
}
}
# 默认值为 80;对于空闲的 SSE 连接,我们可以轻松扩展到更高的值。
max_instance_request_concurrency = 200
# 默认值为 300 秒,但对于 SSE,我们希望允许长寿命连接。
timeout = "900s"
}
lifecycle {
ignore_changes = [client, client_version, template[0].revision]
}
}
``
在负载均衡器层面不需要进行任何实质性的更改,因为与 Serverless NEG 通信时使用的是 HTTPS,并且在此过程中似乎可以正常将连接升级到 HTTP/2。此外,无服务器后端的默认超时时间为 60 分钟,而不是某些其他后端的 30 秒。
相似文章
Codex发现一个隐藏的HTTP/2炸弹
Codex发现了一个名为“HTTP/2炸弹”的远程拒绝服务漏洞,该漏洞针对主流Web服务器(包括nginx、Apache、IIS、Envoy、Pingora)中的HPACK压缩,通过将压缩炸弹与流量控制保持相结合,快速耗尽服务器内存。
使用 Go 的 net/http/httptrace 追踪 HTTP 请求
本文介绍了如何使用 Go 的 net/http/httptrace 包通过基于上下文的钩子追踪 HTTP 请求阶段(DNS、连接、TLS 等),并演示了构建 CLI 追踪工具和 RoundTripper 日志记录器。
httpx2 - Pydantic 的分支
Pydantic 复刻了 httpx HTTP 库,创建了 httpx2,以解决维护问题。原始分支 httpxyz 对此表示欢迎,并鼓励社区支持 httpx2。
Go 语言服务器可以实现令人印象深刻的代码导航
Go 语言服务器 (gopls) 为 Go 开发者提供了令人印象深刻的代码导航功能,增强了 IDE 的能力。
就用Go
一篇带有强烈观点的开发者文章倡导使用Go编程语言,强调其简洁的语法、强大的标准库、高效的并发模型以及单二进制部署,作为对过于复杂的现代技术栈的实用替代方案。