feat: 配置优化
This commit is contained in:
@@ -2,25 +2,29 @@ package channel
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"gpt-load/internal/config"
|
||||||
"gpt-load/internal/models"
|
"gpt-load/internal/models"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/datatypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetChannel returns a channel proxy based on the group's channel type.
|
// GetChannel returns a channel proxy based on the group's channel type.
|
||||||
func GetChannel(group *models.Group) (ChannelProxy, error) {
|
func GetChannel(group *models.Group) (ChannelProxy, error) {
|
||||||
switch group.ChannelType {
|
switch group.ChannelType {
|
||||||
case "openai":
|
case "openai":
|
||||||
return NewOpenAIChannel(group.Upstreams)
|
return NewOpenAIChannel(group.Upstreams, group.Config)
|
||||||
case "gemini":
|
case "gemini":
|
||||||
return NewGeminiChannel(group.Upstreams)
|
return NewGeminiChannel(group.Upstreams, group.Config)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported channel type: %s", group.ChannelType)
|
return nil, fmt.Errorf("unsupported channel type: %s", group.ChannelType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBaseChannelWithUpstreams is a helper function to create and configure a BaseChannel.
|
// newBaseChannelWithUpstreams is a helper function to create and configure a BaseChannel.
|
||||||
func newBaseChannelWithUpstreams(name string, upstreams []string) (BaseChannel, error) {
|
func newBaseChannelWithUpstreams(name string, upstreams []string, groupConfig datatypes.JSONMap) (BaseChannel, error) {
|
||||||
if len(upstreams) == 0 {
|
if len(upstreams) == 0 {
|
||||||
return BaseChannel{}, fmt.Errorf("at least one upstream is required for %s channel", name)
|
return BaseChannel{}, fmt.Errorf("at least one upstream is required for %s channel", name)
|
||||||
}
|
}
|
||||||
@@ -34,9 +38,21 @@ func newBaseChannelWithUpstreams(name string, upstreams []string) (BaseChannel,
|
|||||||
upstreamURLs = append(upstreamURLs, u)
|
upstreamURLs = append(upstreamURLs, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get effective settings by merging system and group configs
|
||||||
|
settingsManager := config.GetSystemSettingsManager()
|
||||||
|
effectiveSettings := settingsManager.GetEffectiveConfig(groupConfig)
|
||||||
|
|
||||||
|
// Configure the HTTP client with the effective timeouts
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
IdleConnTimeout: time.Duration(effectiveSettings.IdleConnTimeout) * time.Second,
|
||||||
|
},
|
||||||
|
Timeout: time.Duration(effectiveSettings.RequestTimeout) * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
return BaseChannel{
|
return BaseChannel{
|
||||||
Name: name,
|
Name: name,
|
||||||
Upstreams: upstreamURLs,
|
Upstreams: upstreamURLs,
|
||||||
HTTPClient: &http.Client{},
|
HTTPClient: httpClient,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@@ -5,14 +5,15 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/datatypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GeminiChannel struct {
|
type GeminiChannel struct {
|
||||||
BaseChannel
|
BaseChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGeminiChannel(upstreams []string) (*GeminiChannel, error) {
|
func NewGeminiChannel(upstreams []string, config datatypes.JSONMap) (*GeminiChannel, error) {
|
||||||
base, err := newBaseChannelWithUpstreams("gemini", upstreams)
|
base, err := newBaseChannelWithUpstreams("gemini", upstreams, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -5,14 +5,15 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/datatypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OpenAIChannel struct {
|
type OpenAIChannel struct {
|
||||||
BaseChannel
|
BaseChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOpenAIChannel(upstreams []string) (*OpenAIChannel, error) {
|
func NewOpenAIChannel(upstreams []string, config datatypes.JSONMap) (*OpenAIChannel, error) {
|
||||||
base, err := newBaseChannelWithUpstreams("openai", upstreams)
|
base, err := newBaseChannelWithUpstreams("openai", upstreams, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"gpt-load/internal/errors"
|
"gpt-load/internal/errors"
|
||||||
"gpt-load/internal/types"
|
"gpt-load/internal/types"
|
||||||
@@ -37,8 +36,7 @@ var DefaultConstants = Constants{
|
|||||||
|
|
||||||
// Manager implements the ConfigManager interface
|
// Manager implements the ConfigManager interface
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
config *Config
|
config *Config
|
||||||
roundRobinCounter uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config represents the application configuration
|
// Config represents the application configuration
|
||||||
@@ -67,25 +65,27 @@ func (m *Manager) ReloadConfig() error {
|
|||||||
logrus.Info("Info: Create .env file to support environment variable configuration")
|
logrus.Info("Info: Create .env file to support environment variable configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get business logic defaults from the single source of truth
|
||||||
|
defaultSettings := DefaultSystemSettings()
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
Server: types.ServerConfig{
|
Server: types.ServerConfig{
|
||||||
Port: parseInteger(os.Getenv("PORT"), 3000),
|
Port: parseInteger(os.Getenv("PORT"), 3000),
|
||||||
Host: getEnvOrDefault("HOST", "0.0.0.0"),
|
Host: getEnvOrDefault("HOST", "0.0.0.0"),
|
||||||
// Server timeout configs now come from system settings, not environment
|
// Server timeout configs now come from system settings, not environment
|
||||||
// Using defaults here, will be overridden by system settings
|
// Using defaults from SystemSettings struct as the initial value
|
||||||
ReadTimeout: 120,
|
ReadTimeout: defaultSettings.ServerReadTimeout,
|
||||||
WriteTimeout: 1800,
|
WriteTimeout: defaultSettings.ServerWriteTimeout,
|
||||||
IdleTimeout: 120,
|
IdleTimeout: defaultSettings.ServerIdleTimeout,
|
||||||
GracefulShutdownTimeout: 60,
|
GracefulShutdownTimeout: defaultSettings.ServerGracefulShutdownTimeout,
|
||||||
},
|
},
|
||||||
OpenAI: types.OpenAIConfig{
|
OpenAI: types.OpenAIConfig{
|
||||||
// OPENAI_BASE_URL is removed from environment config
|
// BaseURLs will be configured per group
|
||||||
// Base URLs will be configured per group
|
BaseURLs: []string{},
|
||||||
BaseURLs: []string{}, // Will be set per group
|
|
||||||
// Timeout configs now come from system settings
|
// Timeout configs now come from system settings
|
||||||
RequestTimeout: 30,
|
RequestTimeout: defaultSettings.RequestTimeout,
|
||||||
ResponseTimeout: 30,
|
ResponseTimeout: defaultSettings.ResponseTimeout,
|
||||||
IdleConnTimeout: 120,
|
IdleConnTimeout: defaultSettings.IdleConnTimeout,
|
||||||
},
|
},
|
||||||
Auth: types.AuthConfig{
|
Auth: types.AuthConfig{
|
||||||
Key: os.Getenv("AUTH_KEY"),
|
Key: os.Getenv("AUTH_KEY"),
|
||||||
@@ -122,24 +122,6 @@ func (m *Manager) ReloadConfig() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetServerConfig returns server configuration
|
|
||||||
// func (m *Manager) GetServerConfig() types.ServerConfig {
|
|
||||||
// return m.config.Server
|
|
||||||
// }
|
|
||||||
|
|
||||||
// GetOpenAIConfig returns OpenAI configuration
|
|
||||||
// func (m *Manager) GetOpenAIConfig() types.OpenAIConfig {
|
|
||||||
// config := m.config.OpenAI
|
|
||||||
// if len(config.BaseURLs) > 1 {
|
|
||||||
// // Use atomic counter for thread-safe round-robin
|
|
||||||
// index := atomic.AddUint64(&m.roundRobinCounter, 1) - 1
|
|
||||||
// config.BaseURL = config.BaseURLs[index%uint64(len(config.BaseURLs))]
|
|
||||||
// } else if len(config.BaseURLs) == 1 {
|
|
||||||
// config.BaseURL = config.BaseURLs[0]
|
|
||||||
// }
|
|
||||||
// return config
|
|
||||||
// }
|
|
||||||
|
|
||||||
// GetAuthConfig returns authentication configuration
|
// GetAuthConfig returns authentication configuration
|
||||||
func (m *Manager) GetAuthConfig() types.AuthConfig {
|
func (m *Manager) GetAuthConfig() types.AuthConfig {
|
||||||
return m.config.Auth
|
return m.config.Auth
|
||||||
@@ -164,7 +146,7 @@ func (m *Manager) GetLogConfig() types.LogConfig {
|
|||||||
func (m *Manager) GetEffectiveServerConfig() types.ServerConfig {
|
func (m *Manager) GetEffectiveServerConfig() types.ServerConfig {
|
||||||
config := m.config.Server
|
config := m.config.Server
|
||||||
|
|
||||||
// Merge with system settings
|
// Merge with system settings from database
|
||||||
settingsManager := GetSystemSettingsManager()
|
settingsManager := GetSystemSettingsManager()
|
||||||
systemSettings := settingsManager.GetSettings()
|
systemSettings := settingsManager.GetSettings()
|
||||||
|
|
||||||
@@ -176,35 +158,6 @@ func (m *Manager) GetEffectiveServerConfig() types.ServerConfig {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEffectiveOpenAIConfig returns OpenAI configuration merged with system settings and group config
|
|
||||||
func (m *Manager) GetEffectiveOpenAIConfig(groupConfig map[string]any) types.OpenAIConfig {
|
|
||||||
config := m.config.OpenAI
|
|
||||||
|
|
||||||
// Merge with system settings
|
|
||||||
settingsManager := GetSystemSettingsManager()
|
|
||||||
effectiveSettings := settingsManager.GetEffectiveConfig(groupConfig)
|
|
||||||
|
|
||||||
config.RequestTimeout = effectiveSettings.RequestTimeout
|
|
||||||
config.ResponseTimeout = effectiveSettings.ResponseTimeout
|
|
||||||
config.IdleConnTimeout = effectiveSettings.IdleConnTimeout
|
|
||||||
|
|
||||||
// Apply round-robin for multiple URLs if configured
|
|
||||||
if len(config.BaseURLs) > 1 {
|
|
||||||
index := atomic.AddUint64(&m.roundRobinCounter, 1) - 1
|
|
||||||
config.BaseURL = config.BaseURLs[index%uint64(len(config.BaseURLs))]
|
|
||||||
} else if len(config.BaseURLs) == 1 {
|
|
||||||
config.BaseURL = config.BaseURLs[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEffectiveLogConfig returns log configuration (now uses environment config only)
|
|
||||||
// func (m *Manager) GetEffectiveLogConfig() types.LogConfig {
|
|
||||||
// // Log configuration is now managed via environment variables only
|
|
||||||
// return m.config.Log
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Validate validates the configuration
|
// Validate validates the configuration
|
||||||
func (m *Manager) Validate() error {
|
func (m *Manager) Validate() error {
|
||||||
var validationErrors []string
|
var validationErrors []string
|
||||||
@@ -232,7 +185,6 @@ func (m *Manager) Validate() error {
|
|||||||
// DisplayConfig displays current configuration information
|
// DisplayConfig displays current configuration information
|
||||||
func (m *Manager) DisplayConfig() {
|
func (m *Manager) DisplayConfig() {
|
||||||
serverConfig := m.GetEffectiveServerConfig()
|
serverConfig := m.GetEffectiveServerConfig()
|
||||||
// openaiConfig := m.GetOpenAIConfig()
|
|
||||||
authConfig := m.GetAuthConfig()
|
authConfig := m.GetAuthConfig()
|
||||||
corsConfig := m.GetCORSConfig()
|
corsConfig := m.GetCORSConfig()
|
||||||
perfConfig := m.GetPerformanceConfig()
|
perfConfig := m.GetPerformanceConfig()
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"gorm.io/datatypes"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -220,7 +221,7 @@ func (sm *SystemSettingsManager) UpdateSettings(settingsMap map[string]string) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetEffectiveConfig 获取有效配置 (系统配置 + 分组覆盖)
|
// GetEffectiveConfig 获取有效配置 (系统配置 + 分组覆盖)
|
||||||
func (sm *SystemSettingsManager) GetEffectiveConfig(groupConfig map[string]any) SystemSettings {
|
func (sm *SystemSettingsManager) GetEffectiveConfig(groupConfig datatypes.JSONMap) SystemSettings {
|
||||||
sm.mu.RLock()
|
sm.mu.RLock()
|
||||||
defer sm.mu.RUnlock()
|
defer sm.mu.RUnlock()
|
||||||
|
|
||||||
@@ -244,8 +245,19 @@ func (sm *SystemSettingsManager) GetEffectiveConfig(groupConfig map[string]any)
|
|||||||
if fieldName, ok := jsonToField[key]; ok {
|
if fieldName, ok := jsonToField[key]; ok {
|
||||||
fieldValue := v.FieldByName(fieldName)
|
fieldValue := v.FieldByName(fieldName)
|
||||||
if fieldValue.IsValid() && fieldValue.CanSet() {
|
if fieldValue.IsValid() && fieldValue.CanSet() {
|
||||||
if intVal, err := interfaceToInt(val); err == nil {
|
switch fieldValue.Kind() {
|
||||||
fieldValue.SetInt(int64(intVal))
|
case reflect.Int:
|
||||||
|
if intVal, err := interfaceToInt(val); err == nil {
|
||||||
|
fieldValue.SetInt(int64(intVal))
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
if strVal, ok := interfaceToString(val); ok {
|
||||||
|
fieldValue.SetString(strVal)
|
||||||
|
}
|
||||||
|
case reflect.Bool:
|
||||||
|
if boolVal, ok := interfaceToBool(val); ok {
|
||||||
|
fieldValue.SetBool(boolVal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,3 +372,24 @@ func interfaceToInt(val interface{}) (int, error) {
|
|||||||
return 0, fmt.Errorf("cannot convert to int: %v", val)
|
return 0, fmt.Errorf("cannot convert to int: %v", val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func interfaceToString(val interface{}) (string, bool) {
|
||||||
|
s, ok := val.(string)
|
||||||
|
return s, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func interfaceToBool(val interface{}) (bool, bool) {
|
||||||
|
switch v := val.(type) {
|
||||||
|
case bool:
|
||||||
|
return v, true
|
||||||
|
case string:
|
||||||
|
lowerV := strings.ToLower(v)
|
||||||
|
if lowerV == "true" || lowerV == "1" || lowerV == "on" {
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
if lowerV == "false" || lowerV == "0" || lowerV == "off" {
|
||||||
|
return false, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
@@ -7,19 +7,11 @@ import (
|
|||||||
|
|
||||||
// ConfigManager defines the interface for configuration management
|
// ConfigManager defines the interface for configuration management
|
||||||
type ConfigManager interface {
|
type ConfigManager interface {
|
||||||
// GetServerConfig() ServerConfig
|
|
||||||
// GetOpenAIConfig() OpenAIConfig
|
|
||||||
GetAuthConfig() AuthConfig
|
GetAuthConfig() AuthConfig
|
||||||
GetCORSConfig() CORSConfig
|
GetCORSConfig() CORSConfig
|
||||||
GetPerformanceConfig() PerformanceConfig
|
GetPerformanceConfig() PerformanceConfig
|
||||||
GetLogConfig() LogConfig
|
GetLogConfig() LogConfig
|
||||||
|
|
||||||
// Effective configuration methods that merge system settings
|
|
||||||
GetEffectiveServerConfig() ServerConfig
|
GetEffectiveServerConfig() ServerConfig
|
||||||
GetEffectiveOpenAIConfig(groupConfig map[string]any) OpenAIConfig
|
|
||||||
// GetEffectivePerformanceConfig() PerformanceConfig
|
|
||||||
// GetEffectiveLogConfig() LogConfig
|
|
||||||
|
|
||||||
Validate() error
|
Validate() error
|
||||||
DisplayConfig()
|
DisplayConfig()
|
||||||
ReloadConfig() error
|
ReloadConfig() error
|
||||||
|
Reference in New Issue
Block a user