From bece1b52012333dd6cf3aa8cea24b019563d1233 Mon Sep 17 00:00:00 2001 From: yangjianbo Date: Sat, 24 Jan 2026 20:01:03 +0800 Subject: [PATCH] =?UTF-8?q?perf(=E6=9C=8D=E5=8A=A1=E7=AB=AF):=20=E5=90=AF?= =?UTF-8?q?=E7=94=A8=20h2c=20=E5=B9=B6=E4=BF=9D=E7=95=99=20HTTP/1.1=20?= =?UTF-8?q?=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_CN.md | 23 +++++++++++++++++++++++ backend/cmd/server/main.go | 11 ++++++++++- backend/internal/server/http.go | 5 ++++- deploy/Caddyfile | 1 + 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/README_CN.md b/README_CN.md index 41d399d5..8129c3b2 100644 --- a/README_CN.md +++ b/README_CN.md @@ -358,6 +358,29 @@ Invalid base URL: invalid url scheme: http ./sub2api ``` +#### HTTP/2 (h2c) 与 HTTP/1.1 回退 + +后端明文端口默认支持 h2c,并保留 HTTP/1.1 回退用于 WebSocket 与旧客户端。浏览器通常不支持 h2c,性能收益主要在反向代理或内网链路。 + +**反向代理示例(Caddy):** + +```caddyfile +transport http { + versions h2c h1 +} +``` + +**验证:** + +```bash +# h2c prior knowledge +curl --http2-prior-knowledge -I http://localhost:8080/health +# HTTP/1.1 回退 +curl --http1.1 -I http://localhost:8080/health +# WebSocket 回退验证(需管理员 token) +websocat -H="Sec-WebSocket-Protocol: sub2api-admin, jwt." ws://localhost:8080/api/v1/admin/ops/ws/qps +``` + #### 开发模式 ```bash diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index f8a7d313..65b8c659 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -24,6 +24,8 @@ import ( "github.com/Wei-Shaw/sub2api/internal/web" "github.com/gin-gonic/gin" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" ) //go:embed VERSION @@ -122,7 +124,14 @@ func runSetupServer() { log.Printf("Setup wizard available at http://%s", addr) log.Println("Complete the setup wizard to configure Sub2API") - if err := r.Run(addr); err != nil { + server := &http.Server{ + Addr: addr, + Handler: h2c.NewHandler(r, &http2.Server{}), + ReadHeaderTimeout: 30 * time.Second, + IdleTimeout: 120 * time.Second, + } + + if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatalf("Failed to start setup server: %v", err) } } diff --git a/backend/internal/server/http.go b/backend/internal/server/http.go index 52d5c926..f3e15006 100644 --- a/backend/internal/server/http.go +++ b/backend/internal/server/http.go @@ -14,6 +14,8 @@ import ( "github.com/gin-gonic/gin" "github.com/google/wire" "github.com/redis/go-redis/v9" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" ) // ProviderSet 提供服务器层的依赖 @@ -56,9 +58,10 @@ func ProvideRouter( // ProvideHTTPServer 提供 HTTP 服务器 func ProvideHTTPServer(cfg *config.Config, router *gin.Engine) *http.Server { + handler := h2c.NewHandler(router, &http2.Server{}) return &http.Server{ Addr: cfg.Server.Address(), - Handler: router, + Handler: handler, // ReadHeaderTimeout: 读取请求头的超时时间,防止慢速请求头攻击 ReadHeaderTimeout: time.Duration(cfg.Server.ReadHeaderTimeout) * time.Second, // IdleTimeout: 空闲连接超时时间,释放不活跃的连接资源 diff --git a/deploy/Caddyfile b/deploy/Caddyfile index e5636213..d4144057 100644 --- a/deploy/Caddyfile +++ b/deploy/Caddyfile @@ -87,6 +87,7 @@ example.com { # 连接池优化 transport http { + versions h2c h1 keepalive 120s keepalive_idle_conns 256 read_buffer 16KB