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
|
||
}
|