docs(plan): 添加 Phase 9 规划 - HTTP/3 与性能优化扩展
- 规划 HTTP/3 (QUIC) 支持,使用 quic-go 库 - 规划一致性哈希负载均衡算法 - 规划 gzip_static 预压缩支持 - 规划滑动窗口限流算法 - 更新 update-prompts.md 添加新分析任务 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a1aa05406c
commit
5e19d1a5ee
623
docs/plan.md
623
docs/plan.md
@ -1850,18 +1850,615 @@ Phase 8:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 第九阶段:HTTP/3 支持与性能优化扩展
|
||||||
|
|
||||||
|
### 目标
|
||||||
|
|
||||||
|
实现 HTTP/3 (QUIC) 协议支持,扩展负载均衡算法和限流机制,提升项目协议完整性和性能优化能力。
|
||||||
|
|
||||||
|
### 背景分析
|
||||||
|
|
||||||
|
通过与 nginx 功能对比分析,发现以下功能缺失:
|
||||||
|
|
||||||
|
| 功能 | 当前状态 | 优先级 | 说明 |
|
||||||
|
|------|----------|--------|------|
|
||||||
|
| HTTP/3 (QUIC) | ❌ 未实现 | P0 | 新一代 HTTP 协议,性能提升 20-30% |
|
||||||
|
| 一致性哈希负载均衡 | ❌ 未实现 | P1 | 缓存代理场景关键功能 |
|
||||||
|
| gzip_static 预压缩 | ❌ 未实现 | P2 | 静态资源优化,减少 CPU 开销 |
|
||||||
|
| 滑动窗口限流 | ❌ 未实现 | P2 | 更精确的限流算法 |
|
||||||
|
|
||||||
|
### 技术选型
|
||||||
|
|
||||||
|
**HTTP/3 库**:使用 [quic-go](https://github.com/quic-go/quic-go)。
|
||||||
|
|
||||||
|
**选择理由**:
|
||||||
|
- **官方支持**:Go 生态系统中最成熟的 QUIC 实现
|
||||||
|
- **HTTP/3 完整实现**:支持 http3.Server,与标准库接口兼容
|
||||||
|
- **活跃维护**:定期更新,跟进 IETF QUIC 规范
|
||||||
|
- **性能优秀**:0-RTT、连接迁移等特性支持完善
|
||||||
|
|
||||||
|
**版本要求**:`github.com/quic-go/quic-go v0.48.2`(支持 Go 1.21+)
|
||||||
|
|
||||||
|
**关键挑战**:
|
||||||
|
|
||||||
|
| 挑战 | 说明 | 解决方案 |
|
||||||
|
|------|------|----------|
|
||||||
|
| 接口不兼容 | fasthttp 与 quic-go 接口不同 | 编写适配层转换请求格式 |
|
||||||
|
| Handler 复用 | 需让 HTTP/3 使用现有 handler 链 | adapter.go 将 http.Handler 转换为 fasthttp.RequestCtx |
|
||||||
|
| TLS 配置共享 | QUIC 内置 TLS,需复用现有证书 | 从 ssl 模块获取 tls.Config |
|
||||||
|
|
||||||
|
### 任务列表
|
||||||
|
|
||||||
|
#### 9.1 HTTP/3 服务器核心 (P0)
|
||||||
|
|
||||||
|
**实现**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/http3/server.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
|
"github.com/quic-go/quic-go/http3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Server HTTP/3 服务器。
|
||||||
|
type Server struct {
|
||||||
|
config *config.HTTP3Config
|
||||||
|
http3Server *http3.Server
|
||||||
|
handler fasthttp.RequestHandler
|
||||||
|
adapter *Adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start 启动 HTTP/3 服务器。
|
||||||
|
func (s *Server) Start() error {
|
||||||
|
// 1. 创建 UDP 监听器
|
||||||
|
listener, err := quic.ListenAddrEarly(s.config.Listen, s.tlsConfig, s.quicConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to listen QUIC: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 创建 HTTP/3 服务器
|
||||||
|
s.http3Server = &http3.Server{
|
||||||
|
Handler: s.adapter.Wrap(s.handler),
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.http3Server.Serve(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop 停止 HTTP/3 服务器。
|
||||||
|
func (s *Server) Stop() error {
|
||||||
|
if s.http3Server != nil {
|
||||||
|
return s.http3Server.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**实现要点**:
|
||||||
|
|
||||||
|
- 使用 `quic.ListenAddrEarly` 支持 0-RTT
|
||||||
|
- 复用 `internal/ssl` 模块的 TLS 配置
|
||||||
|
- 配置 `Alt-Svc` 响应头告知客户端可用 HTTP/3
|
||||||
|
- 支持优雅关闭
|
||||||
|
|
||||||
|
#### 9.2 HTTP/3 请求适配层 (P0)
|
||||||
|
|
||||||
|
**实现**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/http3/adapter.go
|
||||||
|
|
||||||
|
// Adapter 将 fasthttp.RequestHandler 适配为 http.Handler。
|
||||||
|
type Adapter struct{}
|
||||||
|
|
||||||
|
// Wrap 包装 fasthttp handler 为 http.Handler。
|
||||||
|
func (a *Adapter) Wrap(handler fasthttp.RequestHandler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// 1. 创建 fasthttp.RequestCtx
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
|
||||||
|
// 2. 转换请求
|
||||||
|
a.convertRequest(r, ctx)
|
||||||
|
|
||||||
|
// 3. 调用 fasthttp handler
|
||||||
|
handler(ctx)
|
||||||
|
|
||||||
|
// 4. 转换响应
|
||||||
|
a.convertResponse(ctx, w)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertRequest 将 net/http.Request 转换为 fasthttp.RequestCtx。
|
||||||
|
func (a *Adapter) convertRequest(r *http.Request, ctx *fasthttp.RequestCtx) {
|
||||||
|
// 方法
|
||||||
|
ctx.Request.Header.SetMethod(r.Method)
|
||||||
|
|
||||||
|
// URI
|
||||||
|
ctx.Request.SetRequestURI(r.URL.String())
|
||||||
|
|
||||||
|
// 头部
|
||||||
|
for k, v := range r.Header {
|
||||||
|
for _, vv := range v {
|
||||||
|
ctx.Request.Header.Add(k, vv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求体
|
||||||
|
if r.Body != nil {
|
||||||
|
body, _ := io.ReadAll(r.Body)
|
||||||
|
ctx.Request.SetBody(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 远程地址
|
||||||
|
ctx.SetRemoteAddr(r.RemoteAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertResponse 将 fasthttp.RequestCtx 响应写入 http.ResponseWriter。
|
||||||
|
func (a *Adapter) convertResponse(ctx *fasthttp.RequestCtx, w http.ResponseWriter) {
|
||||||
|
// 状态码
|
||||||
|
w.WriteHeader(ctx.Response.StatusCode())
|
||||||
|
|
||||||
|
// 头部
|
||||||
|
ctx.Response.Header.VisitAll(func(k, v []byte) {
|
||||||
|
w.Header().Add(string(k), string(v))
|
||||||
|
})
|
||||||
|
|
||||||
|
// 响应体
|
||||||
|
w.Write(ctx.Response.Body())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**性能优化**:
|
||||||
|
|
||||||
|
- 使用 sync.Pool 复用 RequestCtx 对象
|
||||||
|
- 避免不必要的内存分配
|
||||||
|
- 流式处理大请求体
|
||||||
|
|
||||||
|
#### 9.3 一致性哈希负载均衡 (P1)
|
||||||
|
|
||||||
|
**实现**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/loadbalance/consistent_hash.go
|
||||||
|
|
||||||
|
// ConsistentHash 一致性哈希负载均衡器。
|
||||||
|
type ConsistentHash struct {
|
||||||
|
// virtualNodes 虚拟节点数,默认 150
|
||||||
|
virtualNodes int
|
||||||
|
|
||||||
|
// circle 哈希环,key 为哈希值,value 为目标
|
||||||
|
circle map[uint64]*Target
|
||||||
|
|
||||||
|
// sortedHashes 排序后的哈希值列表,用于二分查找
|
||||||
|
sortedHashes []uint64
|
||||||
|
|
||||||
|
// hashKey 哈希键来源
|
||||||
|
hashKey string // "ip", "uri", "header:xxx"
|
||||||
|
|
||||||
|
// mu 读写锁
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select 根据哈希键选择目标。
|
||||||
|
func (c *ConsistentHash) Select(targets []*Target) *Target {
|
||||||
|
return c.SelectByKey(targets, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectByKey 根据指定键选择目标。
|
||||||
|
func (c *ConsistentHash) SelectByKey(targets []*Target, key string) *Target {
|
||||||
|
c.mu.RLock()
|
||||||
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
|
// 1. 计算键的哈希值
|
||||||
|
hash := c.hashKey(key)
|
||||||
|
|
||||||
|
// 2. 二分查找最近的节点
|
||||||
|
idx := sort.Search(len(c.sortedHashes), func(i int) bool {
|
||||||
|
return c.sortedHashes[i] >= hash
|
||||||
|
})
|
||||||
|
|
||||||
|
// 3. 环形回绕
|
||||||
|
if idx >= len(c.sortedHashes) {
|
||||||
|
idx = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.circle[c.sortedHashes[idx]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTarget 添加目标到哈希环。
|
||||||
|
func (c *ConsistentHash) AddTarget(target *Target) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
for i := 0; i < c.virtualNodes; i++ {
|
||||||
|
key := fmt.Sprintf("%s#%d", target.URL, i)
|
||||||
|
hash := c.hashKey(key)
|
||||||
|
c.circle[hash] = target
|
||||||
|
c.sortedHashes = append(c.sortedHashes, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(c.sortedHashes, func(i, j int) bool {
|
||||||
|
return c.sortedHashes[i] < c.sortedHashes[j]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashKey 计算哈希值(使用 FNV-64a)。
|
||||||
|
func (c *ConsistentHash) hashKey(key string) uint64 {
|
||||||
|
h := fnv.New64a()
|
||||||
|
h.Write([]byte(key))
|
||||||
|
return h.Sum64()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**配置示例**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
proxy:
|
||||||
|
- path: /api
|
||||||
|
targets:
|
||||||
|
- url: http://backend1:8080
|
||||||
|
- url: http://backend2:8080
|
||||||
|
- url: http://backend3:8080
|
||||||
|
load_balance: consistent_hash
|
||||||
|
hash_key: ip # ip / uri / header:X-User-ID
|
||||||
|
virtual_nodes: 150
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9.4 gzip_static 预压缩支持 (P2)
|
||||||
|
|
||||||
|
**实现**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/middleware/compression/gzip_static.go
|
||||||
|
|
||||||
|
// GzipStatic 预压缩文件支持。
|
||||||
|
type GzipStatic struct {
|
||||||
|
// enabled 是否启用
|
||||||
|
enabled bool
|
||||||
|
|
||||||
|
// root 静态文件根目录
|
||||||
|
root string
|
||||||
|
|
||||||
|
// extensions 支持的扩展名
|
||||||
|
extensions []string // 默认 [".html", ".css", ".js", ".json", ".xml"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeFile 发送预压缩文件(如果存在)。
|
||||||
|
func (g *GzipStatic) ServeFile(ctx *fasthttp.RequestCtx, filePath string) bool {
|
||||||
|
if !g.enabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 检查客户端是否支持 gzip
|
||||||
|
if !bytes.Contains(ctx.Request.Header.Peek("Accept-Encoding"), []byte("gzip")) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查扩展名
|
||||||
|
if !g.matchExtension(filePath) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查预压缩文件是否存在
|
||||||
|
gzPath := filePath + ".gz"
|
||||||
|
if _, err := os.Stat(filepath.Join(g.root, gzPath)); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 发送预压缩文件
|
||||||
|
ctx.Response.Header.Set("Content-Encoding", "gzip")
|
||||||
|
ctx.Response.Header.Set("Vary", "Accept-Encoding")
|
||||||
|
fasthttp.ServeFile(ctx, filepath.Join(g.root, gzPath))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**配置示例**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
compression:
|
||||||
|
gzip_static: on # on / off
|
||||||
|
gzip_static_extensions: [".html", ".css", ".js", ".json", ".xml", ".svg"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**与现有压缩中间件集成**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// compression.go 中添加预压缩检查
|
||||||
|
func (m *CompressionMiddleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||||
|
return func(ctx *fasthttp.RequestCtx) {
|
||||||
|
// 1. 尝试发送预压缩文件
|
||||||
|
if m.gzipStatic.ServeFile(ctx, string(ctx.Path())) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 回退到实时压缩
|
||||||
|
// ... 现有逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9.5 滑动窗口限流算法 (P2)
|
||||||
|
|
||||||
|
**实现**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/middleware/security/sliding_window.go
|
||||||
|
|
||||||
|
// SlidingWindowLimiter 滑动窗口限流器。
|
||||||
|
type SlidingWindowLimiter struct {
|
||||||
|
// window 窗口大小
|
||||||
|
window time.Duration
|
||||||
|
|
||||||
|
// limit 窗口内最大请求数
|
||||||
|
limit int
|
||||||
|
|
||||||
|
// precise 是否使用精确模式
|
||||||
|
precise bool
|
||||||
|
|
||||||
|
// counters 窗口计数器,key 为窗口起始时间
|
||||||
|
counters map[int64]*windowCounter
|
||||||
|
|
||||||
|
// mu 读写锁
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// windowCounter 窗口计数器。
|
||||||
|
type windowCounter struct {
|
||||||
|
count int64
|
||||||
|
// precise 模式下记录每个请求时间
|
||||||
|
timestamps []time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow 检查是否允许请求。
|
||||||
|
func (s *SlidingWindowLimiter) Allow(key string) bool {
|
||||||
|
if s.precise {
|
||||||
|
return s.allowPrecise(key)
|
||||||
|
}
|
||||||
|
return s.allowApproximate(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// allowApproximate 近似滑动窗口(推荐,内存 O(1))。
|
||||||
|
func (s *SlidingWindowLimiter) allowApproximate(key string) bool {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
currentWindow := now.UnixNano() / int64(s.window)
|
||||||
|
prevWindow := currentWindow - 1
|
||||||
|
|
||||||
|
// 获取当前窗口计数
|
||||||
|
current, ok := s.counters[currentWindow]
|
||||||
|
if !ok {
|
||||||
|
current = &windowCounter{}
|
||||||
|
s.counters[currentWindow] = current
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取上一个窗口计数
|
||||||
|
prev, ok := s.counters[prevWindow]
|
||||||
|
|
||||||
|
// 计算滑动窗口内的请求数
|
||||||
|
// 公式:当前窗口计数 × 1.0 + 上一窗口计数 × (1 - 当前窗口已过比例)
|
||||||
|
var count int64
|
||||||
|
if ok {
|
||||||
|
elapsed := float64(now.UnixNano()%int64(s.window)) / float64(s.window)
|
||||||
|
count = current.count + int64(float64(prev.count)*(1-elapsed))
|
||||||
|
} else {
|
||||||
|
count = current.count
|
||||||
|
}
|
||||||
|
|
||||||
|
if count >= int64(s.limit) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
current.count++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// allowPrecise 精确滑动窗口(内存 O(n),精确限流)。
|
||||||
|
func (s *SlidingWindowLimiter) allowPrecise(key string) bool {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
windowStart := now.Add(-s.window)
|
||||||
|
|
||||||
|
// 清理过期的时间戳
|
||||||
|
valid := make([]time.Time, 0, len(s.timestamps))
|
||||||
|
for _, t := range s.timestamps {
|
||||||
|
if t.After(windowStart) {
|
||||||
|
valid = append(valid, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.timestamps = valid
|
||||||
|
|
||||||
|
// 检查是否超过限制
|
||||||
|
if len(s.timestamps) >= s.limit {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.timestamps = append(s.timestamps, now)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**配置示例**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
security:
|
||||||
|
rate_limit:
|
||||||
|
request_rate: 100
|
||||||
|
burst: 20
|
||||||
|
algorithm: sliding_window # token_bucket / sliding_window
|
||||||
|
sliding_window_mode: approximate # approximate / precise
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9.6 配置扩展
|
||||||
|
|
||||||
|
**新增配置结构**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/config/config.go
|
||||||
|
|
||||||
|
// HTTP3Config HTTP/3 配置。
|
||||||
|
type HTTP3Config struct {
|
||||||
|
Enabled bool `yaml:"enabled"` // 是否启用 HTTP/3
|
||||||
|
Listen string `yaml:"listen"` // UDP 监听地址,如 ":443"
|
||||||
|
MaxStreams int `yaml:"max_streams"` // 最大并发流
|
||||||
|
IdleTimeout time.Duration `yaml:"idle_timeout"` // 空闲超时
|
||||||
|
Enable0RTT bool `yaml:"enable_0rtt"` // 启用 0-RTT
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalanceConfig 负载均衡扩展配置。
|
||||||
|
type LoadBalanceConfig struct {
|
||||||
|
HashKey string `yaml:"hash_key"` // 一致性哈希键:ip / uri / header:xxx
|
||||||
|
VirtualNodes int `yaml:"virtual_nodes"` // 虚拟节点数,默认 150
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompressionConfig 扩展。
|
||||||
|
type CompressionConfig struct {
|
||||||
|
// ... 现有字段
|
||||||
|
GzipStatic bool `yaml:"gzip_static"` // 启用预压缩
|
||||||
|
GzipStaticExtensions []string `yaml:"gzip_static_extensions"` // 预压缩扩展名
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimitConfig 扩展。
|
||||||
|
type RateLimitConfig struct {
|
||||||
|
// ... 现有字段
|
||||||
|
Algorithm string `yaml:"algorithm"` // token_bucket / sliding_window
|
||||||
|
SlidingWindowMode string `yaml:"sliding_window_mode"` // approximate / precise
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9.7 app.go 集成
|
||||||
|
|
||||||
|
**修改文件**:`internal/app/app.go`
|
||||||
|
|
||||||
|
**修改内容**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Run 启动应用(修改后)。
|
||||||
|
func (a *App) Run() error {
|
||||||
|
// 1. 启动 TCP Server (HTTP/1.1/2)
|
||||||
|
go func() {
|
||||||
|
if err := a.srv.Start(); err != nil {
|
||||||
|
logging.Error().Msgf("HTTP server error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 2. 如果启用 HTTP/3,启动 UDP Server
|
||||||
|
if a.config.HTTP3.Enabled {
|
||||||
|
go func() {
|
||||||
|
if err := a.http3Server.Start(); err != nil {
|
||||||
|
logging.Error().Msgf("HTTP/3 server error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 如果配置了 Stream,启动 Stream Server
|
||||||
|
// ... 现有逻辑
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown 关闭应用(修改后)。
|
||||||
|
func (a *App) Shutdown() error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
// 关闭 HTTP/3 Server
|
||||||
|
if a.http3Server != nil {
|
||||||
|
if err := a.http3Server.Stop(); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭 TCP Server
|
||||||
|
if err := a.srv.Stop(); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... 其他关闭逻辑
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return fmt.Errorf("shutdown errors: %v", errs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 验证方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. HTTP/3 测试
|
||||||
|
# 启动服务器(配置 http3.enabled: true)
|
||||||
|
./lolly -c config.yaml
|
||||||
|
|
||||||
|
# 使用 curl 测试 HTTP/3
|
||||||
|
curl --http3 https://localhost:8443/
|
||||||
|
|
||||||
|
# 检查 Alt-Svc 头
|
||||||
|
curl -I https://localhost:8443/
|
||||||
|
# 应返回 Alt-Svc: h3=":443"
|
||||||
|
|
||||||
|
# 2. 一致性哈希测试
|
||||||
|
# 配置 load_balance: consistent_hash
|
||||||
|
for i in {1..10}; do
|
||||||
|
curl http://localhost:8080/api/test
|
||||||
|
done
|
||||||
|
# 相同 IP 的请求应路由到同一后端
|
||||||
|
|
||||||
|
# 3. gzip_static 测试
|
||||||
|
# 创建预压缩文件
|
||||||
|
gzip -k static/index.html
|
||||||
|
|
||||||
|
# 测试请求
|
||||||
|
curl -H "Accept-Encoding: gzip" -I http://localhost:8080/index.html
|
||||||
|
# 应返回 Content-Encoding: gzip,且响应时间更快
|
||||||
|
|
||||||
|
# 4. 滑动窗口限流测试
|
||||||
|
# 配置 algorithm: sliding_window
|
||||||
|
for i in {1..120}; do
|
||||||
|
curl http://localhost:8080/
|
||||||
|
done
|
||||||
|
# 应有部分请求返回 429
|
||||||
|
|
||||||
|
# 5. 完整测试
|
||||||
|
go test ./... -race
|
||||||
|
go build ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文件依赖关系图
|
||||||
|
|
||||||
|
```
|
||||||
|
Phase 9:
|
||||||
|
internal/http3/server.go → internal/http3/adapter.go(新增)
|
||||||
|
internal/http3/server.go → internal/ssl/ssl.go(TLS 配置复用)
|
||||||
|
internal/app/app.go → internal/http3/server.go(集成)
|
||||||
|
|
||||||
|
internal/loadbalance/balancer.go → internal/loadbalance/consistent_hash.go(新增)
|
||||||
|
|
||||||
|
internal/middleware/compression/compression.go → internal/middleware/compression/gzip_static.go(新增)
|
||||||
|
|
||||||
|
internal/middleware/security/ratelimit.go → internal/middleware/security/sliding_window.go(新增)
|
||||||
|
|
||||||
|
internal/config/config.go → 扩展配置结构
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 总体进度追踪(更新)
|
## 总体进度追踪(更新)
|
||||||
|
|
||||||
| 阶段 | 状态 | 主要功能 |
|
| 阶段 | 状态 | 主要功能 |
|
||||||
| ------- | ------ | ------------------------- |
|
| ------- | -------- | ------------------------- |
|
||||||
| Phase 1 | ✅ 完成 | 项目骨架、配置系统 |
|
| Phase 1 | ✅ 完成 | 项目骨架、配置系统 |
|
||||||
| Phase 2 | ✅ 完成 | HTTP 核心、静态文件、路由 |
|
| Phase 2 | ✅ 完成 | HTTP 核心、静态文件、路由 |
|
||||||
| Phase 3 | ✅ 完成 | 反向代理、负载均衡 |
|
| Phase 3 | ✅ 完成 | 反向代理、负载均衡 |
|
||||||
| Phase 4 | ✅ 完成 | SSL/TLS、安全控制 |
|
| Phase 4 | ✅ 完成 | SSL/TLS、安全控制 |
|
||||||
| Phase 5 | ✅ 完成 | 重写、压缩、缓存、日志 |
|
| Phase 5 | ✅ 完成 | 重写、压缩、缓存、日志 |
|
||||||
| Phase 6 | ✅ 完成 | Stream、性能优化、热升级 |
|
| Phase 6 | ✅ 完成 | Stream、性能优化、热升级 |
|
||||||
| Phase 7 | ✅ 完成 | 功能完善 |
|
| Phase 7 | ✅ 完成 | 功能完善 |
|
||||||
| Phase 8 | ✅ 完成 | 问题修复(WebSocket集成、UDP清理、热升级修复)|
|
| Phase 8 | ✅ 完成 | 问题修复(WebSocket集成、UDP清理、热升级修复)|
|
||||||
|
| Phase 9 | 🔄 计划中| HTTP/3、一致性哈希、gzip_static、滑动窗口限流 |
|
||||||
|
|
||||||
**Phase 8 完成日期**:2026-04-03
|
**Phase 8 完成日期**:2026-04-03
|
||||||
|
|
||||||
@ -1869,3 +2466,9 @@ Phase 8:
|
|||||||
1. WebSocket 代理集成 - `handleWebSocket` 现调用 `ProxyWebSocket`
|
1. WebSocket 代理集成 - `handleWebSocket` 现调用 `ProxyWebSocket`
|
||||||
2. UDP Stream 冗余代码 - 删除 `udpListener` 类型及相关测试
|
2. UDP Stream 冗余代码 - 删除 `udpListener` 类型及相关测试
|
||||||
3. 热升级监听器继承 - 改用 `net.Listen` + `Serve` 模式,支持监听器继承
|
3. 热升级监听器继承 - 改用 `net.Listen` + `Serve` 模式,支持监听器继承
|
||||||
|
|
||||||
|
**Phase 9 计划内容**:
|
||||||
|
1. HTTP/3 (QUIC) 支持 - 使用 quic-go,双栈并行架构
|
||||||
|
2. 一致性哈希负载均衡 - 支持可配置哈希键(ip/uri/header)
|
||||||
|
3. gzip_static 预压缩 - 静态资源优化
|
||||||
|
4. 滑动窗口限流 - 近似和精确两种模式
|
||||||
|
|||||||
@ -7,10 +7,14 @@
|
|||||||
|
|
||||||
/ultrawork 深度分析 https://nginx.org/en/docs/ nginx 的功能,@docs/ 目录下已经有一些分析过的文档了,看看有没有能完善的
|
/ultrawork 深度分析 https://nginx.org/en/docs/ nginx 的功能,@docs/ 目录下已经有一些分析过的文档了,看看有没有能完善的
|
||||||
|
|
||||||
|
/ultrawork 深度分析下当前的配置文件,是否有配置文件描述了,但代码未实现的功能
|
||||||
|
|
||||||
## 单元测试
|
## 单元测试
|
||||||
|
|
||||||
/ultrawork 分析下当前测试覆盖率
|
/ultrawork 分析下当前测试覆盖率
|
||||||
|
|
||||||
|
/plan 分析一个完善测试的方案
|
||||||
|
|
||||||
## 注释
|
## 注释
|
||||||
|
|
||||||
/ultrawork 参考 @docs/comments.md,分析项目注释是否完善
|
/ultrawork 参考 @docs/comments.md,分析项目注释是否完善
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user