package config import ( "context" "encoding/json" "fmt" "io/ioutil" "os" "strings" "time" "github.com/fsnotify/fsnotify" "go.etcd.io/etcd/client" ) var Conf *Configure const ConfigPath = "conf/common.json" // mysql 配置 type MysqlConfig struct { User string `json:"user"` Password string `json:"password"` Addr string `json:"addr"` DefaultDB string `json:"default_db"` Charset string `json:"charset"` MaxIdle json.Number `json:"max_idle"` MaxConn json.Number `json:"max_conn"` } // redis 配置 type RedisConfig struct { Addrs string `json:"addrs"` Password string `json:"password"` DefaultDB json.Number `json:"default_db"` PoolSize json.Number `json:"pool_size"` MinIdleConns json.Number `json:"min_idle_conns"` MaxRetries json.Number `json:"max_retries"` IsCluster string `json:"is_cluster"` } type ElasticConfig struct { Addr string `json:"addr"` Sniff string `json:"sniff"` } type LogConfig struct { MaxSize json.Number `json:"max_size"` MaxBackups json.Number `json:"max_backups"` MaxAge json.Number `json:"max_age"` DisableStacktrace string `json:"disable_stacktrace"` } type RPCNode struct { Scheme string `json:"scheme"` Name string `json:"name"` UpdateInterval json.Number `json:"update_interval"` MysqlDB string `json:"mysql_db"` RedisDB json.Number `json:"redis_db"` Log LogConfig `json:"log"` ServiceName string `json:"service_name"` ServicePort json.Number `json:"service_port"` } type RPCConfig struct { BasePath string `json:"base_path"` AuthCheck RPCNode `json:"gd_auth_check"` Crontab RPCNode `json:"gd_crontab"` } type RateLimitConf struct { RateLimitChannel string `json:"rate_limit_channel"` RescueTickerTime json.Number `json:"rescue_ticker_time"` } type DingDingConfig struct { AccessToken string `json:"access_token"` Secret string `json:"secret"` } type ThirdPartConfig struct { ZjcAppKey string `json:"zjc_app_key"` ZjcAppSecret string `json:"zjc_app_secret"` ZcrkEngineUserName string `json:"zcrk_engine_user_name"` ZcrkEnginePasswrod string `json:"zcrk_engine_passwrod"` ZcrkUserName string `json:"zcrk_user_name"` ZcrkPasswrod string `json:"zcrk_passwrod"` ZcrkUserNameOne string `json:"zcrk_user_name_one"` ZcrkPasswrodOne string `json:"zcrk_passwrod_one"` HttpProxyUrl string `json:"http_proxy_url" description:"数据源正向代理url"` EdAppId string `json:"ed_app_id"` // eds16787f343c1676853 EdAppSecret string `json:"ed_app_secret"` // 3d3f47bef850c5a2b0c15d4b94b6b3a3 C300Token string `json:"c_300_token"` // be318b5cf56c65e9172b8636699fcf17 ArcbangAppId string `json:"arcbang_app_id"` // 476795160986910721 ArcbangAppKey string `json:"arcbang_app_key"` // 897c86f2779341e88e38b3eb9acb384f NjKey string `json:"nj_key"` // E78E1F76-A9FE-4687-A3EB-198B7628D959 DbDwkey string `json:"db_dwkey"` // chejinjia DbToken string `json:"db_token"` // chejinjia2017030 DbEncKey string `json:"db_enc_key"` // 91724890 ScViolationDwkey string `json:"sc_violation_dwkey"` // be2hjdeii1f90c5c55bfa0af7 ScViolationToken string `json:"sc_violation_token"` // be2hjdeii1f90c5c55bfa0af7 ScViolationEncKey string `json:"sc_violation_enc_key"` // 33725169 MojiAppKey string `json:"moji_app_key"` // 25270402 MojiAppSecret string `json:"moji_app_secret"` // bc1ec844f9a516919c85c09b1c75d682 MojiAppCode string `json:"moji_app_code"` // fd4df24553a04d08a71a2566203a37db LcbAppCode string `json:"lcb_app_code"` // 3101 LcbSecret string `json:"lcb_secret"` // b9b548a34220f097e6c22ca933f76a8e JhAppKey string `json:"jh_app_key"` // 1ef0aa20c3d4cbe71f4a799122625bb9 ZxCompanyKey string `json:"zx_company_key"` // 001061808271603202J1I3Q1H0T2L ZxCompanyCode string `json:"zx_company_code"` // 65db6e13ecef31a54642df747986472a MsToken string `json:"ms_token"` // 81ed9993bcad8cd0d1f55f9565104b76 ZqyKey string `json:"zqy_key"` // CETRo4r-KIkk05-uoyiE-20V7NU-Ab36 ZhUser string `json:"zh_user"` // tangmimi ZhPwd string `json:"zh_pwd"` // E37A41FE3B1843E1AC46E6589CD76412 ZzxAppKey string `json:"zzx_app_key"` ZzxUser string `json:"zzx_user"` ZjtAuthorizationCode string `json:"zjt_authorization_code"` // LkPw8252672h4NmF ZjtCryptoKey string `json:"zjt_crypto_key"` // Wuz2162IF9O60X58 ZjtUserNo string `json:"zjt_user_no"` // SYZNCQCAR DjDwkey string `json:"dj_dwkey"` // wdhyiylhotoabycqtq DjToken string `json:"dj_token"` // MIYYKOUKIYKKKYWH081YOIYLIOJ DjEncKey string `json:"dj_enc_key"` // 91724890 DjScDwkey string `json:"dj_sc_dwkey"` // whsc00081890080870 DjScToken string `json:"dj_sc_token"` // MIYYKOUKIYKKKYWH081YOIYLIOJ DjScEncKey string `json:"dj_sc_enc_key"` // 91724890 DjAppid string `json:"dj_appid"` // 9225417112424 DjSecrtkey string `json:"dj_secrtkey"` // IYYTOKYLHBTLIYKYQUYLHYLIY DjScAppid string `json:"dj_sc_appid"` // 5524892445452455145 DjScSecrtkey string `json:"dj_sc_secrtkey"` // YUOUIYOIYKHMJHUYUIOYLIYIKYIKYIJU SpyUserName string `json:"spy_user_name"` SpyPassWord string `json:"spy_pass_word"` JisuAppkey string `json:"jisu_appkey"` BjcUserName string `json:"bjc_user_name"` BjcPwd string `json:"bjc_pwd"` BjcLoginUrl string `json:"bjc_login_url"` DjVinAppid string `json:"dj_vin_appid"` // 9225417112424 DjVinSecrtkey string `json:"dj_vin_secrtkey"` // IYYTOKYLHBTLIYKYQUYLHYLIY C003AppCode string `json:"c003_app_code"` DingTalkWebhook string `json:"ding_talk_webhook"` HystrixLoopTime json.Number `json:"hystrix_loop_time"` AdmAppkey string `json:"adm_appkey"` HystrixPublishChannel string `json:"hystrix_publish_channel"` DingDing DingDingConfig `json:"ding_ding"` RobotUrl string `json:"robot_url"` } type Configure struct { RunMode string `json:"run_mode"` AppKey string `json:"app_key"` AppSecret string `json:"app_secret"` Mysql MysqlConfig `json:"mysql"` Redis RedisConfig `json:"redis"` Elastic ElasticConfig `json:"elastic"` Rpc RPCConfig `json:"rpc"` GdInnerAccount string `json:"gd_inner_account"` RateLimit RateLimitConf `json:"rate_limit"` ThirdPart ThirdPartConfig `json:"third_part"` } func watchEtcd(keysAPI client.KeysAPI, index uint64, runmode, key string, cli client.Client) { basePath := fmt.Sprintf("/%s/config", runmode) watcherOptions := &client.WatcherOptions{ AfterIndex: index, Recursive: true, } watcher := keysAPI.Watcher(basePath, watcherOptions) for { r, err := watcher.Next(context.Background()) if err != nil || r == nil { break } if r.Node != nil && r.PrevNode != nil { if r.Node.Key == r.PrevNode.Key && (r.Node.Value != r.PrevNode.Value) { reloadEtcd(runmode, key, cli) } } if r.Node != nil && r.PrevNode == nil { reloadEtcd(runmode, key, cli) } } } func reloadEtcd(runmode, key string, cli client.Client) { keysAPI := client.NewKeysAPI(cli) basePath := fmt.Sprintf("/%s/config", runmode) if resp, err := keysAPI.Get(context.Background(), basePath, &client.GetOptions{ Recursive: true, }); err == nil && resp != nil && resp.Node != nil { conf := &Configure{} value := getNodeData(key, resp.Node) if jsonStr, err := json.Marshal(value); err == nil { if err := json.Unmarshal(jsonStr, &conf); err == nil { *Conf = *conf return } else { fmt.Printf("json Unmarshal failed. error:%s", err) } } else { fmt.Printf("json Marshal failed. error:%s", err) } } else { fmt.Printf("get %s failed. error:%s", basePath, err) } } // key 为加密密钥 func GetConfig(runmode, key string, cli client.Client) *Configure { keysAPI := client.NewKeysAPI(cli) basePath := fmt.Sprintf("/%s/config", runmode) if resp, err := keysAPI.Get(context.Background(), basePath, &client.GetOptions{ Recursive: true, }); err == nil && resp != nil && resp.Node != nil { Conf = &Configure{} value := getNodeData(key, resp.Node) if jsonStr, err := json.Marshal(value); err == nil { if err := json.Unmarshal(jsonStr, &Conf); err == nil { go watchEtcd(keysAPI, resp.Index, runmode, key, cli) return Conf } else { fmt.Printf("json Unmarshal failed. error:%s", err) } } else { fmt.Printf("json Marshal failed. error:%s", err) } } else { fmt.Printf("get %s failed. error:%s", basePath, err) os.Exit(1) } return nil } // 递归取出node的叶子节点值 func getNodeData(key string, head *client.Node) (value interface{}) { s0 := strings.Split(head.Key, "/") len0 := len(s0) if len0 == 0 { return } if head.Dir { mapData := map[string]interface{}{} for _, node := range head.Nodes { s1 := strings.Split(node.Key, "/") len1 := len(s1) if len1 == 0 { break } mapData[s1[len1-1]] = getNodeData(key, node) } value = mapData } else { if key != "" && head.Value != "" { if bytesData, err := Base64URLDecode(head.Value); err != nil { fmt.Printf("Base64URLDecode(%s) failed. error:%s", head.Value, err) os.Exit(1) } else { if data, err := AesDecrypt(bytesData, []byte(key)); err != nil { fmt.Printf("AesDecrypt failed. error:%s", err) os.Exit(1) } else { value = string(data) } } } else { // 无加密,直接取值 value = head.Value } } return } // 适配k8s 方式 // 公共配置会以configmap的方式映射到容器的conf/common.json中 func GetConfigForK8s() *Configure { buffer, err := ioutil.ReadFile(ConfigPath) if err != nil { fmt.Printf("get %s failed. error:%s", ConfigPath, err) return nil } Conf = &Configure{} if err := json.Unmarshal(buffer, Conf); err != nil { fmt.Printf("json Unmarshal failed. error:%s", err) return nil } go watchConfigFileForK8s() return Conf } func ReloadConfigForK8s() { buffer, err := ioutil.ReadFile(ConfigPath) if err != nil { fmt.Printf("get %s failed. error:%s", ConfigPath, err) } confTmp := &Configure{} if err := json.Unmarshal(buffer, confTmp); err != nil { fmt.Printf("json Unmarshal failed. error:%s", err) } Conf = confTmp } // 判断路径文件/文件夹是否存在 func Exists(path string) bool { _, err := os.Stat(path) if err != nil { if os.IsExist(err) { return true } return false } return true } func watchConfigFileForK8s() { fileExist := true watch, err := fsnotify.NewWatcher() if err != nil { fmt.Printf("new file watcher failed\n\n") os.Exit(1) } defer watch.Close() /*err = watch.Add(ConfigPath) if err != nil { fmt.Printf("add file watcher failed\n\n") os.Exit(1) }*/ for { // 判断文件是否存在 if !Exists(ConfigPath) { time.Sleep(10 * time.Second) fileExist = false continue } else { watch.Remove(ConfigPath) watch.Add(ConfigPath) if !fileExist { // 文件重新创建 ReloadConfigForK8s() } fileExist = true } select { case ev := <-watch.Events: { fmt.Println("op : ", ev.Op) ReloadConfigForK8s() } case err := <-watch.Errors: { fmt.Println("error : ", err) continue } } } }