github.com/zly-app/zapp@v1.3.3/config/apollo.go (about)

     1  /*
     2  -------------------------------------------------
     3     Author :       zlyuancn
     4     date:         2020/12/18
     5     Description :
     6  -------------------------------------------------
     7  */
     8  
     9  package config
    10  
    11  import (
    12  	"errors"
    13  	"fmt"
    14  	"os"
    15  	"strings"
    16  
    17  	"github.com/spf13/viper"
    18  	"go.uber.org/zap"
    19  
    20  	"github.com/zly-app/zapp/config/apollo_sdk"
    21  	"github.com/zly-app/zapp/consts"
    22  	"github.com/zly-app/zapp/logger"
    23  )
    24  
    25  const defApplicationDataType = "yaml"
    26  
    27  // 默认application命名空间下哪些key数据会被解析
    28  var defApplicationParseKeys = []string{"frame", "components", "plugins", "services"}
    29  
    30  type ApolloConfig struct {
    31  	Address                 string   // apollo-api地址, 多个地址用英文逗号连接
    32  	AppId                   string   // 应用名
    33  	AccessKey               string   // 验证key, 优先级高于基础认证
    34  	AuthBasicUser           string   // 基础认证用户名, 可用于nginx的基础认证扩展
    35  	AuthBasicPassword       string   // 基础认证密码
    36  	Cluster                 string   // 集群名, 默认default
    37  	AlwaysLoadFromRemote    bool     // 总是从远程获取, 在远程加载失败时不会从备份文件加载
    38  	BackupFile              string   // 备份文件名
    39  	ApplicationDataType     string   // application命名空间下key的值的数据类型, 支持yaml,yml,toml,json
    40  	ApplicationParseKeys    []string // application命名空间下哪些key数据会被解析, 无论如何默认的key(frame/components/plugins/services)会被解析
    41  	Namespaces              []string // 其他自定义命名空间
    42  	IgnoreNamespaceNotFound bool     // 是否忽略命名空间不存在, 无论如何设置application命名空间必须存在
    43  	client                  *apollo_sdk.ApolloClient
    44  }
    45  
    46  // 从viper构建apollo配置
    47  func makeApolloConfigFromViper(vi *viper.Viper) (*ApolloConfig, error) {
    48  	if !vi.IsSet(consts.ApolloConfigKey) {
    49  		return nil, errors.New("apollo配置不存在")
    50  	}
    51  
    52  	var conf ApolloConfig
    53  	v := vi.Get(consts.ApolloConfigKey)
    54  	if value, ok := v.(*ApolloConfig); ok {
    55  		conf = *value
    56  		if conf.client != nil {
    57  			return &conf, nil
    58  		}
    59  	} else {
    60  		err := vi.UnmarshalKey(consts.ApolloConfigKey, &conf)
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  	}
    65  
    66  	if conf.Cluster == "" {
    67  		conf.Cluster = os.Getenv(consts.ApolloConfigClusterFromEnvKey)
    68  	}
    69  
    70  	switch v := strings.ToLower(conf.ApplicationDataType); v {
    71  	case "":
    72  		conf.ApplicationDataType = defApplicationDataType
    73  	case "yaml", "yml", "json", "toml":
    74  		conf.ApplicationDataType = v
    75  	default:
    76  		return nil, fmt.Errorf("不支持的ApplicationDataType: %v", conf.ApplicationDataType)
    77  	}
    78  
    79  	ac := &apollo_sdk.ApolloClient{
    80  		Address:                 conf.Address,
    81  		AppId:                   conf.AppId,
    82  		AccessKey:               conf.AccessKey,
    83  		AuthBasicUser:           conf.AuthBasicUser,
    84  		AuthBasicPassword:       conf.AuthBasicPassword,
    85  		Cluster:                 conf.Cluster,
    86  		AlwaysLoadFromRemote:    conf.AlwaysLoadFromRemote,
    87  		BackupFile:              conf.BackupFile,
    88  		Namespaces:              append([]string{}, conf.Namespaces...),
    89  		IgnoreNamespaceNotFound: conf.IgnoreNamespaceNotFound,
    90  	}
    91  	err := ac.Init()
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	conf.client = ac
    96  
    97  	vi.Set(consts.ApolloConfigKey, &conf)
    98  	return &conf, nil
    99  }
   100  
   101  // 从apollo中获取配置构建viper
   102  func makeViperFromApollo(conf *ApolloConfig) (*viper.Viper, error) {
   103  	dataList, err := conf.client.GetNamespacesData()
   104  	if err != nil {
   105  		return nil, fmt.Errorf("获取apollo配置数据失败: %s", err)
   106  	}
   107  
   108  	configs := make(map[string]interface{}, len(dataList))
   109  	for namespace, data := range dataList {
   110  		err := analyseApolloConfig(configs, namespace, data.Configurations, conf)
   111  		if err != nil {
   112  			return nil, fmt.Errorf("分析apollo配置数据失败: %v", err)
   113  		}
   114  	}
   115  
   116  	// 构建viper
   117  	vi := viper.New()
   118  	if err = vi.MergeConfigMap(configs); err != nil {
   119  		return nil, fmt.Errorf("合并apollo配置数据失败: %s", err)
   120  	}
   121  	return vi, nil
   122  }
   123  
   124  // 分析apollo配置
   125  func analyseApolloConfig(dst map[string]interface{}, namespace string, configurations map[string]string, conf *ApolloConfig) error {
   126  	if namespace != apollo_sdk.ApplicationNamespace {
   127  		dst[namespace] = configurations
   128  		logger.Log.Info("分析apollo配置数据",
   129  			zap.String("namespace", namespace),
   130  			zap.Any("configurations", configurations),
   131  		)
   132  		return nil
   133  	}
   134  
   135  	isParseKey := func(key string) bool {
   136  		for _, s := range defApplicationParseKeys {
   137  			if key == s {
   138  				return true
   139  			}
   140  		}
   141  		for _, s := range conf.ApplicationParseKeys {
   142  			if key == s {
   143  				return true
   144  			}
   145  		}
   146  		return false
   147  	}
   148  
   149  	for k, v := range configurations {
   150  		if !isParseKey(k) {
   151  			mm, ok := dst[namespace]
   152  			if !ok {
   153  				mm = make(map[string]string)
   154  				dst[namespace] = mm
   155  			}
   156  			mm.(map[string]string)[k] = v
   157  			continue
   158  		}
   159  
   160  		vi := viper.New()
   161  		vi.SetConfigType(conf.ApplicationDataType)
   162  		err := vi.ReadConfig(strings.NewReader(v))
   163  		if err != nil {
   164  			return fmt.Errorf("解析数据失败 key: %v, value: %v, err: %v", k, v, err)
   165  		}
   166  		data := vi.AllSettings()
   167  		dst[k] = data
   168  	}
   169  	return nil
   170  }
   171  
   172  // 获取apollo客户端
   173  func GetApolloClient() (*apollo_sdk.ApolloClient, error) {
   174  	if Conf == nil {
   175  		return nil, fmt.Errorf("config未初始化")
   176  	}
   177  	vi := Conf.GetViper()
   178  	conf, err := makeApolloConfigFromViper(vi)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	return conf.client, nil
   183  }