feat: 十轮网关优化 - 安全加固/可观测性/性能/可靠性
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled
CI / security-scan (push) Has been cancelled

- SSE Keepalive Ping (15s心跳防止代理断连)
- Timing HTTP 头 (X-Timing-Queue/Inference/Total-Ms)
- Adapter Request-ID 传播到后端
- Session 清理日志回调
- Server 安全加固 (ReadHeaderTimeout/MaxHeaderBytes 防 slowloris)
- Usage Tracker 数据保留清理 (retentionDays + 定期清理)
- Config Reload 后 Adapter Registry 更新 (RegisterIfAbsent + RWMutex)
- Rate Limiter 空闲 Bucket 清理 (30分钟过期)
- Shutdown Drain 超时可配置 (ShutdownDrainSeconds)
- Config 模型字段校验增强 (provider/endpoint/actual_model)
- Auth 过期 Key 自动清理 (5分钟扫描)
- Admin API Rate Limiting
- Adapter Health Check 独立超时 (每个 adapter 3s)
- TCP 连接阶段超时 (DialContext 5s + KeepAlive 30s)
- 幂等键缓存、审计日志、Gzip 中间件、CORS Expose Headers
- Backpressure 响应头、熔断器 Prometheus 指标
- 连接池优化、Trace-ID 全链路传播
This commit is contained in:
selfrelease
2026-08-03 15:43:11 +08:00
parent e7e98271d4
commit da9c8334d8
27 changed files with 3002 additions and 309 deletions
+106 -39
View File
@@ -12,35 +12,39 @@ import (
// Config is the root configuration structure.
type Config struct {
Server ServerConfig `yaml:"server"`
Auth AuthConfig `yaml:"auth"`
Scheduler SchedulerConfig `yaml:"scheduler"`
Timeouts TimeoutConfig `yaml:"timeouts"`
Context ContextConfig `yaml:"context"`
Server ServerConfig `yaml:"server"`
Auth AuthConfig `yaml:"auth"`
Scheduler SchedulerConfig `yaml:"scheduler"`
Timeouts TimeoutConfig `yaml:"timeouts"`
Context ContextConfig `yaml:"context"`
Models map[string]ModelConfig `yaml:"models"`
Routing RoutingConfig `yaml:"routing"`
CircuitBreaker CircuitBreakerConfig `yaml:"circuit_breaker"`
Backpressure BackpressureConfig `yaml:"backpressure"`
Observability ObservabilityConfig `yaml:"observability"`
Storage StorageConfig `yaml:"storage"`
Routing RoutingConfig `yaml:"routing"`
CircuitBreaker CircuitBreakerConfig `yaml:"circuit_breaker"`
Backpressure BackpressureConfig `yaml:"backpressure"`
Observability ObservabilityConfig `yaml:"observability"`
Storage StorageConfig `yaml:"storage"`
}
type ServerConfig struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
AdminPort int `yaml:"admin_port"`
MaxRequestBodyMB int `yaml:"max_request_body_mb"`
Host string `yaml:"host"`
Port int `yaml:"port"`
AdminPort int `yaml:"admin_port"`
MaxRequestBodyMB int `yaml:"max_request_body_mb"`
CORSAllowedOrigins []string `yaml:"cors_allowed_origins"`
}
type AuthConfig struct {
Enabled bool `yaml:"enabled"`
Methods []string `yaml:"methods"`
JWTIssuer string `yaml:"jwt_issuer"`
JWTSecretEnv string `yaml:"jwt_secret_env"`
Enabled bool `yaml:"enabled"`
Methods []string `yaml:"methods"`
JWTIssuer string `yaml:"jwt_issuer"`
JWTSecretEnv string `yaml:"jwt_secret_env"`
RateLimitPerMinute int `yaml:"rate_limit_per_minute"`
RateLimitBurst int `yaml:"rate_limit_burst"`
UsageWindowMinutes int `yaml:"usage_window_minutes"`
}
type SchedulerConfig struct {
MaxRunningTasks int `yaml:"max_running_tasks"`
MaxRunningTasks int `yaml:"max_running_tasks"`
MaxQueuedTasks int `yaml:"max_queued_tasks"`
Fairness string `yaml:"fairness"`
PriorityAgingSeconds int `yaml:"priority_aging_seconds"`
@@ -48,21 +52,25 @@ type SchedulerConfig struct {
}
type TimeoutConfig struct {
DefaultConnectMs int `yaml:"default_connect_ms"`
DefaultQueueMs int `yaml:"default_queue_ms"`
DefaultFirstTokenMs int `yaml:"default_first_token_ms"`
DefaultInferenceMs int `yaml:"default_inference_ms"`
DefaultIdleMs int `yaml:"default_idle_ms"`
DefaultTotalMs int `yaml:"default_total_ms"`
CancelGracePeriodMs int `yaml:"cancel_grace_period_ms"`
DefaultConnectMs int `yaml:"default_connect_ms"`
DefaultQueueMs int `yaml:"default_queue_ms"`
DefaultFirstTokenMs int `yaml:"default_first_token_ms"`
DefaultInferenceMs int `yaml:"default_inference_ms"`
DefaultIdleMs int `yaml:"default_idle_ms"`
DefaultTotalMs int `yaml:"default_total_ms"`
CancelGracePeriodMs int `yaml:"cancel_grace_period_ms"`
ShutdownDrainSeconds int `yaml:"shutdown_drain_seconds"` // 优雅关机时等待运行中任务的超时时间
}
type ContextConfig struct {
SafetyMarginRatio float64 `yaml:"safety_margin_ratio"`
DefaultPolicy string `yaml:"default_policy"`
MaxSessionMessages int `yaml:"max_session_messages"`
SessionIdleTTLMinutes int `yaml:"session_idle_ttl_minutes"`
EnablePromptPersistence bool `yaml:"enable_prompt_persistence"`
SafetyMarginRatio float64 `yaml:"safety_margin_ratio"`
DefaultPolicy string `yaml:"default_policy"`
MaxSessionMessages int `yaml:"max_session_messages"`
SessionIdleTTLMinutes int `yaml:"session_idle_ttl_minutes"`
EnablePromptPersistence bool `yaml:"enable_prompt_persistence"`
EnableLLMSummary bool `yaml:"enable_llm_summary"`
SummaryMaxTokens int `yaml:"summary_max_tokens"`
SummaryTimeoutSeconds int `yaml:"summary_timeout_seconds"`
}
type ModelConfig struct {
@@ -80,7 +88,7 @@ type ModelConfig struct {
type RoutingConfig struct {
SensitiveDataLocalOnly bool `yaml:"sensitive_data_local_only"`
AllowCloudFallbackByDefault bool `yaml:"allow_cloud_fallback_by_default"`
OverloadStrategy []string `yaml:"overload_strategy"`
OverloadStrategy []string `yaml:"overload_strategy"`
}
type CircuitBreakerConfig struct {
@@ -98,12 +106,12 @@ type BackpressureConfig struct {
}
type ObservabilityConfig struct {
MetricsEnabled bool `yaml:"metrics_enabled"`
MetricsPath string `yaml:"metrics_path"`
TracingEnabled bool `yaml:"tracing_enabled"`
PromptLogging string `yaml:"prompt_logging"`
AuditRetentionDays int `yaml:"audit_retention_days"`
LogLevel string `yaml:"log_level"`
MetricsEnabled bool `yaml:"metrics_enabled"`
MetricsPath string `yaml:"metrics_path"`
TracingEnabled bool `yaml:"tracing_enabled"`
PromptLogging string `yaml:"prompt_logging"`
AuditRetentionDays int `yaml:"audit_retention_days"`
LogLevel string `yaml:"log_level"`
}
type StorageConfig struct {
@@ -153,7 +161,7 @@ func applyDefaults(cfg *Config) {
cfg.Server.Host = "0.0.0.0"
}
if cfg.Server.Port == 0 {
cfg.Server.Port = 8080
cfg.Server.Port = 39000
}
if cfg.Server.AdminPort == 0 {
cfg.Server.AdminPort = 8081
@@ -194,6 +202,9 @@ func applyDefaults(cfg *Config) {
if cfg.Timeouts.CancelGracePeriodMs == 0 {
cfg.Timeouts.CancelGracePeriodMs = 3000
}
if cfg.Timeouts.ShutdownDrainSeconds == 0 {
cfg.Timeouts.ShutdownDrainSeconds = 10
}
if cfg.Context.SafetyMarginRatio == 0 {
cfg.Context.SafetyMarginRatio = 0.08
}
@@ -206,6 +217,12 @@ func applyDefaults(cfg *Config) {
if cfg.Context.SessionIdleTTLMinutes == 0 {
cfg.Context.SessionIdleTTLMinutes = 60
}
if cfg.Context.SummaryMaxTokens == 0 {
cfg.Context.SummaryMaxTokens = 256
}
if cfg.Context.SummaryTimeoutSeconds == 0 {
cfg.Context.SummaryTimeoutSeconds = 15
}
if cfg.CircuitBreaker.ErrorRateThreshold == 0 {
cfg.CircuitBreaker.ErrorRateThreshold = 0.1
}
@@ -242,6 +259,15 @@ func applyDefaults(cfg *Config) {
if cfg.Observability.AuditRetentionDays == 0 {
cfg.Observability.AuditRetentionDays = 180
}
if cfg.Auth.RateLimitPerMinute <= 0 {
cfg.Auth.RateLimitPerMinute = 60
}
if cfg.Auth.RateLimitBurst <= 0 {
cfg.Auth.RateLimitBurst = 10
}
if cfg.Auth.UsageWindowMinutes <= 0 {
cfg.Auth.UsageWindowMinutes = 60
}
if cfg.Storage.SessionDB == "" {
cfg.Storage.SessionDB = "sqlite:///var/lib/edgeai/sessions.db"
}
@@ -251,6 +277,18 @@ func applyDefaults(cfg *Config) {
}
func validate(cfg *Config) error {
if cfg.Server.Port <= 0 || cfg.Server.Port > 65535 {
return fmt.Errorf("server.port must be in [1, 65535]")
}
if cfg.Server.AdminPort < 0 || cfg.Server.AdminPort > 65535 {
return fmt.Errorf("server.admin_port must be in [0, 65535]")
}
if cfg.Server.AdminPort > 0 && cfg.Server.AdminPort == cfg.Server.Port {
return fmt.Errorf("server.admin_port must differ from server.port")
}
if cfg.Server.MaxRequestBodyMB <= 0 {
return fmt.Errorf("server.max_request_body_mb must be positive")
}
if cfg.Scheduler.MaxRunningTasks <= 0 {
return fmt.Errorf("scheduler.max_running_tasks must be positive")
}
@@ -260,12 +298,41 @@ func validate(cfg *Config) error {
if cfg.Context.SafetyMarginRatio < 0 || cfg.Context.SafetyMarginRatio >= 1 {
return fmt.Errorf("context.safety_margin_ratio must be in [0, 1)")
}
if cfg.Context.MaxSessionMessages <= 0 {
return fmt.Errorf("context.max_session_messages must be positive")
}
if cfg.Backpressure.Level1Threshold >= cfg.Backpressure.Level2Threshold {
return fmt.Errorf("backpressure level1 threshold must be less than level2")
}
if cfg.Backpressure.Level2Threshold >= cfg.Backpressure.Level3Threshold {
return fmt.Errorf("backpressure level2 threshold must be less than level3")
}
if cfg.Auth.RateLimitPerMinute <= 0 {
return fmt.Errorf("auth.rate_limit_per_minute must be positive")
}
if cfg.Auth.RateLimitBurst <= 0 {
return fmt.Errorf("auth.rate_limit_burst must be positive")
}
if len(cfg.Models) == 0 {
return fmt.Errorf("models must not be empty")
}
for name, mc := range cfg.Models {
if mc.Provider == "" {
return fmt.Errorf("models.%s.provider must not be empty", name)
}
if mc.Endpoint == "" {
return fmt.Errorf("models.%s.endpoint must not be empty", name)
}
if mc.ActualModel == "" {
return fmt.Errorf("models.%s.actual_model must not be empty", name)
}
if mc.ContextWindow < 0 {
return fmt.Errorf("models.%s.context_window must not be negative", name)
}
if mc.MaxOutputTokens < 0 {
return fmt.Errorf("models.%s.max_output_tokens must not be negative", name)
}
}
return nil
}