Files
gf/os/gcfg/gcfg_adapter_content.go
Lance Add ac3efe5a00 feat(os/gcfg): Add file watcher with custom callback support (#4446)
为`gcfg`添加配置文件变更自定义回调,实现了`WatcherAdapter`接口,以下是`AdapterFile`的用法
test.yaml
```
b: "b"

```
```
package main

import (
	"fmt"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gcfg"
	"github.com/gogf/gf/v2/os/gctx"
)

func main() {
	ctx := gctx.New()
	file, _ := gcfg.NewAdapterFile("test.yaml")
	file.Data(ctx)
	file.AddWatcher("test", func() {
		value := file.MustGet(ctx, "b")
		fmt.Println(value.String())
	})
	server := g.Server()
	server.Run()
}
```
使用`g`和默认配置文件
```
	file := g.Cfg().GetAdapter().(*gcfg.AdapterFile)
	file.AddWatcher("test", func() {

	})
	file := g.Cfg().GetAdapter().(*gcfg.AdapterFile)
	file.RemoveWatcher("test")
```

注意:由于`gf`的`AdapterFile`使用的监听到文件变化删除缓存下一次重新初始化的懒加载方案,所有除了默认加载的`config.xxx`文件外,自定义的配置文件像`test.yaml`之类的都需要在`AddWatcher`前主动读取一次数据进行初始化监听(
`g.Cfg("test").Data(ctx)`)

---------

Co-authored-by: hailaz <739476267@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Hunk Zhu <hunk@joy999.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-15 16:59:52 +08:00

107 lines
3.5 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gcfg
import (
"context"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
)
var (
// Compile-time checking for interface implementation.
_ Adapter = (*AdapterContent)(nil)
_ WatcherAdapter = (*AdapterContent)(nil)
)
// AdapterContent implements interface Adapter using content.
// The configuration content supports the coding types as package `gjson`.
type AdapterContent struct {
jsonVar *gvar.Var // The pared JSON object for configuration content, type: *gjson.Json.
watchers *WatcherRegistry // Watchers for watching file changes.
}
// NewAdapterContent returns a new configuration management object using custom content.
// The parameter `content` specifies the default configuration content for reading.
func NewAdapterContent(content ...string) (*AdapterContent, error) {
a := &AdapterContent{
jsonVar: gvar.New(nil, true),
watchers: NewWatcherRegistry(),
}
if len(content) > 0 {
if err := a.SetContent(content[0]); err != nil {
return nil, err
}
}
return a, nil
}
// SetContent sets customized configuration content for specified `file`.
// The `file` is unnecessary param, default is DefaultConfigFile.
func (a *AdapterContent) SetContent(content string) error {
j, err := gjson.LoadContent([]byte(content), true)
if err != nil {
return gerror.Wrap(err, `load configuration content failed`)
}
a.jsonVar.Set(j)
adapterCtx := NewAdapterContentCtx().WithOperation(OperationSet).WithContent(content)
a.notifyWatchers(adapterCtx.Ctx)
return nil
}
// Available checks and returns the backend configuration service is available.
// The optional parameter `resource` specifies certain configuration resource.
//
// Note that this function does not return error as it just does simply check for
// backend configuration service.
func (a *AdapterContent) Available(ctx context.Context, resource ...string) (ok bool) {
return !a.jsonVar.IsNil()
}
// Get retrieves and returns value by specified `pattern` in current resource.
// Pattern like:
// "x.y.z" for map item.
// "x.0.y" for slice item.
func (a *AdapterContent) Get(ctx context.Context, pattern string) (value any, err error) {
if a.jsonVar.IsNil() {
return nil, nil
}
return a.jsonVar.Val().(*gjson.Json).Get(pattern).Val(), nil
}
// Data retrieves and returns all configuration data in current resource as map.
// Note that this function may lead lots of memory usage if configuration data is too large,
// you can implement this function if necessary.
func (a *AdapterContent) Data(ctx context.Context) (data map[string]any, err error) {
if a.jsonVar.IsNil() {
return nil, nil
}
return a.jsonVar.Val().(*gjson.Json).Var().Map(), nil
}
// AddWatcher adds a watcher for the specified configuration file.
func (a *AdapterContent) AddWatcher(name string, fn func(ctx context.Context)) {
a.watchers.Add(name, fn)
}
// RemoveWatcher removes the watcher for the specified configuration file.
func (a *AdapterContent) RemoveWatcher(name string) {
a.watchers.Remove(name)
}
// GetWatcherNames returns all watcher names.
func (a *AdapterContent) GetWatcherNames() []string {
return a.watchers.GetNames()
}
// notifyWatchers notifies all watchers.
func (a *AdapterContent) notifyWatchers(ctx context.Context) {
a.watchers.Notify(ctx)
}