trpc.group/trpc-go/trpc-go@v1.0.3/config/trpc_config.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package config
    15  
    16  import (
    17  	"encoding/json"
    18  	"errors"
    19  	"fmt"
    20  	"strings"
    21  	"sync"
    22  
    23  	"github.com/BurntSushi/toml"
    24  	"github.com/spf13/cast"
    25  	yaml "gopkg.in/yaml.v3"
    26  	"trpc.group/trpc-go/trpc-go/internal/expandenv"
    27  
    28  	"trpc.group/trpc-go/trpc-go/log"
    29  )
    30  
    31  var (
    32  	// ErrConfigNotExist is config not exist error
    33  	ErrConfigNotExist = errors.New("trpc/config: config not exist")
    34  
    35  	// ErrProviderNotExist is provider not exist error
    36  	ErrProviderNotExist = errors.New("trpc/config: provider not exist")
    37  
    38  	// ErrCodecNotExist is codec not exist error
    39  	ErrCodecNotExist = errors.New("trpc/config: codec not exist")
    40  )
    41  
    42  func init() {
    43  	RegisterCodec(&YamlCodec{})
    44  	RegisterCodec(&JSONCodec{})
    45  	RegisterCodec(&TomlCodec{})
    46  }
    47  
    48  // LoadOption defines the option function for loading configuration.
    49  type LoadOption func(*TrpcConfig)
    50  
    51  // TrpcConfigLoader is a config loader for trpc.
    52  type TrpcConfigLoader struct {
    53  	watchers sync.Map
    54  }
    55  
    56  // Load returns the config specified by input parameter.
    57  func (loader *TrpcConfigLoader) Load(path string, opts ...LoadOption) (Config, error) {
    58  	c, err := newTrpcConfig(path, opts...)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	w := &watcher{}
    64  	i, loaded := loader.watchers.LoadOrStore(c.p, w)
    65  	if !loaded {
    66  		c.p.Watch(w.watch)
    67  	} else {
    68  		w = i.(*watcher)
    69  	}
    70  
    71  	c = w.getOrCreate(c.path).getOrStore(c)
    72  	if err = c.init(); err != nil {
    73  		return nil, err
    74  	}
    75  	return c, nil
    76  }
    77  
    78  // Reload reloads config data.
    79  func (loader *TrpcConfigLoader) Reload(path string, opts ...LoadOption) error {
    80  	c, err := newTrpcConfig(path, opts...)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	v, ok := loader.watchers.Load(c.p)
    86  	if !ok {
    87  		return ErrConfigNotExist
    88  	}
    89  	w := v.(*watcher)
    90  
    91  	s := w.get(path)
    92  	if s == nil {
    93  		return ErrConfigNotExist
    94  	}
    95  
    96  	oc := s.get(c.id)
    97  	if oc == nil {
    98  		return ErrConfigNotExist
    99  	}
   100  
   101  	return oc.Load()
   102  }
   103  
   104  func newTrpcConfigLoad() *TrpcConfigLoader {
   105  	return &TrpcConfigLoader{}
   106  }
   107  
   108  // DefaultConfigLoader is the default config loader.
   109  var DefaultConfigLoader = newTrpcConfigLoad()
   110  
   111  // YamlCodec is yaml codec.
   112  type YamlCodec struct{}
   113  
   114  // Name returns yaml codec's name.
   115  func (*YamlCodec) Name() string {
   116  	return "yaml"
   117  }
   118  
   119  // Unmarshal deserializes the in bytes into out parameter by yaml.
   120  func (c *YamlCodec) Unmarshal(in []byte, out interface{}) error {
   121  	return yaml.Unmarshal(in, out)
   122  }
   123  
   124  // JSONCodec is json codec.
   125  type JSONCodec struct{}
   126  
   127  // Name returns json codec's name.
   128  func (*JSONCodec) Name() string {
   129  	return "json"
   130  }
   131  
   132  // Unmarshal deserializes the in bytes into out parameter by json.
   133  func (c *JSONCodec) Unmarshal(in []byte, out interface{}) error {
   134  	return json.Unmarshal(in, out)
   135  }
   136  
   137  // TomlCodec is toml codec.
   138  type TomlCodec struct{}
   139  
   140  // Name returns toml codec's name.
   141  func (*TomlCodec) Name() string {
   142  	return "toml"
   143  }
   144  
   145  // Unmarshal deserializes the in bytes into out parameter by toml.
   146  func (c *TomlCodec) Unmarshal(in []byte, out interface{}) error {
   147  	return toml.Unmarshal(in, out)
   148  }
   149  
   150  // watch manage one data provider
   151  type watcher struct {
   152  	sets sync.Map // *set
   153  }
   154  
   155  // get config item by path
   156  func (w *watcher) get(path string) *set {
   157  	if i, ok := w.sets.Load(path); ok {
   158  		return i.(*set)
   159  	}
   160  	return nil
   161  }
   162  
   163  // getOrCreate get config item by path if not exist and create and return
   164  func (w *watcher) getOrCreate(path string) *set {
   165  	i, _ := w.sets.LoadOrStore(path, &set{})
   166  	return i.(*set)
   167  }
   168  
   169  // watch func
   170  func (w *watcher) watch(path string, data []byte) {
   171  	if v := w.get(path); v != nil {
   172  		v.watch(data)
   173  	}
   174  }
   175  
   176  // set manages configs with same provider and name with different type
   177  // used config.id as unique identifier
   178  type set struct {
   179  	path  string
   180  	mutex sync.RWMutex
   181  	items []*TrpcConfig
   182  }
   183  
   184  // get data
   185  func (s *set) get(id string) *TrpcConfig {
   186  	s.mutex.RLock()
   187  	defer s.mutex.RUnlock()
   188  	for _, v := range s.items {
   189  		if v.id == id {
   190  			return v
   191  		}
   192  	}
   193  	return nil
   194  }
   195  
   196  func (s *set) getOrStore(tc *TrpcConfig) *TrpcConfig {
   197  	if v := s.get(tc.id); v != nil {
   198  		return v
   199  	}
   200  
   201  	s.mutex.Lock()
   202  	for _, item := range s.items {
   203  		if item.id == tc.id {
   204  			s.mutex.Unlock()
   205  			return item
   206  		}
   207  	}
   208  	// not found and add
   209  	s.items = append(s.items, tc)
   210  	s.mutex.Unlock()
   211  	return tc
   212  }
   213  
   214  // watch data change, delete no watch model config and update watch model config and target notify
   215  func (s *set) watch(data []byte) {
   216  	var items []*TrpcConfig
   217  	var del []*TrpcConfig
   218  	s.mutex.Lock()
   219  	for _, v := range s.items {
   220  		if v.watch {
   221  			items = append(items, v)
   222  		} else {
   223  			del = append(del, v)
   224  		}
   225  	}
   226  	s.items = items
   227  	s.mutex.Unlock()
   228  
   229  	for _, item := range items {
   230  		err := item.doWatch(data)
   231  		item.notify(data, err)
   232  	}
   233  
   234  	for _, item := range del {
   235  		item.notify(data, nil)
   236  	}
   237  }
   238  
   239  // defaultNotifyChange default hook for notify config changed
   240  var defaultWatchHook = func(message WatchMessage) {}
   241  
   242  // SetDefaultWatchHook set default hook notify when config changed
   243  func SetDefaultWatchHook(f func(message WatchMessage)) {
   244  	defaultWatchHook = f
   245  }
   246  
   247  // WatchMessage change message
   248  type WatchMessage struct {
   249  	Provider  string // provider name
   250  	Path      string // config path
   251  	ExpandEnv bool   // expend env status
   252  	Codec     string // codec
   253  	Watch     bool   // status for start watch
   254  	Value     []byte // config content diff ?
   255  	Error     error  // load error message, success is empty string
   256  }
   257  
   258  var _ Config = (*TrpcConfig)(nil)
   259  
   260  // TrpcConfig is used to parse yaml config file for trpc.
   261  type TrpcConfig struct {
   262  	id  string       // config identity
   263  	msg WatchMessage // new to init message for notify only copy
   264  
   265  	p         DataProvider // config provider
   266  	path      string       // config name
   267  	decoder   Codec        // config codec
   268  	expandEnv bool         // status for whether replace the variables in the configuration with environment variables
   269  
   270  	// because function is not support comparable in singleton, so the following options work only for the first load
   271  	watch     bool
   272  	watchHook func(message WatchMessage)
   273  
   274  	mutex sync.RWMutex
   275  	value *entity // store config value
   276  }
   277  
   278  type entity struct {
   279  	raw  []byte      // current binary data
   280  	data interface{} // unmarshal type to use point type, save latest no error data
   281  }
   282  
   283  func newEntity() *entity {
   284  	return &entity{
   285  		data: make(map[string]interface{}),
   286  	}
   287  }
   288  
   289  func newTrpcConfig(path string, opts ...LoadOption) (*TrpcConfig, error) {
   290  	c := &TrpcConfig{
   291  		path:    path,
   292  		p:       GetProvider("file"),
   293  		decoder: GetCodec("yaml"),
   294  		watchHook: func(message WatchMessage) {
   295  			defaultWatchHook(message)
   296  		},
   297  	}
   298  	for _, o := range opts {
   299  		o(c)
   300  	}
   301  	if c.p == nil {
   302  		return nil, ErrProviderNotExist
   303  	}
   304  	if c.decoder == nil {
   305  		return nil, ErrCodecNotExist
   306  	}
   307  
   308  	c.msg.Provider = c.p.Name()
   309  	c.msg.Path = c.path
   310  	c.msg.Codec = c.decoder.Name()
   311  	c.msg.ExpandEnv = c.expandEnv
   312  	c.msg.Watch = c.watch
   313  
   314  	// since reflect.String() cannot uniquely identify a type, this id is used as a preliminary judgment basis
   315  	const idFormat = "provider:%s path:%s codec:%s env:%t watch:%t"
   316  	c.id = fmt.Sprintf(idFormat, c.p.Name(), c.path, c.decoder.Name(), c.expandEnv, c.watch)
   317  	return c, nil
   318  }
   319  
   320  func (c *TrpcConfig) get() *entity {
   321  	c.mutex.RLock()
   322  	defer c.mutex.RUnlock()
   323  	if c.value != nil {
   324  		return c.value
   325  	}
   326  	return newEntity()
   327  }
   328  
   329  // init return config entity error when entity is empty and load run loads config once
   330  func (c *TrpcConfig) init() error {
   331  	c.mutex.RLock()
   332  	if c.value != nil {
   333  		c.mutex.RUnlock()
   334  		return nil
   335  	}
   336  	c.mutex.RUnlock()
   337  
   338  	c.mutex.Lock()
   339  	defer c.mutex.Unlock()
   340  	if c.value != nil {
   341  		return nil
   342  	}
   343  
   344  	data, err := c.p.Read(c.path)
   345  	if err != nil {
   346  		return fmt.Errorf("trpc/config failed to load error: %w config id: %s", err, c.id)
   347  	}
   348  	return c.set(data)
   349  }
   350  func (c *TrpcConfig) doWatch(data []byte) error {
   351  	c.mutex.Lock()
   352  	defer c.mutex.Unlock()
   353  	return c.set(data)
   354  }
   355  func (c *TrpcConfig) set(data []byte) error {
   356  	if c.expandEnv {
   357  		data = expandenv.ExpandEnv(data)
   358  	}
   359  
   360  	e := newEntity()
   361  	e.raw = data
   362  	err := c.decoder.Unmarshal(data, &e.data)
   363  	if err != nil {
   364  		return fmt.Errorf("trpc/config: failed to parse:%w, id:%s", err, c.id)
   365  	}
   366  	c.value = e
   367  	return nil
   368  }
   369  func (c *TrpcConfig) notify(data []byte, err error) {
   370  	m := c.msg
   371  
   372  	m.Value = data
   373  	if err != nil {
   374  		m.Error = err
   375  	}
   376  
   377  	c.watchHook(m)
   378  }
   379  
   380  // Load loads config.
   381  func (c *TrpcConfig) Load() error {
   382  	if c.p == nil {
   383  		return ErrProviderNotExist
   384  	}
   385  
   386  	c.mutex.Lock()
   387  	defer c.mutex.Unlock()
   388  	data, err := c.p.Read(c.path)
   389  	if err != nil {
   390  		return fmt.Errorf("trpc/config failed to load error: %w config id: %s", err, c.id)
   391  	}
   392  
   393  	return c.set(data)
   394  }
   395  
   396  // Reload reloads config.
   397  func (c *TrpcConfig) Reload() {
   398  	if err := c.Load(); err != nil {
   399  		log.Tracef("trpc/config: failed to reload %s: %v", c.id, err)
   400  	}
   401  }
   402  
   403  // Get returns config value by key. If key is absent will return the default value.
   404  func (c *TrpcConfig) Get(key string, defaultValue interface{}) interface{} {
   405  	if v, ok := c.search(key); ok {
   406  		return v
   407  	}
   408  	return defaultValue
   409  }
   410  
   411  // Unmarshal deserializes the config into input param.
   412  func (c *TrpcConfig) Unmarshal(out interface{}) error {
   413  	return c.decoder.Unmarshal(c.get().raw, out)
   414  }
   415  
   416  // Bytes returns original config data as bytes.
   417  func (c *TrpcConfig) Bytes() []byte {
   418  	return c.get().raw
   419  }
   420  
   421  // GetInt returns int value by key, the second parameter
   422  // is default value when key is absent or type conversion fails.
   423  func (c *TrpcConfig) GetInt(key string, defaultValue int) int {
   424  	return c.findWithDefaultValue(key, defaultValue).(int)
   425  }
   426  
   427  // GetInt32 returns int32 value by key, the second parameter
   428  // is default value when key is absent or type conversion fails.
   429  func (c *TrpcConfig) GetInt32(key string, defaultValue int32) int32 {
   430  	return c.findWithDefaultValue(key, defaultValue).(int32)
   431  }
   432  
   433  // GetInt64 returns int64 value by key, the second parameter
   434  // is default value when key is absent or type conversion fails.
   435  func (c *TrpcConfig) GetInt64(key string, defaultValue int64) int64 {
   436  	return c.findWithDefaultValue(key, defaultValue).(int64)
   437  }
   438  
   439  // GetUint returns uint value by key, the second parameter
   440  // is default value when key is absent or type conversion fails.
   441  func (c *TrpcConfig) GetUint(key string, defaultValue uint) uint {
   442  	return c.findWithDefaultValue(key, defaultValue).(uint)
   443  }
   444  
   445  // GetUint32 returns uint32 value by key, the second parameter
   446  // is default value when key is absent or type conversion fails.
   447  func (c *TrpcConfig) GetUint32(key string, defaultValue uint32) uint32 {
   448  	return c.findWithDefaultValue(key, defaultValue).(uint32)
   449  }
   450  
   451  // GetUint64 returns uint64 value by key, the second parameter
   452  // is default value when key is absent or type conversion fails.
   453  func (c *TrpcConfig) GetUint64(key string, defaultValue uint64) uint64 {
   454  	return c.findWithDefaultValue(key, defaultValue).(uint64)
   455  }
   456  
   457  // GetFloat64 returns float64 value by key, the second parameter
   458  // is default value when key is absent or type conversion fails.
   459  func (c *TrpcConfig) GetFloat64(key string, defaultValue float64) float64 {
   460  	return c.findWithDefaultValue(key, defaultValue).(float64)
   461  }
   462  
   463  // GetFloat32 returns float32 value by key, the second parameter
   464  // is default value when key is absent or type conversion fails.
   465  func (c *TrpcConfig) GetFloat32(key string, defaultValue float32) float32 {
   466  	return c.findWithDefaultValue(key, defaultValue).(float32)
   467  }
   468  
   469  // GetBool returns bool value by key, the second parameter
   470  // is default value when key is absent or type conversion fails.
   471  func (c *TrpcConfig) GetBool(key string, defaultValue bool) bool {
   472  	return c.findWithDefaultValue(key, defaultValue).(bool)
   473  }
   474  
   475  // GetString returns string value by key, the second parameter
   476  // is default value when key is absent or type conversion fails.
   477  func (c *TrpcConfig) GetString(key string, defaultValue string) string {
   478  	return c.findWithDefaultValue(key, defaultValue).(string)
   479  }
   480  
   481  // IsSet returns if the config specified by key exists.
   482  func (c *TrpcConfig) IsSet(key string) bool {
   483  	_, ok := c.search(key)
   484  	return ok
   485  }
   486  
   487  // findWithDefaultValue ensures that the type of `value` is same as `defaultValue`
   488  func (c *TrpcConfig) findWithDefaultValue(key string, defaultValue interface{}) (value interface{}) {
   489  	v, ok := c.search(key)
   490  	if !ok {
   491  		return defaultValue
   492  	}
   493  
   494  	var err error
   495  	switch defaultValue.(type) {
   496  	case bool:
   497  		v, err = cast.ToBoolE(v)
   498  	case string:
   499  		v, err = cast.ToStringE(v)
   500  	case int:
   501  		v, err = cast.ToIntE(v)
   502  	case int32:
   503  		v, err = cast.ToInt32E(v)
   504  	case int64:
   505  		v, err = cast.ToInt64E(v)
   506  	case uint:
   507  		v, err = cast.ToUintE(v)
   508  	case uint32:
   509  		v, err = cast.ToUint32E(v)
   510  	case uint64:
   511  		v, err = cast.ToUint64E(v)
   512  	case float64:
   513  		v, err = cast.ToFloat64E(v)
   514  	case float32:
   515  		v, err = cast.ToFloat32E(v)
   516  	default:
   517  	}
   518  
   519  	if err != nil {
   520  		return defaultValue
   521  	}
   522  	return v
   523  }
   524  
   525  func (c *TrpcConfig) search(key string) (interface{}, bool) {
   526  	e := c.get()
   527  
   528  	unmarshalledData, ok := e.data.(map[string]interface{})
   529  	if !ok {
   530  		return nil, false
   531  	}
   532  
   533  	subkeys := strings.Split(key, ".")
   534  	value, err := search(unmarshalledData, subkeys)
   535  	if err != nil {
   536  		log.Debugf("trpc config: search key %s failed: %+v", key, err)
   537  		return value, false
   538  	}
   539  
   540  	return value, true
   541  }
   542  
   543  func search(unmarshalledData map[string]interface{}, keys []string) (interface{}, error) {
   544  	if len(keys) == 0 {
   545  		return nil, ErrConfigNotExist
   546  	}
   547  
   548  	key, ok := unmarshalledData[keys[0]]
   549  	if !ok {
   550  		return nil, ErrConfigNotExist
   551  	}
   552  
   553  	if len(keys) == 1 {
   554  		return key, nil
   555  	}
   556  	switch key := key.(type) {
   557  	case map[interface{}]interface{}:
   558  		return search(cast.ToStringMap(key), keys[1:])
   559  	case map[string]interface{}:
   560  		return search(key, keys[1:])
   561  	default:
   562  		return nil, ErrConfigNotExist
   563  	}
   564  }