package utils import ( "crypto/md5" "encoding/hex" "fmt" "net/url" "os" "strings" "sync" "time" "github.com/fsnotify/fsnotify" "github.com/spf13/viper" ) // 外部传入Nacos参数结构体 type NacosConfig struct { Hosts []string NamespaceId string Group string ConfigID string } var ( runViper = viper.New() loadOnce sync.Once nacosCfg NacosConfig currentMd5 string ) func GetConfig(key string) interface{} { return runViper.Get(key) } func GetConfigString(key string) string { return runViper.GetString(key) } func GetConfigInt(key string) int { return runViper.GetInt(key) } func GetConfigInt64(key string) int64 { return runViper.GetInt64(key) } func GetConfigBool(key string) bool { return runViper.GetBool(key) } // 初始化入口 func InitConfig(cfg NacosConfig) { loadOnce.Do(func() { nacosCfg = cfg if len(nacosCfg.Hosts) == 0 || nacosCfg.ConfigID == "" { panic("nacos Hosts 与 ConfigID 不能为空") } // 尝试拉取远端配置 pullErr := fetchNacosAndSaveRunToml() if pullErr != nil { fmt.Printf("【警告】Nacos远程拉取失败: %v\n尝试加载本地run.toml缓存启动\n", pullErr) } // 加载配置,容错:无缓存文件不直接panic,打印提示 loadRunToml() // 后台定时同步 startFixedIntervalSync() }) } // 标准URL解析,彻底解决重复拼接http://BUG func getNacosBaseUrl() string { addr := nacosCfg.Hosts[0] // 判断开头有没有http/https协议 if strings.HasPrefix(addr, "http://") || strings.HasPrefix(addr, "https://") { return addr } // 无协议强制加上 http:// return "http://" + addr } // 请求Nacos配置接口 func fetchNacosConfig() (content string, md5Str string, err error) { baseUrl := getNacosBaseUrl() fullUrl := fmt.Sprintf( "%s/nacos/v1/cs/configs?dataId=%s&group=%s&tenant=%s", baseUrl, url.QueryEscape(nacosCfg.ConfigID), url.QueryEscape(nacosCfg.Group), url.QueryEscape(nacosCfg.NamespaceId), ) body, err := Curl(fullUrl).Get("") if err != nil { return "", "", err } hash := md5.Sum([]byte(body)) md5Str = hex.EncodeToString(hash[:]) return body, md5Str, nil } // MD5比对写入本地文件 func fetchNacosAndSaveRunToml() error { content, newMd5, err := fetchNacosConfig() if err != nil { return err } if currentMd5 == newMd5 { return nil } err = os.WriteFile("run.toml", []byte(content), 0644) if err != nil { return fmt.Errorf("写入run.toml失败: %w", err) } currentMd5 = newMd5 fmt.Println("Nacos配置拉取成功,已缓存至本地run.toml") return nil } // 加载本地配置,增加降级容错 func loadRunToml() { runViper.SetConfigFile("run.toml") err := runViper.ReadInConfig() if err != nil { fmt.Printf("run.toml本地缓存不存在: %v\n当前仅内置默认密钥、超时配置生效,数据库等远端配置缺失,服务无法正常运行\n", err) return } runViper.WatchConfig() runViper.OnConfigChange(func(e fsnotify.Event) { fmt.Println("run.toml文件变更,自动重载配置") _ = runViper.ReadInConfig() }) } // 5分钟定时同步远端配置 func startFixedIntervalSync() { go func() { ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop() for range ticker.C { err := fetchNacosAndSaveRunToml() if err != nil { fmt.Printf("定时同步Nacos异常: %v\n", err) } } }() }