perf(服务端): 启用 h2c 并保留 HTTP/1.1 回退
This commit is contained in:
23
README_CN.md
23
README_CN.md
@@ -358,6 +358,29 @@ Invalid base URL: invalid url scheme: http
|
|||||||
./sub2api
|
./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.<ADMIN_TOKEN>" ws://localhost:8080/api/v1/admin/ops/ws/qps
|
||||||
|
```
|
||||||
|
|
||||||
#### 开发模式
|
#### 开发模式
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import (
|
|||||||
"github.com/Wei-Shaw/sub2api/internal/web"
|
"github.com/Wei-Shaw/sub2api/internal/web"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/h2c"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed VERSION
|
//go:embed VERSION
|
||||||
@@ -122,7 +124,14 @@ func runSetupServer() {
|
|||||||
log.Printf("Setup wizard available at http://%s", addr)
|
log.Printf("Setup wizard available at http://%s", addr)
|
||||||
log.Println("Complete the setup wizard to configure Sub2API")
|
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)
|
log.Fatalf("Failed to start setup server: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/wire"
|
"github.com/google/wire"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/h2c"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProviderSet 提供服务器层的依赖
|
// ProviderSet 提供服务器层的依赖
|
||||||
@@ -56,9 +58,10 @@ func ProvideRouter(
|
|||||||
|
|
||||||
// ProvideHTTPServer 提供 HTTP 服务器
|
// ProvideHTTPServer 提供 HTTP 服务器
|
||||||
func ProvideHTTPServer(cfg *config.Config, router *gin.Engine) *http.Server {
|
func ProvideHTTPServer(cfg *config.Config, router *gin.Engine) *http.Server {
|
||||||
|
handler := h2c.NewHandler(router, &http2.Server{})
|
||||||
return &http.Server{
|
return &http.Server{
|
||||||
Addr: cfg.Server.Address(),
|
Addr: cfg.Server.Address(),
|
||||||
Handler: router,
|
Handler: handler,
|
||||||
// ReadHeaderTimeout: 读取请求头的超时时间,防止慢速请求头攻击
|
// ReadHeaderTimeout: 读取请求头的超时时间,防止慢速请求头攻击
|
||||||
ReadHeaderTimeout: time.Duration(cfg.Server.ReadHeaderTimeout) * time.Second,
|
ReadHeaderTimeout: time.Duration(cfg.Server.ReadHeaderTimeout) * time.Second,
|
||||||
// IdleTimeout: 空闲连接超时时间,释放不活跃的连接资源
|
// IdleTimeout: 空闲连接超时时间,释放不活跃的连接资源
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ example.com {
|
|||||||
|
|
||||||
# 连接池优化
|
# 连接池优化
|
||||||
transport http {
|
transport http {
|
||||||
|
versions h2c h1
|
||||||
keepalive 120s
|
keepalive 120s
|
||||||
keepalive_idle_conns 256
|
keepalive_idle_conns 256
|
||||||
read_buffer 16KB
|
read_buffer 16KB
|
||||||
|
|||||||
Reference in New Issue
Block a user