fsnotify监听必须使用绝对路径,否则macos/linux下会静默失败;需先用filepath.abs()转换再add,且os.stat确认存在后才监听,避免panic。

fsnotify监听必须用绝对路径,否则 macOS/Linux 下事件丢失
监听相对路径(比如 "./config.yaml")在某些系统上会静默失败:macOS 的 FSEvents、Linux 的 inotify 都要求监听路径是真实存在的文件全路径。编辑器保存时先写临时文件再 rename,触发的是 MOVED_TO 事件而非 WRITE,如果只监听目录或路径不准确,就收不到。
- 启动前先用
filepath.Abs()转成绝对路径,再传给watcher.Add() - 不要监听整个配置目录(如
"./conf"),避免子目录变更误触发;只加具体文件路径 - 检查文件是否存在:
os.Stat(configPath)成功后再监听,否则watcher.Add()可能 panic
viper.WatchConfig() 不会自动重读,回调里漏掉 ReadInConfig 就永远用旧值
调了 viper.WatchConfig() 但 viper.GetInt("port") 还是旧数字?不是监听没生效,而是 viper 内部缓存根本没刷新。它只发通知,不碰文件、不解析、不更新内存里的 map。
- 回调第一行必须是
viper.ReadInConfig(),否则后续viper.Unmarshal()拿到的仍是旧缓存 -
viper.SetConfigType("yaml")必须在ReadInConfig()之前设好,否则类型不匹配会静默失败 - 别依赖
viper.Get()实时返回新值——它读的是 viper 自己的缓存,而缓存只在ReadInConfig()后更新
atomic.Value 替换配置指针比 sync.RWMutex 更适合高并发读场景
HTTP handler 每秒几百次读配置,sync.RWMutex 的读锁虽轻量,但仍有调度开销;atomic.Value 是无锁的,只要保证写入的是完整结构体指针,读取就天然线程安全。
- 声明全局变量:
var globalConf atomic.Value,初始化时globalConf.Store(&defaultCfg) - 成功解析新配置后:
globalConf.Store(&newCfg),失败则跳过,旧配置继续生效 - 业务代码统一用
conf := globalConf.Load().(*Config)读取,不能缓存该指针(对象可能被 GC) - 结构体字段必须全部导出,不能含
sync.Mutex或闭包等不可复制字段,否则unsafe.Pointer转换会崩溃
容器环境 inotify 资源耗尽会导致监听完全静默
现象是“改了配置文件,日志没打印任何变化”,也没有报错。Docker 默认的 fs.inotify.max_user_watches 通常只有 8192,一个服务监听十几个 YAML 文件就满了。
- 宿主机查限制:
cat /proc/sys/fs/inotify/max_user_watches - Docker 启动时加参数:
--sysctl fs.inotify.max_user_watches=524288 - Kubernetes 中通过
securityContext.sysctls设置,或降级为轮询兜底(仅限低频场景) - 监听路径必须是文件绝对路径,且确保挂载到容器内的 ConfigMap 文件权限为可读(
0644)
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











