From 88a2c1fc1b83e32afa37bffc554417c2a11300c9 Mon Sep 17 00:00:00 2001 From: xfy Date: Mon, 8 Jun 2026 17:57:06 +0800 Subject: [PATCH] feat(config): add Least Time and Sticky configuration support - Add least_time and sticky to valid algorithms list - Add LeastTimeConfig and StickyConfig structures - Update default config generation with new options - Add configuration validation for new fields --- internal/config/defaults.go | 13 +++++- internal/config/proxy_config.go | 75 +++++++++++++++++++++++++++++- internal/config/validate.go | 17 +++++++ internal/loadbalance/algorithms.go | 2 + 4 files changed, 105 insertions(+), 2 deletions(-) diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 6909950..6a873f2 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -397,7 +397,7 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) { buf.WriteString(" # backup: false # 备份服务器标记(仅当主服务器不可用时使用)\n") buf.WriteString(" # down: false # 永久不可用标记\n") buf.WriteString(" # proxy_uri: \"\" # 代理传递的 URI 路径\n") - buf.WriteString(" # load_balance: round_robin # 负载均衡算法(有效值: round_robin, weighted_round_robin, least_conn, ip_hash, consistent_hash, random)\n") + buf.WriteString(" # load_balance: round_robin # 负载均衡算法(有效值: round_robin, weighted_round_robin, least_conn, ip_hash, consistent_hash, random, least_time, sticky)\n") buf.WriteString(" # hash_key: ip # 一致性哈希键(仅 load_balance=consistent_hash 时有效,有效值: ip, uri, header:X-Name)\n") buf.WriteString(" # virtual_nodes: 150 # 一致性哈希虚拟节点数(仅 load_balance=consistent_hash 时有效)\n") buf.WriteString(" # health_check: # 健康检查\n") @@ -480,6 +480,17 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) { buf.WriteString(" # # replacement: \"$scheme://$host:$server_port/\" # 替换目标(支持 $host, $scheme, $server_port 等变量)\n") buf.WriteString(" # # - pattern: \"~^http://[^/]+:8000/(.*)$\" # 正则匹配示例\n") buf.WriteString(" # # replacement: \"$scheme://$host/$1\" # 使用捕获组 $1\n") + buf.WriteString(" # least_time: # 最小时间负载均衡配置\n") + buf.WriteString(" # metric: last_byte # 指标类型(header | last_byte)\n") + buf.WriteString(" # default_time: 1ms # 无统计样本时的默认响应时间\n") + buf.WriteString(" # sticky: # Session Sticky 配置\n") + buf.WriteString(" # enabled: false # 是否启用\n") + buf.WriteString(" # name: lolly_route # cookie 名称\n") + buf.WriteString(" # expires: 1h # session 有效期\n") + buf.WriteString(" # path: / # cookie 路径\n") + buf.WriteString(" # http_only: true # HttpOnly flag\n") + buf.WriteString(" # same_site: Lax # SameSite 属性\n") + buf.WriteString(" # fallback_balance: round_robin # fallback 算法\n") buf.WriteString("\n") // SSL 配置 diff --git a/internal/config/proxy_config.go b/internal/config/proxy_config.go index 06af244..294541e 100644 --- a/internal/config/proxy_config.go +++ b/internal/config/proxy_config.go @@ -19,7 +19,7 @@ import ( // 注意事项: // - Path 使用前缀匹配,较长路径优先匹配 // - 至少配置一个 Target 才能正常工作 -// - 负载均衡算法支持:round_robin、weighted_round_robin、least_conn、ip_hash、consistent_hash、random +// - 负载均衡算法支持:round_robin、weighted_round_robin、least_conn、ip_hash、consistent_hash、random、least_time、sticky // - 一致性哈希需要配置 HashKey // // 使用示例: @@ -56,6 +56,8 @@ type ProxyConfig struct { NextUpstream NextUpstreamConfig `yaml:"next_upstream"` Cache ProxyCacheConfig `yaml:"cache"` Timeout ProxyTimeout `yaml:"timeout"` + LeastTime LeastTimeConfig `yaml:"least_time"` + Sticky StickyConfig `yaml:"sticky"` // 基本类型字段 VirtualNodes int `yaml:"virtual_nodes"` @@ -440,6 +442,77 @@ type RedirectRewriteRule struct { Replacement string `yaml:"replacement"` } +// LeastTimeConfig 最小时间负载均衡配置。 +// +// 基于后端响应时间选择最优后端。 +// +// 注意事项: +// - Metric 决定时间统计方式 +// - DefaultTime 用于首次请求无样本时 +// +// 使用示例: +// +// least_time: +// metric: last_byte # 指标类型(header | last_byte) +// default_time: 1ms # 无统计样本时的默认响应时间 +type LeastTimeConfig struct { + // Metric 时间指标类型 + // 可选值:header(首字节时间)、last_byte(末字节时间) + Metric string `yaml:"metric"` + + // DefaultTime 无统计样本时的默认响应时间 + // 用于首次请求或新后端加入时 + DefaultTime time.Duration `yaml:"default_time"` +} + +// StickyConfig Session Sticky 配置。 +// +// 通过 Cookie 将同一客户端固定到同一后端。 +// +// 注意事项: +// - Enabled 为 true 时启用 +// - FallbackAlgo 指定当无法找到 sticky 后端时使用的算法 +// +// 使用示例: +// +// sticky: +// enabled: true +// name: lolly_route # cookie 名称 +// expires: 1h # session 有效期 +// path: / # cookie 路径 +// http_only: true # HttpOnly flag +// same_site: Lax # SameSite 属性 +// fallback_balance: round_robin # fallback 算法 +type StickyConfig struct { + // Enabled 是否启用 Session Sticky + Enabled bool `yaml:"enabled"` + + // Name Cookie 名称 + Name string `yaml:"name"` + + // Expires session 有效期 + Expires time.Duration `yaml:"expires"` + + // Domain Cookie 域 + Domain string `yaml:"domain"` + + // Path Cookie 路径 + Path string `yaml:"path"` + + // Secure Secure flag + Secure bool `yaml:"secure"` + + // HttpOnly HttpOnly flag + HttpOnly bool `yaml:"http_only"` + + // SameSite SameSite 属性 + // 可选值:Lax, Strict, None + SameSite string `yaml:"same_site"` + + // FallbackAlgo 无法找到 sticky 后端时的 fallback 算法 + FallbackAlgo string `yaml:"fallback_balance"` +} + // NextUpstreamConfig 故障转移配置,定义后端失败时的自动重试行为。 // // 当后端返回特定错误状态码或连接失败时,自动尝试下一个可用后端。 diff --git a/internal/config/validate.go b/internal/config/validate.go index 995df36..2d8c4ac 100644 --- a/internal/config/validate.go +++ b/internal/config/validate.go @@ -504,6 +504,23 @@ func validateProxy(p *ProxyConfig) error { return fmt.Errorf("无效的负载均衡算法:%s", p.LoadBalance) } + // validate least_time config + if p.LoadBalance == "least_time" { + if p.LeastTime.Metric != "" && p.LeastTime.Metric != "header" && p.LeastTime.Metric != "last_byte" { + return fmt.Errorf("无效的 least_time metric: %s(有效值: header, last_byte)", p.LeastTime.Metric) + } + } + + // validate sticky config + if p.LoadBalance == "sticky" { + if !p.Sticky.Enabled { + return fmt.Errorf("load_balance=sticky 时 sticky.enabled 必须为 true") + } + if p.Sticky.FallbackAlgo != "" && !loadbalance.IsValidAlgorithm(p.Sticky.FallbackAlgo) { + return fmt.Errorf("无效的 sticky fallback_balance: %s", p.Sticky.FallbackAlgo) + } + } + // 验证故障转移配置 if err := validateNextUpstream(&p.NextUpstream); err != nil { return fmt.Errorf("next_upstream: %w", err) diff --git a/internal/loadbalance/algorithms.go b/internal/loadbalance/algorithms.go index e3fbbc8..2eaba2f 100644 --- a/internal/loadbalance/algorithms.go +++ b/internal/loadbalance/algorithms.go @@ -24,6 +24,8 @@ var validAlgorithms = []string{ "ip_hash", "consistent_hash", "random", + "least_time", + "sticky", } // IsValidAlgorithm 检查给定的算法名称是否有效。