Эх сурвалжийг харах

feat(config): 支持环境变量和JSON格式配置加载

- 新增环境变量配置源,支持前缀过滤和嵌套结构解析
- 实现环境变量值的类型自动转换(布尔值、整数、浮点数)
- 添加JSON格式配置文件的支持,使用sonic库进行解析
- 优化配置合并逻辑,确保热更新时能正确解析不同类型配置
- 完善配置加载流程,在每次
dcsunny 5 өдөр өмнө
parent
commit
9076168859
3 өөрчлөгдсөн 110 нэмэгдсэн , 8 устгасан
  1. 80 0
      config/env.go
  2. 28 8
      config/load.go
  3. 2 0
      config/source.go

+ 80 - 0
config/env.go

@@ -0,0 +1,80 @@
+package config
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/go-kratos/kratos/v2/config"
+	"github.com/go-kratos/kratos/v2/config/env"
+)
+
+type EnvSource struct {
+	Prefixes []string
+}
+
+func (e *EnvSource) NewSource() (config.Source, error) {
+	source := env.NewSource(e.Prefixes...)
+	return source, nil
+}
+
+func (e *EnvSource) Validate() bool {
+	if len(e.Prefixes) == 0 {
+		return false
+	}
+	return true
+}
+
+func (e *EnvSource) String() string {
+	return fmt.Sprintf("env source prefixes:%v", e.Prefixes)
+}
+
+// buildNestedMap 将环境变量的键值对转换为嵌套的 map 结构
+// 例如: "SERVER_PORT" -> "8080" 转换为 {"server": {"port": "8080"}}
+func buildNestedMap(key, value string) map[string]any {
+	result := make(map[string]any)
+	current := result
+
+	// 将大写的键名转换为小写,并用下划线分割
+	parts := strings.Split(strings.ToLower(key), "_")
+
+	// 构建嵌套结构
+	for i, part := range parts {
+		if i == len(parts)-1 {
+			// 最后一个部分,设置值
+			current[part] = convertEnvValue(value)
+		} else {
+			// 中间部分,创建嵌套 map
+			if _, exists := current[part]; !exists {
+				current[part] = make(map[string]any)
+			}
+			current = current[part].(map[string]any)
+		}
+	}
+
+	return result
+}
+
+// convertEnvValue 尝试将环境变量的字符串值转换为合适的类型
+func convertEnvValue(value string) any {
+	// 布尔值转换
+	if value == "true" {
+		return true
+	}
+	if value == "false" {
+		return false
+	}
+
+	// 整数转换
+	if i, err := strconv.Atoi(value); err == nil {
+		return i
+	}
+
+	// 浮点数转换
+	if f, err := strconv.ParseFloat(value, 64); err == nil {
+		return f
+	}
+
+	// 默认返回字符串
+	return value
+}

+ 28 - 8
config/load.go

@@ -6,6 +6,7 @@ import (
 
 	"dario.cat/mergo"
 	"git.ikuban.com/server/yaml"
+	"github.com/bytedance/sonic"
 	"github.com/elliotchance/orderedmap/v3"
 	"github.com/go-kratos/kratos/v2/config"
 )
@@ -46,23 +47,40 @@ func Load(sources []Source, bc any) error {
 			if !ok {
 				return fmt.Errorf("unknown config key,key:%s", kv.Key)
 			}
-
-			next := map[string]any{}
-			err := yaml.Unmarshal(kv.Value, next)
-			if err != nil {
-				return err
+			if kv.Format == "" {
+				// 处理环境变量配置(Format 为空)
+				// 环境变量的值已经是简单的字符串,不需要额外解析
+				// 将环境变量值直接设置到 sourceMap 中对应键的位置
+				// 这里需要将简单的键值对转换为嵌套结构
+				envMap := buildNestedMap(kv.Key, string(kv.Value))
+				sourceMap.Set(kv.Key, envMap)
+			} else if kv.Format == Yaml.String() {
+				next := map[string]any{}
+				err := yaml.Unmarshal(kv.Value, next)
+				if err != nil {
+					return err
+				}
+				sourceMap.Set(kv.Key, convertMap(next))
+			} else if kv.Format == Json.String() {
+				next := map[string]any{}
+				err := sonic.Unmarshal(kv.Value, &next)
+				if err != nil {
+					return err
+				}
+				sourceMap.Set(kv.Key, convertMap(next))
 			}
 
-			sourceMap.Set(kv.Key, convertMap(next))
-
+			// 在每个配置源处理完成后,立即合并并解析到目标结构体
+			// 这样确保配置热更新也能正常工作
 			merged := make(map[string]any)
 			for c := range sourceMap.Values() {
-				err = mergo.Merge(&merged, c, mergo.WithOverride)
+				err := mergo.Merge(&merged, c, mergo.WithOverride)
 				if err != nil {
 					return err
 				}
 			}
 
+			// 将合并后的配置序列化为 YAML 再解析到目标结构体
 			b, err := yaml.Marshal(merged)
 			if err != nil {
 				return err
@@ -73,6 +91,7 @@ func Load(sources []Source, bc any) error {
 				return err
 			}
 
+			// 执行默认配置函数
 			if defaultCfg != nil {
 				defaultCfg(bc)
 			}
@@ -83,6 +102,7 @@ func Load(sources []Source, bc any) error {
 	if err := c.Load(); err != nil {
 		return err
 	}
+
 	return nil
 }
 

+ 2 - 0
config/source.go

@@ -14,10 +14,12 @@ type Format string
 
 const (
 	Yaml Format = "yaml"
+	Json Format = "json"
 )
 
 var formatMap = map[Format]string{
 	Yaml: "yaml",
+	Json: "json",
 }
 
 func (f Format) String() string {