1 Star 0 Fork 0

ltotal / ppw_gin

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
config.go 10.22 KB
一键复制 编辑 原始数据 按行查看 历史
ltotal 提交于 2024-05-06 16:14 . 添加额外封装的单独启动器
package pkg
import (
"bytes"
"encoding/json"
"fmt"
"github.com/mitchellh/mapstructure"
"github.com/philchia/agollo/v4"
"github.com/spf13/viper"
"reflect"
"regexp"
"strconv"
"strings"
"time"
)
type ConfigPkg struct {
*viper.Viper
ConfigPath string // 本地配置文件路径
ConfigType string // 本地配置文件类型
LocalStructMap map[string]interface{}
}
type ApolloPkg struct {
*viper.Viper
apClients map[string]agollo.Client // apollo客户端实例map
apAliasMap map[string]string // 使用viper读写apollo配置时的外部调用简写key关联
prefix string
}
type ApolloConfPkg struct {
Service string `mapstructure:"service"`
CacheDir string `mapstructure:"cache_dir"`
App map[string]AppConfigPkg `mapstructure:"app"`
}
type AppConfigPkg struct {
AppID string `mapstructure:"app_id"`
Cluster string `mapstructure:"cluster"`
Secret string `mapstructure:"secret"`
Namespaces []string `mapstructure:"namespaces"`
}
// ApUpdateCallback 外部指定的apollo配置变更后的回调,用于外部跟进处理配置变更后的后续逻辑
type ApUpdateCallback func(appId string, namespace string, changeKeys []string)
var Config = &ConfigPkg{}
var Apollo = &ApolloPkg{Viper: viper.New()}
var ApolloAlias = func(alias string) *ApolloPkg {
conf := &ApolloPkg{
Viper: Apollo.Viper,
}
if apKey, ok := Apollo.apAliasMap[alias]; ok {
conf.prefix = apKey
}
return conf
}
var ApolloStruct = func(alias string, outputStruct interface{}) error {
conf := ApolloAlias(alias)
mp := conf.GetStringMap("")
return decode(mp, &outputStruct)
}
// Init 配置初始化
func (c *ConfigPkg) Init(configPath string, configType string) {
c.ConfigPath = configPath
c.ConfigType = configType
}
// Read 读取本地配置文件
func (c *ConfigPkg) Read(file string) *ConfigPkg {
vp := &ConfigPkg{Viper: viper.New()}
vp.AddConfigPath(c.ConfigPath)
vp.SetConfigType(c.ConfigType)
vp.SetConfigName(file)
if err := vp.ReadInConfig(); err != nil {
panic(fmt.Sprintf("配置读取失败:%s", err))
}
return vp
}
// Update 动态写入本地配置文件
func (c *ConfigPkg) Update(updates map[string]interface{}, prefix string) error {
for key, value := range updates {
fullKey := key
if len(prefix) > 0 {
fullKey = prefix + "." + key
}
c.Set(fullKey, value)
}
return c.WriteConfigAs(c.ConfigFileUsed())
}
func (c *ConfigPkg) GetStringMapStruct(key string, outputStruct interface{}) error {
mp := c.Viper.GetStringMap(key)
return decode(mp, &outputStruct)
}
// Start 启动apollo配置中心,配置读写依赖viper
func (c *ApolloPkg) Start(apConf *ApolloConfPkg, callback ApUpdateCallback) error {
c.apClients = make(map[string]agollo.Client, len(apConf.App))
c.apAliasMap = make(map[string]string)
for _, conf := range apConf.App {
localConf := conf
namespaces := localConf.Namespaces
// 创建一个新的切片用于存储分割后的右侧部分
realNamespaces := make([]string, len(namespaces))
// 遍历切片中的每个元素
for _, namespace := range namespaces {
parts := strings.SplitN(namespace, ":", 2)
if len(parts) == 2 {
// 将分割后的右侧部分添加到新的切片中
realNamespaces = append(realNamespaces, parts[1])
c.apAliasMap[parts[0]] = fmt.Sprintf("%s.%s", localConf.AppID, parts[1])
} else {
realNamespaces = append(realNamespaces, namespace)
}
}
client := agollo.NewClient(&agollo.Conf{
AppID: localConf.AppID,
Cluster: localConf.Cluster,
CacheDir: apConf.CacheDir,
AccesskeySecret: localConf.Secret,
NameSpaceNames: realNamespaces,
MetaAddr: apConf.Service,
})
err := client.Start()
if err != nil {
return err
}
// 配置更新监听,支持apollo的 properties 和 json
client.OnUpdate(func(event *agollo.ChangeEvent) {
var changeKeys []string
for _, change := range event.Changes {
changeKeys = append(changeKeys, change.Key)
}
content := client.GetPropertiesContent(agollo.WithNamespace(event.Namespace))
if strings.HasSuffix(event.Namespace, ".json") {
re := regexp.MustCompile(`\\n|\\|\s|\t`)
content = re.ReplaceAllString(content, "")
}
updateConfigs := c.transApolloConfigFormat2Map(client, event.Namespace)
if len(updateConfigs) > 0 {
_ = c.Update(map[string]interface{}{
localConf.AppID + "." + event.Namespace: updateConfigs,
})
}
// 内部的OnUpdate监听仅负责对服务端变更配置的更新,其余处理走外部回调
if callback != nil {
callback(localConf.AppID, event.Namespace, changeKeys)
}
})
c.apClients[localConf.AppID] = client
updateMap := make(map[string]interface{})
for _, namespace := range realNamespaces {
subMap := c.transApolloConfigFormat2Map(client, namespace)
updateMap[conf.AppID+"."+namespace] = subMap
}
_ = c.Update(updateMap)
}
return nil
}
// Update 合并方式更新
func (c *ApolloPkg) Update(updates map[string]interface{}) error {
configBytes, err := json.Marshal(updates)
if err != nil {
return err
}
c.SetConfigType("json")
err = c.MergeConfig(bytes.NewBuffer(configBytes))
return err
}
// GetString 重写viper的配置获取方法,兼容键名前缀
func (c *ApolloPkg) GetString(key string) string {
return c.Viper.GetString(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetBool(key string) bool {
return c.Viper.GetBool(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetInt(key string) int {
return c.Viper.GetInt(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetInt32(key string) int32 {
return c.Viper.GetInt32(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetInt64(key string) int64 {
return c.Viper.GetInt64(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetUint(key string) uint {
return c.Viper.GetUint(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetUint16(key string) uint16 {
return c.Viper.GetUint16(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetUint32(key string) uint32 {
return c.Viper.GetUint32(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetUint64(key string) uint64 {
return c.Viper.GetUint64(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetFloat64(key string) float64 {
return c.Viper.GetFloat64(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetTime(key string) time.Time {
return c.Viper.GetTime(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetDuration(key string) time.Duration {
return c.Viper.GetDuration(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetIntSlice(key string) []int {
return c.Viper.GetIntSlice(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetStringSlice(key string) []string {
return c.Viper.GetStringSlice(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetStringMap(key string) map[string]any {
return c.Viper.GetStringMap(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetStringMapStruct(key string, outputStruct interface{}) error {
mp := c.Viper.GetStringMap(c.getFinalApKey(key))
return decode(mp, &outputStruct)
}
func (c *ApolloPkg) GetStringMapString(key string) map[string]string {
return c.Viper.GetStringMapString(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetStringMapStringSlice(key string) map[string][]string {
return c.Viper.GetStringMapStringSlice(c.getFinalApKey(key))
}
func (c *ApolloPkg) GetSizeInBytes(key string) uint {
return c.Viper.GetSizeInBytes(c.getFinalApKey(key))
}
func (c *ApolloPkg) getFinalApKey(key string) string {
// 如果配置文件没有根据格式配置每个namespace的前缀,则返回原生的key
if c.prefix == "" {
return key
}
// 如果键为空,则直接返回组装后的前缀作为key名,指向整个配置对象
if key == "" {
return c.prefix
}
return fmt.Sprintf("%s.%s", c.prefix, key)
}
// transApolloConfigFormat2Map 将apollo的properties、json配置格式转换为map[string]interface{}
func (c *ApolloPkg) transApolloConfigFormat2Map(client agollo.Client, namespace string) map[string]interface{} {
subMap := make(map[string]interface{})
namespaceOpt := agollo.WithNamespace(namespace)
keys := client.GetAllKeys(namespaceOpt)
for _, key := range keys {
value := client.GetString(key, namespaceOpt)
if strings.HasSuffix(namespace, ".json") {
var data map[string]interface{}
err := json.Unmarshal([]byte(value), &data)
if err != nil {
panic(err)
}
for subKey, subValue := range data {
subMap[subKey] = subValue
}
break
} else {
var tmp []interface{}
if err := json.Unmarshal([]byte(value), &tmp); err == nil {
subMap[key] = tmp
} else {
subMap[key] = value
}
}
}
return subMap
}
func decode(input map[string]interface{}, output interface{}) error {
decoderConfig := &mapstructure.DecoderConfig{
DecodeHook: mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToSliceHookFunc(","),
stringToVariousTypesHook(),
),
Result: output,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
return fmt.Errorf("结构decoder创建出错: %w", err)
}
if err = decoder.Decode(input); err != nil {
return fmt.Errorf("结构编译出错: %w", err)
}
return nil
}
// stringToVariousTypesHook 处理字符串到多种类型的转换
func stringToVariousTypesHook() mapstructure.DecodeHookFuncType {
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
value, ok := data.(string)
if !ok {
return data, nil
}
// 根据目标类型进行转换
switch t.Kind() {
case reflect.String:
return value, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.ParseInt(value, 10, t.Bits())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.ParseUint(value, 10, t.Bits())
case reflect.Bool:
return strconv.ParseBool(value)
case reflect.Float32, reflect.Float64:
if floatValue, err := strconv.ParseFloat(value, t.Bits()); err != nil {
return nil, err
} else if t.Kind() == reflect.Float32 {
return float32(floatValue), nil
} else {
return floatValue, nil
}
case reflect.Slice:
// 假设目标切片类型是 []string
var result []string
if err := json.Unmarshal([]byte(value), &result); err != nil {
return nil, err
}
return result, nil
default:
return nil, fmt.Errorf("不支持的json数据解析 %s", t.Kind())
}
}
}
1
https://gitee.com/ltotal/ppw_gin.git
git@gitee.com:ltotal/ppw_gin.git
ltotal
ppw_gin
ppw_gin
v0.8.2

搜索帮助