掘金 后端 ( ) • 2024-04-24 20:38

这里是weihubeats,觉得文章不错可以关注公众号小奏技术

背景

最近有个golang服务要接入apollo,所以要研究下apollogolang接入方式

发现官方提供了agollogolang sdk

实际维护频率也处于比较低频的状态

接入步骤

  1. 引入依赖
go get -u github.com/apolloconfig/agollo/v4@latest
  1. 从apollo读取配置信息

我这里先总结下大致步骤,后面会给出完整代码

a. 构建*config.AppConfig

b. 通过*config.AppConfig读取到一个string对象(properties),即所有配置文件(官方不支持yaml的直接读取)

c. 将properties转换为map[string]interface{}对象

2-4. 将map[string]interface{}对象转换为yaml对象

如果只需要读取properties文件,可以直接使用agollo提供的GetContent方法,之后无需进行其他转换

完整代码

 const (
	envApolloAddr    = "APOLLO_ADDR"
	envApolloAppID   = "APOLLO_APP_ID"
	envApolloCluster = "APOLLO_CLUSTER"

	defaultApolloAddr = "http://127.0.0.1:8080"
	defaultAppID      = "小奏技术"
	defaultNamespace  = "xiaozou.yaml"
)

func readConfigByApollo() *Config {
	c := getApolloConfig()

	client, err := agollo.StartWithConfig(func() (*config.AppConfig, error) {
		return c, nil
	})
	if err != nil {
		panic(err)
	}
	configYAML := client.GetConfig(defaultNamespace).GetContent()

	propMap, err := propertiesToMap(configYAML)
	if err != nil {
		panic(err)
	}
	// 将map转换为YAML字节流
	yamlBytes, err := yaml.Marshal(propMap)
	if err != nil {
		panic(err)
	}

	var apolloConfig *Config
	err = yaml.Unmarshal(yamlBytes, &apolloConfig)
	if err != nil {
		_ = fmt.Errorf(err.Error())
		panic(err)
	}

	// 打印解析后的配置,或者进行后续处理
	fmt.Printf("Config: %+v\n", apolloConfig)

	return apolloConfig

}

func getApolloConfig() *config.AppConfig {
	apolloAddr := os.Getenv(envApolloAddr)
	if apolloAddr == "" {
		apolloAddr = defaultApolloAddr
	}
	fmt.Printf("apollo addr: %s\n", apolloAddr)

	appID := os.Getenv(envApolloAppID)

	if appID == "" {
		appID = defaultAppID
	}

	namespace := os.Getenv(envApolloCluster)
	if namespace == "" {
		namespace = defaultNamespace
	}
	c := &config.AppConfig{
		AppID:          appID,
		Cluster:        "default",
		NamespaceName:  namespace,
		IP:             apolloAddr,
		IsBackupConfig: true,
		Secret:         "",
	}
	return c
}

func propertiesToMap(properties string) (map[string]interface{}, error) {
	result := make(map[string]interface{})
	lines := strings.Split(properties, "\n")
	for _, line := range lines {
		if line == "" || strings.HasSuffix(line, "#") {
			continue
		}
		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("invalid property format: %s", line)
		}
		// 处理嵌套
		value := parseValue(parts[1])
		keys := strings.Split(parts[0], ".")
		lastMap := result
		for i, key := range keys {
			if i == len(keys)-1 {
				lastMap[key] = value
			} else {
				if _, ok := lastMap[key]; !ok {
					lastMap[key] = make(map[string]interface{})
				}
				lastMap = lastMap[key].(map[string]interface{})
			}
		}
	}
	return result, nil

}

func parseValue(str string) interface{} {

	if str == "true" || str == "True" {
		return true
	} else if str == "false" || str == "False" {
		return false
	}
	if i, err := strconv.Atoi(str); err == nil {
		return i
	}

	if strings.HasPrefix(str, "[") && strings.HasSuffix(str, "]") {
		str = str[1 : len(str)-1] // 移除括号
		if str == "" {
			return []string{}
		}
		// 分割字符串,并去除每个元素周围的空格
		elements := strings.Split(str, ",")
		for i, el := range elements {
			elements[i] = strings.TrimSpace(el)
		}
		return elements
	}

	// 返回原始字符串
	return str

}

上面的Config结构体如下

type Config struct {
	Server  *Server                   `yaml:"server"`
	Log     *Log                      `yaml:"log"`
	Topic   *Topic                    `yaml:"topic"`
	Configs map[string]RocketmqConfig `yaml:"rocketmq"`
}

yaml处理使用是https://github.com/go-yaml/yaml