lawless/server/config/config.go

219 行
4.6 KiB
Go

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

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