219 行
4.6 KiB
Go
219 行
4.6 KiB
Go
|
|
// Package config 负责从 Nacos 配置中心拉取动态参数并维护本地缓存。
|
|||
|
|
// 服务端逻辑统一通过 config.GetXxx(key) 读取,避免硬编码。
|
|||
|
|
package config
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"encoding/json"
|
|||
|
|
"fmt"
|
|||
|
|
"os"
|
|||
|
|
"strconv"
|
|||
|
|
"sync"
|
|||
|
|
|
|||
|
|
"gopkg.in/yaml.v3"
|
|||
|
|
|
|||
|
|
"github.com/nacos-group/nacos-sdk-go/v2/clients"
|
|||
|
|
"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
|
|||
|
|
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
|||
|
|
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// Config 维护 Nacos 配置的本地缓存,支持热更监听。
|
|||
|
|
type Config struct {
|
|||
|
|
client config_client.IConfigClient
|
|||
|
|
cache map[string]interface{}
|
|||
|
|
mu sync.RWMutex
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Global 全局配置实例,由 main.go 初始化。
|
|||
|
|
var Global *Config
|
|||
|
|
|
|||
|
|
// NewNacosConfig 创建并连接 Nacos 配置中心。
|
|||
|
|
// 参数可从环境变量读取:NACOS_SERVER、NACOS_NAMESPACE。
|
|||
|
|
func NewNacosConfig(serverAddr, namespace string) (*Config, error) {
|
|||
|
|
if serverAddr == "" {
|
|||
|
|
serverAddr = os.Getenv("NACOS_SERVER")
|
|||
|
|
}
|
|||
|
|
if serverAddr == "" {
|
|||
|
|
serverAddr = "127.0.0.1"
|
|||
|
|
}
|
|||
|
|
if namespace == "" {
|
|||
|
|
namespace = os.Getenv("NACOS_NAMESPACE")
|
|||
|
|
}
|
|||
|
|
if namespace == "" {
|
|||
|
|
namespace = "honghuang"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
sc := []constant.ServerConfig{
|
|||
|
|
{
|
|||
|
|
IpAddr: serverAddr,
|
|||
|
|
Port: 8848,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
cc := constant.ClientConfig{
|
|||
|
|
NamespaceId: namespace,
|
|||
|
|
TimeoutMs: 5000,
|
|||
|
|
NotLoadCacheAtStart: true,
|
|||
|
|
LogDir: "/tmp/nacos/log",
|
|||
|
|
CacheDir: "/tmp/nacos/cache",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
client, err := clients.NewConfigClient(
|
|||
|
|
vo.NacosClientParam{
|
|||
|
|
ServerConfigs: sc,
|
|||
|
|
ClientConfig: &cc,
|
|||
|
|
},
|
|||
|
|
)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, fmt.Errorf("nacos config client init failed: %w", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return &Config{
|
|||
|
|
client: client,
|
|||
|
|
cache: make(map[string]interface{}),
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Load 加载指定 dataId + group 的 YAML/JSON 配置到本地缓存。
|
|||
|
|
func (c *Config) Load(dataId, group string) error {
|
|||
|
|
content, err := c.client.GetConfig(vo.ConfigParam{DataId: dataId, Group: group})
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("nacos get config %s/%s failed: %w", dataId, group, err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var m map[string]interface{}
|
|||
|
|
if err := yaml.Unmarshal([]byte(content), &m); err != nil {
|
|||
|
|
return fmt.Errorf("parse config %s/%s failed: %w", dataId, group, err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
c.mu.Lock()
|
|||
|
|
for k, v := range m {
|
|||
|
|
c.cache[k] = v
|
|||
|
|
}
|
|||
|
|
c.mu.Unlock()
|
|||
|
|
|
|||
|
|
// 监听后续变更
|
|||
|
|
_ = c.client.ListenConfig(vo.ConfigParam{
|
|||
|
|
DataId: dataId,
|
|||
|
|
Group: group,
|
|||
|
|
OnChange: func(namespace, group, dataId, data string) {
|
|||
|
|
var changed map[string]interface{}
|
|||
|
|
if err := yaml.Unmarshal([]byte(data), &changed); err != nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
c.mu.Lock()
|
|||
|
|
for k, v := range changed {
|
|||
|
|
c.cache[k] = v
|
|||
|
|
}
|
|||
|
|
c.mu.Unlock()
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewStaticConfig 创建一个不连接 Nacos 的静态配置实例,便于测试与本地降级。
|
|||
|
|
func NewStaticConfig(defaults map[string]interface{}) *Config {
|
|||
|
|
cache := make(map[string]interface{})
|
|||
|
|
for k, v := range defaults {
|
|||
|
|
cache[k] = v
|
|||
|
|
}
|
|||
|
|
return &Config{cache: cache}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Get 返回原始配置值。
|
|||
|
|
func (c *Config) Get(key string) (interface{}, bool) {
|
|||
|
|
if c == nil {
|
|||
|
|
return nil, false
|
|||
|
|
}
|
|||
|
|
c.mu.RLock()
|
|||
|
|
defer c.mu.RUnlock()
|
|||
|
|
if c.cache == nil {
|
|||
|
|
return nil, false
|
|||
|
|
}
|
|||
|
|
v, ok := c.cache[key]
|
|||
|
|
return v, ok
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetString 读取字符串配置,缺失返回默认值。
|
|||
|
|
func (c *Config) GetString(key, def string) string {
|
|||
|
|
v, ok := c.Get(key)
|
|||
|
|
if !ok {
|
|||
|
|
return def
|
|||
|
|
}
|
|||
|
|
s, _ := v.(string)
|
|||
|
|
if s == "" {
|
|||
|
|
return def
|
|||
|
|
}
|
|||
|
|
return s
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetInt 读取整型配置。
|
|||
|
|
func (c *Config) GetInt(key string, def int) int {
|
|||
|
|
v, ok := c.Get(key)
|
|||
|
|
if !ok {
|
|||
|
|
return def
|
|||
|
|
}
|
|||
|
|
switch n := v.(type) {
|
|||
|
|
case int:
|
|||
|
|
return n
|
|||
|
|
case int64:
|
|||
|
|
return int(n)
|
|||
|
|
case float64:
|
|||
|
|
return int(n)
|
|||
|
|
case string:
|
|||
|
|
if i, err := strconv.Atoi(n); err == nil {
|
|||
|
|
return i
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return def
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetFloat64 读取浮点配置。
|
|||
|
|
func (c *Config) GetFloat64(key string, def float64) float64 {
|
|||
|
|
v, ok := c.Get(key)
|
|||
|
|
if !ok {
|
|||
|
|
return def
|
|||
|
|
}
|
|||
|
|
switch n := v.(type) {
|
|||
|
|
case float64:
|
|||
|
|
return n
|
|||
|
|
case int:
|
|||
|
|
return float64(n)
|
|||
|
|
case string:
|
|||
|
|
if f, err := strconv.ParseFloat(n, 64); err == nil {
|
|||
|
|
return f
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return def
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetBool 读取布尔配置。
|
|||
|
|
func (c *Config) GetBool(key string, def bool) bool {
|
|||
|
|
v, ok := c.Get(key)
|
|||
|
|
if !ok {
|
|||
|
|
return def
|
|||
|
|
}
|
|||
|
|
switch b := v.(type) {
|
|||
|
|
case bool:
|
|||
|
|
return b
|
|||
|
|
case string:
|
|||
|
|
if r, err := strconv.ParseBool(b); err == nil {
|
|||
|
|
return r
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return def
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetJSON 读取嵌套 JSON 对象并反序列化到 out。
|
|||
|
|
func (c *Config) GetJSON(key string, out interface{}) bool {
|
|||
|
|
v, ok := c.Get(key)
|
|||
|
|
if !ok {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
data, err := json.Marshal(v)
|
|||
|
|
if err != nil {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
return json.Unmarshal(data, out) == nil
|
|||
|
|
}
|