package config import ( "fmt" "os" "path/filepath" "sync" "gopkg.in/yaml.v2" ) // Config 应用配置 type Config struct { Database DBConfig `yaml:"database"` Redis RedisConfig `yaml:"redis"` Doris DorisConfig `yaml:"doris"` Auth Auth `yaml:"auth"` Service Service `yaml:"service"` HTTP HTTPConfig `yaml:"http"` // 新增 HTTP 配置 } // HTTPConfig HTTP客户端配置 type HTTPConfig struct { Timeout int `yaml:"timeout"` // 请求超时时间(秒) MaxIdleConns int `yaml:"max_idle_conns"` // 最大空闲连接数 MaxIdleConnsPerHost int `yaml:"max_idle_conns_per_host"` // 每个主机的最大空闲连接数 IdleConnTimeout int `yaml:"idle_conn_timeout"` // 空闲连接超时时间(秒) MaxConnsPerHost int `yaml:"max_conns_per_host"` // 每个主机的最大连接数 DisableCompression bool `yaml:"disable_compression"` // 是否禁用压缩 DisableKeepAlives bool `yaml:"disable_keep_alives"` // 是否禁用长连接 } // DBConfig 数据库配置 type DBConfig struct { Type string `yaml:"type"` Host string `yaml:"host"` Port int `yaml:"port"` Username string `yaml:"username"` Password string `yaml:"password"` Database string `yaml:"database"` MaxOpenConns int `yaml:"max_open_conns"` MaxIdleConns int `yaml:"max_idle_conns"` ConnMaxLifetime int `yaml:"conn_max_lifetime"` // 单位:秒 } // RedisConfig Redis配置 type RedisConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` Password string `yaml:"password"` DB int `yaml:"db"` PoolSize int `yaml:"pool_size"` // 连接超时和读写超时(秒) DialTimeout int `yaml:"dial_timeout"` ReadTimeout int `yaml:"read_timeout"` WriteTimeout int `yaml:"write_timeout"` // 连接池配置 IdleTimeout int `yaml:"idle_timeout"` // 空闲连接超时(秒) MaxConnAge int `yaml:"max_conn_age"` // 连接最大存活时间(秒) } // DorisConfig Doris配置 type DorisConfig struct { // FE节点配置 FEHost string `yaml:"fe_host"` FEPort int `yaml:"fe_port"` FEUsername string `yaml:"fe_username"` FEPassword string `yaml:"fe_password"` // MySQL协议配置 MySQLHost string `yaml:"mysql_host"` MySQLPort int `yaml:"mysql_port"` // 连接池配置 MaxOpenConns int `yaml:"max_open_conns"` MaxIdleConns int `yaml:"max_idle_conns"` ConnMaxLifetime int `yaml:"conn_max_lifetime"` // 单位:秒 // Stream Load配置 StreamLoadTimeout int `yaml:"stream_load_timeout"` // 秒 BatchSize int `yaml:"batch_size"` // 批量大小 } // Auth 认证配置 type Auth struct { Token string `yaml:"token"` } // Service 微服务配置 type Service struct { ServiceName string `yaml:"service_name"` Port int `yaml:"port"` ReadTimeout int `yaml:"read_timeout"` WriteTimeout int `yaml:"write_timeout"` IdleTimeout int `yaml:"idle_timeout"` TrustedProxies string `yaml:"trusted_proxies"` } // 导出接口,防止外部修改 type IConfig interface { GetDatabase() DBConfig GetRedis() RedisConfig GetDoris() DorisConfig GetAuth() Auth GetService() Service GetHTTP() HTTPConfig // 新增获取HTTP配置方法 IsDatabaseConfigured() bool IsRedisConfigured() bool IsDorisConfigured() bool IsHTTPConfigured() bool // 新增检查HTTP配置方法 } // 实现接口的具体类型 type configWrapper struct { config *Config } func (cw *configWrapper) GetDatabase() DBConfig { return cw.config.Database } func (cw *configWrapper) GetRedis() RedisConfig { return cw.config.Redis } func (cw *configWrapper) GetDoris() DorisConfig { return cw.config.Doris } func (cw *configWrapper) GetAuth() Auth { return cw.config.Auth } func (cw *configWrapper) GetService() Service { return cw.config.Service } func (cw *configWrapper) GetHTTP() HTTPConfig { return cw.config.HTTP } func (cw *configWrapper) IsDatabaseConfigured() bool { db := cw.config.Database return db.Type != "" && db.Host != "" && db.Port > 0 && db.Username != "" && db.Database != "" } func (cw *configWrapper) IsRedisConfigured() bool { redis := cw.config.Redis return redis.Host != "" && redis.Port > 0 } func (cw *configWrapper) IsDorisConfigured() bool { doris := cw.config.Doris // 检查FE配置(HTTP API方式) return doris.FEHost != "" && doris.FEPort > 0 } func (cw *configWrapper) IsHTTPConfigured() bool { httpCfg := cw.config.HTTP return httpCfg.Timeout > 0 } func (cw *configWrapper) IsAuthConfigured() bool { return cw.config.Auth.Token != "" } var ( instance IConfig once sync.Once initErr error ) // GetConfig 获取配置单例实例 func GetConfig() IConfig { once.Do(func() { config, err := loadConfig() if err != nil { initErr = err // 创建一个空的配置实例,避免nil指针 instance = &configWrapper{config: &Config{}} return } instance = &configWrapper{config: config} }) return instance } // GetInitError 获取初始化错误(如果有) func GetInitError() error { return initErr } // loadConfig 加载配置文件 func loadConfig() (*Config, error) { configFile, err := findConfigFile() if err != nil { return nil, err } fmt.Printf("✅ Using config file: %s\n", configFile) // 先创建带默认值的配置 config := &Config{ Service: Service{ ServiceName: "myService", Port: 8080, ReadTimeout: 30, WriteTimeout: 30, IdleTimeout: 60, TrustedProxies: "", }, Redis: RedisConfig{ Port: 6379, DB: 0, PoolSize: 10, DialTimeout: 5, ReadTimeout: 3, WriteTimeout: 3, IdleTimeout: 300, MaxConnAge: 0, // 0表示不限制 }, Doris: DorisConfig{ FEPort: 8030, MySQLPort: 9030, MaxOpenConns: 20, MaxIdleConns: 10, ConnMaxLifetime: 3600, StreamLoadTimeout: 30, BatchSize: 1000, }, HTTP: HTTPConfig{ Timeout: 30, MaxIdleConns: 100, MaxIdleConnsPerHost: 10, IdleConnTimeout: 90, MaxConnsPerHost: 0, // 0表示不限制 DisableCompression: false, DisableKeepAlives: false, }, } // 读取配置文件 data, err := os.ReadFile(configFile) if err != nil { return nil, fmt.Errorf("failed to read config file %s: %v", configFile, err) } err = yaml.Unmarshal(data, &config) if err != nil { return nil, fmt.Errorf("failed to parse config file: %v", err) } return config, nil } // findConfigFile 查找配置文件 func findConfigFile() (string, error) { // 1. 首先尝试可执行文件同目录 exePath, err := os.Executable() if err == nil { exeDir := filepath.Dir(exePath) configFile := filepath.Join(exeDir, "db.yaml") if _, err := os.Stat(configFile); err == nil { return configFile, nil } } // 2. 尝试环境变量指定的路径 envConfigPath := os.Getenv("DB_CONFIG_PATH") if envConfigPath != "" { if _, err := os.Stat(envConfigPath); err == nil { return envConfigPath, nil } return "", fmt.Errorf("DB_CONFIG_PATH file not found: %s", envConfigPath) } // 3. 如果都没有找到,返回错误 exeDir := "unknown" if exePath, err := os.Executable(); err == nil { exeDir = filepath.Dir(exePath) } return "", fmt.Errorf(`no configuration file found! Tried locations: 1. Executable directory: %s/db.yaml 2. Environment variable: DB_CONFIG_PATH Solutions: - Place db.yaml in the same directory as the executable - Or set DB_CONFIG_PATH environment variable to config file path Example: export DB_CONFIG_PATH=/path/to/your/db.yaml`, exeDir) }