var _ config.Watcher = (*watcher)(nil)
type watcher struct {
ctx context.Context
cancel context.CancelFunc
}
func NewWatcher() (config.Watcher, error) {
ctx, cancel := context.WithCancel(context.Background())
return &watcher{ctx: ctx, cancel: cancel}, nil
}
....具体实现的方法.....
这行代码var _ config.Watcher = (*watcher)(nil)在watcher.go文件中用于编译时的接口满足性检查。它确保watcher类型实现了config.Watcher接口。如果watcher没有实现config.Watcher接口的所有方法,编译器将报错。这是一种静态检查手段,用于在不生成任何运行时代码的情况下,提前发现类型与接口实现上的错误。
监听配置:
watch机制 如果是nacos Nacos 的 OnChange 事件处理通常是通过监听配置变化来实现的。在 Nacos 客户端中,当配置发生变化时,Nacos 服务端会通知客户端,客户端收到通知后会触发相应的 OnChange 事件。下面是一个简化的步骤说明和示例代码,展示如何在使用 Nacos 作为配置中心时实现 OnChange 事件的监听。
func (c *Config) Watch() (config.Watcher, error) {
watcher := newWatcher(context.Background(), c.opts.dataID, c.opts.group, c.client.CancelListenConfig)
err := c.client.ListenConfig(vo.ConfigParam{
DataId: c.opts.dataID,
Group: c.opts.group,
OnChange: func(_, group, dataId, data string) {
if dataId == watcher.dataID && group == watcher.group {
watcher.content <- data
}
},
})
if err != nil {
return nil, err
}
return watcher, nil
}
如果是文件配置:
基于fsnotify事件通知
func newWatcher(f *file) (config.Watcher, error) {
fw, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
if err := fw.Add(f.path); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.Background())
return &watcher{f: f, fw: fw, ctx: ctx, cancel: cancel}, nil
}
文件的变更的事件通知是通过fsnotify,nacos事件变更是通过OnChange(),但是你发现依旧还是起了个协程go watch去循环监听配置变动,因为
1.兼容性:nacos OnChange 或 fsnotify 提供的是事件驱动的通知机制,但在某些情况下,可能需要兼容不支持这些机制的环境或配置源。在这种情况下,watch 函数内的循环可以作为后备方案,确保配置变化仍然可以被检测到。
2.鲁棒性:即使使用了事件驱动的通知机制,也可能会遇到监听失败或遗漏事件的情况。例如,fsnotify 在某些特定的文件系统事件下可能不会触发通知。通过在 watch 函数中使用循环,可以增加一层保障,确保即使事件通知失败,系统也能通过轮询机制捕捉到变化。
func (c *config) Load() error {
for _, src := range c.opts.sources {
kvs, err := src.Load()
if err != nil {
return err
}
for _, v := range kvs {
log.Debugf("config loaded: %s format: %s", v.Key, v.Format)
}
if err = c.reader.Merge(kvs...); err != nil {
log.Errorf("failed to merge config source: %v", err)
return err
}
w, err := src.Watch()
if err != nil {
log.Errorf("failed to watch config source: %v", err)
return err
}
c.watchers = append(c.watchers, w)
go c.watch(w)
}
if err := c.reader.Resolve(); err != nil {
log.Errorf("failed to resolve config source: %v", err)
return err
}
return nil
}
func (c *config) watch(w Watcher) {
for {
kvs, err := w.Next()
if err != nil {
if errors.Is(err, context.Canceled) {
log.Infof("watcher's ctx cancel : %v", err)
return
}
time.Sleep(time.Second)
log.Errorf("failed to watch next config: %v", err)
continue
}
if err := c.reader.Merge(kvs...); err != nil {
log.Errorf("failed to merge next config: %v", err)
continue
}
if err := c.reader.Resolve(); err != nil {
log.Errorf("failed to resolve next config: %v", err)
continue
}
c.cached.Range(func(key, value interface{}) bool {
k := key.(string)
v := value.(Value)
if n, ok := c.reader.Value(k); ok && reflect.TypeOf(n.Load()) == reflect.TypeOf(v.Load()) && !reflect.DeepEqual(n.Load(), v.Load()) {
v.Store(n.Load())
if o, ok := c.observers.Load(k); ok {
o.(Observer)(k, v)
}
}
return true
})
}
}
etcd watch机制
<https://www.lixueduan.com/posts/etcd/05-watch/>
PolarisMesh watch
consule