2026-04-08 07:44:58 +08:00
|
|
|
|
package middleware
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"net/http"
|
2026-04-08 18:20:40 +08:00
|
|
|
|
"sync"
|
2026-04-08 07:44:58 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== P1-03 中间件超时配置 ====================
|
|
|
|
|
|
|
|
|
|
|
|
// MiddlewareTimeoutConfig 中间件超时配置
|
|
|
|
|
|
type MiddlewareTimeoutConfig struct {
|
|
|
|
|
|
// 各中间件超时配置
|
|
|
|
|
|
RecoveryTimeout time.Duration // Recovery中间件
|
|
|
|
|
|
LoggingTimeout time.Duration // Logging中间件
|
|
|
|
|
|
RequestIDTimeout time.Duration // RequestID中间件
|
|
|
|
|
|
AuthnTimeout time.Duration // 认证中间件
|
|
|
|
|
|
AuthzTimeout time.Duration // 授权中间件
|
|
|
|
|
|
RateLimitTimeout time.Duration // 限流中间件
|
|
|
|
|
|
IdempotencyTimeout time.Duration // 幂等中间件
|
|
|
|
|
|
BusinessTimeout time.Duration // 业务处理
|
|
|
|
|
|
|
|
|
|
|
|
// 默认超时
|
|
|
|
|
|
DefaultTimeout time.Duration
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DefaultMiddlewareTimeoutConfig 返回默认中间件超时配置
|
|
|
|
|
|
// 根据PRD和行业最佳实践:建议总超时 ≤ 200ms
|
|
|
|
|
|
func DefaultMiddlewareTimeoutConfig() *MiddlewareTimeoutConfig {
|
|
|
|
|
|
return &MiddlewareTimeoutConfig{
|
|
|
|
|
|
// 快速中间件(内存操作)
|
|
|
|
|
|
RecoveryTimeout: 5 * time.Millisecond,
|
|
|
|
|
|
LoggingTimeout: 10 * time.Millisecond,
|
|
|
|
|
|
RequestIDTimeout: 5 * time.Millisecond,
|
|
|
|
|
|
|
|
|
|
|
|
// 网络操作相关
|
|
|
|
|
|
AuthnTimeout: 50 * time.Millisecond, // JWT验证+缓存查询
|
|
|
|
|
|
AuthzTimeout: 30 * time.Millisecond, // 权限检查
|
|
|
|
|
|
RateLimitTimeout: 20 * time.Millisecond, // 限流检查
|
|
|
|
|
|
IdempotencyTimeout: 30 * time.Millisecond, // 幂等检查
|
|
|
|
|
|
|
|
|
|
|
|
// 业务处理(最灵活)
|
|
|
|
|
|
BusinessTimeout: 100 * time.Millisecond,
|
|
|
|
|
|
|
|
|
|
|
|
// 默认兜底超时
|
|
|
|
|
|
DefaultTimeout: 200 * time.Millisecond,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TotalTimeout 计算总超时时间
|
|
|
|
|
|
func (c *MiddlewareTimeoutConfig) TotalTimeout() time.Duration {
|
|
|
|
|
|
return c.RecoveryTimeout +
|
|
|
|
|
|
c.LoggingTimeout +
|
|
|
|
|
|
c.RequestIDTimeout +
|
|
|
|
|
|
c.AuthnTimeout +
|
|
|
|
|
|
c.AuthzTimeout +
|
|
|
|
|
|
c.RateLimitTimeout +
|
|
|
|
|
|
c.IdempotencyTimeout +
|
|
|
|
|
|
c.BusinessTimeout
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MiddlewareTimeoutContext 带超时的中间件上下文
|
|
|
|
|
|
type MiddlewareTimeoutContext struct {
|
|
|
|
|
|
config *MiddlewareTimeoutConfig
|
|
|
|
|
|
deadline time.Time
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewMiddlewareTimeoutContext 创建带超时的中间件上下文
|
|
|
|
|
|
func NewMiddlewareTimeoutContext(config *MiddlewareTimeoutConfig) *MiddlewareTimeoutContext {
|
|
|
|
|
|
if config == nil {
|
|
|
|
|
|
config = DefaultMiddlewareTimeoutConfig()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &MiddlewareTimeoutContext{
|
|
|
|
|
|
config: config,
|
|
|
|
|
|
deadline: time.Now().Add(config.TotalTimeout()),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// WithBusinessTimeout 创建带业务超时的上下文
|
|
|
|
|
|
func (c *MiddlewareTimeoutContext) WithBusinessTimeout() (context.Context, context.CancelFunc) {
|
|
|
|
|
|
return context.WithDeadline(context.Background(), c.deadline)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TimeoutResponseWriter 超时响应writer
|
|
|
|
|
|
type TimeoutResponseWriter struct {
|
|
|
|
|
|
http.ResponseWriter
|
2026-04-08 18:20:40 +08:00
|
|
|
|
mu sync.Mutex
|
2026-04-08 07:44:58 +08:00
|
|
|
|
timeout time.Duration
|
|
|
|
|
|
started time.Time
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *TimeoutResponseWriter) ensureStarted() {
|
2026-04-08 18:20:40 +08:00
|
|
|
|
w.mu.Lock()
|
|
|
|
|
|
defer w.mu.Unlock()
|
2026-04-08 07:44:58 +08:00
|
|
|
|
if w.started.IsZero() {
|
|
|
|
|
|
w.started = time.Now()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *TimeoutResponseWriter) checkTimeout() bool {
|
2026-04-08 18:20:40 +08:00
|
|
|
|
w.mu.Lock()
|
|
|
|
|
|
defer w.mu.Unlock()
|
2026-04-08 07:44:58 +08:00
|
|
|
|
if w.started.IsZero() {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
return time.Since(w.started) > w.timeout
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-08 18:20:40 +08:00
|
|
|
|
func (w *TimeoutResponseWriter) setTimeoutHeader() {
|
|
|
|
|
|
w.mu.Lock()
|
|
|
|
|
|
defer w.mu.Unlock()
|
|
|
|
|
|
w.Header().Set("X-Timeout", "true")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-08 07:44:58 +08:00
|
|
|
|
// WithTimeoutMiddleware 返回带超时检测的中间件
|
2026-04-08 18:20:40 +08:00
|
|
|
|
//
|
|
|
|
|
|
// 设计说明:
|
|
|
|
|
|
// - handler 在 goroutine 中执行
|
|
|
|
|
|
// - 超时时不等待 handler 完成,直接发送超时响应
|
|
|
|
|
|
// - 使用互斥锁确保响应只发送一次
|
|
|
|
|
|
// - 实际生产中应设置合理的超时时间使 handler 有机会在超时前完成
|
2026-04-08 07:44:58 +08:00
|
|
|
|
func WithTimeoutMiddleware(next http.Handler, timeout time.Duration) http.Handler {
|
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2026-04-08 18:20:40 +08:00
|
|
|
|
var mu sync.Mutex
|
|
|
|
|
|
responseSent := false
|
|
|
|
|
|
|
|
|
|
|
|
handlerDone := make(chan struct{})
|
2026-04-08 07:44:58 +08:00
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
next.ServeHTTP(w, r)
|
2026-04-08 18:20:40 +08:00
|
|
|
|
close(handlerDone)
|
2026-04-08 07:44:58 +08:00
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
select {
|
2026-04-08 18:20:40 +08:00
|
|
|
|
case <-handlerDone:
|
2026-04-08 07:44:58 +08:00
|
|
|
|
return
|
|
|
|
|
|
case <-time.After(timeout):
|
2026-04-08 18:20:40 +08:00
|
|
|
|
mu.Lock()
|
|
|
|
|
|
if !responseSent {
|
|
|
|
|
|
responseSent = true
|
|
|
|
|
|
mu.Unlock()
|
|
|
|
|
|
w.Header().Set("X-Timeout", "true")
|
|
|
|
|
|
http.Error(w, fmt.Sprintf("middleware timeout after %v", timeout), http.StatusGatewayTimeout)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
mu.Unlock()
|
2026-04-08 07:44:58 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MiddlewareStageTimeout 中间件阶段超时配置
|
|
|
|
|
|
type MiddlewareStageTimeout struct {
|
|
|
|
|
|
Stage string
|
|
|
|
|
|
Timeout time.Duration
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetStageTimeouts 获取各阶段超时配置
|
|
|
|
|
|
func GetStageTimeouts() []MiddlewareStageTimeout {
|
|
|
|
|
|
config := DefaultMiddlewareTimeoutConfig()
|
|
|
|
|
|
return []MiddlewareStageTimeout{
|
|
|
|
|
|
{"recovery", config.RecoveryTimeout},
|
|
|
|
|
|
{"logging", config.LoggingTimeout},
|
|
|
|
|
|
{"request_id", config.RequestIDTimeout},
|
|
|
|
|
|
{"authn", config.AuthnTimeout},
|
|
|
|
|
|
{"authz", config.AuthzTimeout},
|
|
|
|
|
|
{"ratelimit", config.RateLimitTimeout},
|
|
|
|
|
|
{"idempotency", config.IdempotencyTimeout},
|
|
|
|
|
|
{"business", config.BusinessTimeout},
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|