func addFileWatcher(fileWatcher filewatcher.FileWatcher, file string, callback func()) {
	_ = fileWatcher.Add(file)
	go func() {
		var timerC <-chan time.Time
		for {
			select {
			case <-timerC:
				timerC = nil
				callback()
			case <-fileWatcher.Events(file):
				if timerC == nil {
					timerC = time.After(100 * time.Millisecond)
				}
			}
		}
	}()
}
Add to the FileWatcher the provided file and execute the provided function
on any change event for this file.
Using a debouncing mechanism to avoid calling the callback multiple times
per event.
package config
import (
	"time"
	"istio.io/pkg/filewatcher"
)
var (
	watchers []WatchNotifyConfigFunc
)
type WatchNotifyConfigFunc func(cfg FRPNotifyConfig)
func RegisterConfigListener(watcher WatchNotifyConfigFunc) {
	watchers = append(watchers, watcher)
}
type FRPNotifyConfigController struct {
	bindAddress    string
	windowInterval int64
	configPath string
}
func NewConfigController(bindAddress string, windowInterval int64, configPath string) *FRPNotifyConfigController {
	return &FRPNotifyConfigController{
		bindAddress:    bindAddress,
		windowInterval: windowInterval,
		configPath:     configPath,
	}
}
func (c *FRPNotifyConfigController) Start(stop chan struct{}) {
	c.reload()
	fileWatcher := filewatcher.NewWatcher()
	defer fileWatcher.Close()
	addFileWatcher(fileWatcher, c.configPath, func() {
		c.reload()
	})
	<-stop
}
func (c *FRPNotifyConfigController) reload() {
	cfg := Load(c.configPath)
	cfg.BindAddress = c.bindAddress
	cfg.WindowInterval = c.windowInterval
	log.Debugf("config: %v", cfg)
	c.configEvent(*cfg)
}
func (c *FRPNotifyConfigController) configEvent(cfg FRPNotifyConfig) {
	for _, watcher := range watchers {
		watcher(cfg)
	}
}