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

     1  /*
     2  -------------------------------------------------
     3     Author :       zlyuancn
     4     date:         2021/1/20
     5     Description :
     6  -------------------------------------------------
     7  */
     8  
     9  package config
    10  
    11  import (
    12  	"bytes"
    13  	"encoding/json"
    14  	"flag"
    15  	"fmt"
    16  	"os"
    17  	"strings"
    18  
    19  	"github.com/spf13/viper"
    20  	"go.uber.org/zap"
    21  
    22  	"github.com/zly-app/zapp/consts"
    23  	"github.com/zly-app/zapp/core"
    24  	"github.com/zly-app/zapp/logger"
    25  	"github.com/zly-app/zapp/pkg/utils"
    26  	"github.com/zly-app/zapp/pkg/zlog"
    27  )
    28  
    29  var Conf core.IConfig
    30  
    31  type configCli struct {
    32  	vi     *viper.Viper
    33  	c      *core.Config
    34  	flags  map[string]struct{}
    35  	labels map[string]string
    36  }
    37  
    38  func newConfig(appName string) *core.Config {
    39  	conf := &core.Config{
    40  		Frame: core.FrameConfig{
    41  			Debug:              true,
    42  			FreeMemoryInterval: consts.DefaultFreeMemoryInterval,
    43  			Labels:             make(map[string]string),
    44  			Log:                zlog.DefaultConfig,
    45  			PrintConfig:        true,
    46  		},
    47  	}
    48  	conf.Frame.Log.Name = appName
    49  	return conf
    50  }
    51  
    52  // 解析配置
    53  //
    54  // 配置来源优先级 命令行 > WithViper > WithConfig > WithFiles(Apollo分片优先级最高) > WithApollo > 默认配置文件
    55  // 注意: 多个配置文件如果存在同配置分片会智能合并, 同分片中完全相同的配置节点以最后的文件为准, 从apollo拉取的配置会覆盖相同的文件配置节点
    56  func NewConfig(appName string, opts ...Option) core.IConfig {
    57  	opt := newOptions()
    58  	for _, o := range opts {
    59  		o(opt)
    60  	}
    61  
    62  	confText := flag.String("c", "", "配置文件,多个文件用逗号隔开,同名配置分片会完全覆盖之前的分片")
    63  	testFlag := flag.Bool("t", false, "测试配置文件")
    64  	flag.Parse()
    65  
    66  	var rawVi *viper.Viper
    67  	var err error
    68  	if *confText != "" { // 命令行
    69  		files := strings.Split(*confText, ",")
    70  		rawVi, err = makeViperFromFile(files, false)
    71  		if err != nil {
    72  			logger.Log.Fatal("从命令指定文件加载失败", zap.Error(err))
    73  		}
    74  	} else if opt.vi != nil { // WithViper
    75  		rawVi = opt.vi
    76  	} else if opt.conf != nil { // WithConfig
    77  		rawVi, err = makeViperFromStruct(opt.conf)
    78  		if err != nil {
    79  			logger.Log.Fatal("从配置结构构建viper失败", zap.Any("config", opt.conf), zap.Error(err))
    80  		}
    81  	} else if len(opt.files) > 0 { // WithFiles
    82  		rawVi, err = makeViperFromFile(opt.files, false)
    83  		if err != nil {
    84  			logger.Log.Fatal("从用户指定文件构建viper失败", zap.Error(err))
    85  		}
    86  	} else if opt.apolloConfig != nil { // WithApollo
    87  		rawVi = viper.New()
    88  		rawVi.Set(consts.ApolloConfigKey, opt.apolloConfig)
    89  	} else if rawVi = loadDefaultFiles(); rawVi != nil {
    90  	}
    91  
    92  	vi := viper.New()
    93  	if rawVi != nil {
    94  		if err := vi.MergeConfigMap(rawVi.AllSettings()); err != nil {
    95  			logger.Log.Fatal("合并配置文件失败", zap.Error(err))
    96  		}
    97  	}
    98  
    99  	// 如果发现包含配置
   100  	if vi.IsSet(consts.IncludeConfigFileKey) {
   101  		vi = loadIncludeConfigFile(vi)
   102  	}
   103  
   104  	// 如果从viper中发现了apollo配置
   105  	if vi.IsSet(consts.ApolloConfigKey) {
   106  		apolloConf, err := makeApolloConfigFromViper(vi)
   107  		if err != nil {
   108  			logger.Log.Fatal("解析apollo配置失败", zap.Error(err))
   109  		}
   110  		rawVi, err = makeViperFromApollo(apolloConf)
   111  		if err != nil {
   112  			logger.Log.Fatal("从apollo构建viper失败", zap.Error(err))
   113  		}
   114  		if err = vi.MergeConfigMap(rawVi.AllSettings()); err != nil {
   115  			logger.Log.Fatal("合并apollo配置失败", zap.Error(err))
   116  		}
   117  	}
   118  
   119  	c := &configCli{
   120  		vi: vi,
   121  		c:  newConfig(appName),
   122  	}
   123  	// 解析配置
   124  	if err = vi.Unmarshal(c.c); err != nil {
   125  		logger.Log.Fatal("配置解析失败", zap.Error(err))
   126  	}
   127  
   128  	c.checkDefaultConfig(c.c)
   129  
   130  	if *testFlag {
   131  		logger.Log.Info("配置文件测试成功")
   132  		os.Exit(0)
   133  	}
   134  
   135  	c.makeFlags()
   136  	c.makeLabels()
   137  
   138  	Conf = c
   139  	return c
   140  }
   141  
   142  func (c *configCli) makeFlags() {
   143  	c.flags = make(map[string]struct{}, len(c.c.Frame.Flags))
   144  	for _, flag := range c.c.Frame.Flags {
   145  		c.flags[strings.ToLower(flag)] = struct{}{}
   146  	}
   147  
   148  	flags := make([]string, 0, len(c.flags))
   149  	for flag := range c.flags {
   150  		flags = append(flags, flag)
   151  	}
   152  	c.c.Frame.Flags = flags
   153  }
   154  
   155  func (c *configCli) makeLabels() {
   156  	c.labels = make(map[string]string, len(c.c.Frame.Labels))
   157  	for k, v := range c.c.Frame.Labels {
   158  		c.labels[strings.ToLower(k)] = v
   159  	}
   160  	c.c.Frame.Labels = c.labels
   161  }
   162  
   163  // 加载默认配置文件, 默认配置文件不存在返回nil
   164  func loadDefaultFiles() *viper.Viper {
   165  	files := strings.Split(consts.DefaultConfigFiles, ",")
   166  	vi := viper.New()
   167  	for _, file := range files {
   168  		_, err := os.Stat(file)
   169  		if err != nil {
   170  			if os.IsNotExist(err) {
   171  				continue
   172  			}
   173  			logger.Log.Fatal("读取配置文件信息失败", zap.String("file", file), zap.Error(err))
   174  		}
   175  
   176  		vi.SetConfigFile(file)
   177  		if err = vi.MergeInConfig(); err != nil {
   178  			logger.Log.Fatal("合并配置文件失败", zap.String("file", file), zap.Error(err))
   179  		}
   180  		logger.Log.Info("使用默认配置文件", zap.String("file", file))
   181  		return vi
   182  	}
   183  	return nil
   184  }
   185  
   186  // 合并文件到viper
   187  func mergeFile(vi *viper.Viper, file string, ignoreNotExist bool) error {
   188  	_, err := os.Stat(file)
   189  	if err != nil {
   190  		if os.IsNotExist(err) {
   191  			if ignoreNotExist {
   192  				logger.Log.Warn("配置文件不存在", zap.String("file", file))
   193  				return nil
   194  			}
   195  			return fmt.Errorf("配置文件不存在")
   196  		}
   197  		return fmt.Errorf("读取配置文件信息失败: %s", err)
   198  	}
   199  
   200  	vi.SetConfigFile(file)
   201  	if err = vi.MergeInConfig(); err != nil {
   202  		return err
   203  	}
   204  	return nil
   205  }
   206  
   207  // 从文件构建viper
   208  func makeViperFromFile(files []string, ignoreNotExist bool) (*viper.Viper, error) {
   209  	vi := viper.New()
   210  	for _, file := range files {
   211  		if err := mergeFile(vi, file, ignoreNotExist); err != nil {
   212  			return nil, fmt.Errorf("合并配置文件'%s'失败: %s", file, err.Error())
   213  		}
   214  	}
   215  	return vi, nil
   216  }
   217  
   218  // 从结构体构建viper
   219  func makeViperFromStruct(a interface{}) (*viper.Viper, error) {
   220  	bs, err := json.Marshal(a)
   221  	if err != nil {
   222  		return nil, fmt.Errorf("编码失败: %s", err)
   223  	}
   224  
   225  	vi := viper.New()
   226  	vi.SetConfigType("json")
   227  	err = vi.ReadConfig(bytes.NewReader(bs))
   228  	if err != nil {
   229  		return nil, fmt.Errorf("数据解析失败: %s", err)
   230  	}
   231  	return vi, nil
   232  }
   233  
   234  // 加载包含配置文件
   235  func loadIncludeConfigFile(vi *viper.Viper) *viper.Viper {
   236  	var temp struct {
   237  		Files string
   238  	}
   239  	err := vi.UnmarshalKey(consts.IncludeConfigFileKey, &temp)
   240  	if err != nil {
   241  		logger.Log.Fatal("include配置错误", zap.Error(err))
   242  	}
   243  
   244  	files := strings.Split(temp.Files, ",")
   245  	for _, file := range files {
   246  		if err := mergeFile(vi, file, false); err != nil {
   247  			logger.Log.Fatal("合并包含文件失败", zap.String("file", file), zap.Error(err))
   248  		}
   249  	}
   250  	return vi
   251  }
   252  
   253  func (c *configCli) checkDefaultConfig(conf *core.Config) {
   254  	if conf.Frame.Name != "" {
   255  		conf.Frame.Log.Name = conf.Frame.Name
   256  	}
   257  	conf.Frame.WaitServiceRunTime = utils.Ternary.Or(conf.Frame.WaitServiceRunTime, consts.DefaultWaitServiceRunTime).(int)
   258  	conf.Frame.ServiceUnstableObserveTime = utils.Ternary.Or(conf.Frame.ServiceUnstableObserveTime, consts.DefaultServiceUnstableObserveTime).(int)
   259  }
   260  
   261  func (c *configCli) Config() *core.Config {
   262  	return c.c
   263  }
   264  
   265  func (c *configCli) GetViper() *viper.Viper {
   266  	return c.vi
   267  }
   268  
   269  func (c *configCli) Parse(key string, outPtr interface{}, ignoreNotSet ...bool) error {
   270  	if !c.vi.IsSet(key) {
   271  		if len(ignoreNotSet) > 0 && ignoreNotSet[0] {
   272  			return nil
   273  		}
   274  		return fmt.Errorf("key<%s>不存在", key)
   275  	}
   276  	if err := c.vi.UnmarshalKey(key, outPtr); err != nil {
   277  		return fmt.Errorf("无法解析key<%s>配置: %s", key, err)
   278  	}
   279  	return nil
   280  }
   281  
   282  func (c *configCli) ParseComponentConfig(componentType core.ComponentType, componentName string, outPtr interface{}, ignoreNotSet ...bool) error {
   283  	componentName = utils.Ternary.Or(componentName, consts.DefaultComponentName).(string)
   284  	key := "components." + string(componentType) + "." + componentName
   285  	if !c.vi.IsSet(key) {
   286  		if len(ignoreNotSet) > 0 && ignoreNotSet[0] {
   287  			return nil
   288  		}
   289  		return fmt.Errorf("组件配置<%s.%s>不存在", componentType, componentName)
   290  	}
   291  	if err := c.vi.UnmarshalKey(key, outPtr); err != nil {
   292  		return fmt.Errorf("无法解析<%s.%s>组件配置: %s", componentType, componentName, err)
   293  	}
   294  	return nil
   295  }
   296  
   297  func (c *configCli) ParsePluginConfig(pluginType core.PluginType, outPtr interface{}, ignoreNotSet ...bool) error {
   298  	key := "plugins." + string(pluginType)
   299  	if !c.vi.IsSet(key) {
   300  		if len(ignoreNotSet) > 0 && ignoreNotSet[0] {
   301  			return nil
   302  		}
   303  		return fmt.Errorf("插件配置<%s>不存在", pluginType)
   304  	}
   305  	if err := c.vi.UnmarshalKey(key, outPtr); err != nil {
   306  		return fmt.Errorf("无法解析<%s>插件配置: %s", pluginType, err)
   307  	}
   308  	return nil
   309  }
   310  
   311  func (c *configCli) ParseServiceConfig(serviceType core.ServiceType, outPtr interface{}, ignoreNotSet ...bool) error {
   312  	key := "services." + string(serviceType)
   313  	if !c.vi.IsSet(key) {
   314  		if len(ignoreNotSet) > 0 && ignoreNotSet[0] {
   315  			return nil
   316  		}
   317  		return fmt.Errorf("服务配置<%s>不存在", serviceType)
   318  	}
   319  	if err := c.vi.UnmarshalKey(key, outPtr); err != nil {
   320  		return fmt.Errorf("无法解析<%s>服务配置: %s", serviceType, err)
   321  	}
   322  	return nil
   323  }
   324  
   325  func (c *configCli) GetLabel(name string) string {
   326  	return c.labels[strings.ToLower(name)]
   327  }
   328  
   329  func (c *configCli) GetLabels() map[string]string {
   330  	return c.labels
   331  }
   332  
   333  func (c *configCli) HasFlag(flag string) bool {
   334  	_, ok := c.flags[strings.ToLower(flag)]
   335  	return ok
   336  }
   337  
   338  func (c *configCli) GetFlags() []string {
   339  	return c.c.Frame.Flags
   340  }