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 }