Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5caa12f
feat(gcfg): 添加配置监听器功能
LanceAdd Sep 23, 2025
67483fc
feat(gcfg):重构配置适配器以支持上下文传递
LanceAdd Sep 27, 2025
80e31e5
Merge branch 'master' into fix/gcfg-watcher
hailaz Sep 28, 2025
e6ea72e
fix(os/gcfg): 更新操作上下文类型以支持gctx.StrKey
hailaz Sep 28, 2025
fc83375
fix(os/gcfg): 修复获取Operation Value类型
LanceAdd Sep 28, 2025
fc76c8c
fix(os/gcfg): 整 WithOperation 签名参数类型从 gctx.StrKey 到 string 以简化接口定义
LanceAdd Sep 28, 2025
d3b09ba
Apply gci import order changes
github-actions[bot] Sep 28, 2025
e60ed41
fix(contrib/config/nacos): 完善单元测试
LanceAdd Sep 28, 2025
c594e61
fix(contrib/config/nacos): 完善单元测试
LanceAdd Sep 28, 2025
8b482fc
Merge branch 'master' into fix/gcfg-watcher
joy999 Sep 28, 2025
d4f5b87
fix(contrib/config/nacos): 优化配置变更通知逻辑,简化观察者调用
hailaz Sep 29, 2025
cf35fbf
fix(os/gcfg): 添加接口实现的编译时检查
hailaz Sep 29, 2025
4294f9f
fix(contrib/config/nacos): 修复上下文获取方法,增加类型安全检查
hailaz Sep 29, 2025
6734a9a
fix(contrib/config/polaris): - 在 polaris 客户端中实现配置监听器回调功能,补充缺少的注释
LanceAdd Sep 29, 2025
c4eb06c
Merge branch 'gogf:master' into fix/gcfg-watcher
LanceAdd Sep 29, 2025
5972a83
Merge branch 'master' into fix/gcfg-watcher
hailaz Sep 30, 2025
06fc475
Merge branch 'master' into fix/gcfg-watcher
hailaz Oct 10, 2025
0e0d431
feat(contrib/config): 为 Apollo 和 Consul 配置中心添加监听器管理机制
LanceAdd Oct 10, 2025
d422c23
Apply gci import order changes
github-actions[bot] Oct 10, 2025
ac63193
feat(consul): 实现配置接口和监听接口
LanceAdd Oct 10, 2025
e7984b5
fix: 优化上下文设置方法,简化参数处理
hailaz Oct 10, 2025
4e839d0
refactor: 统一适配器上下文创建方法,简化代码结构
hailaz Oct 10, 2025
a84162c
refactor: 重命名适配器上下文方法,统一内容设置和获取逻辑
hailaz Oct 10, 2025
ad32a9e
refactor: 统一适配器上下文操作常量,简化代码结构
hailaz Oct 10, 2025
b31b5d9
refactor: 优化配置变更函数中的断言逻辑,提升可读性
hailaz Oct 10, 2025
6e546f6
feat(kubecm): 实现Kubecm配置监听和上下文适配器
LanceAdd Oct 13, 2025
902c698
Apply gci import order changes
github-actions[bot] Oct 13, 2025
851add6
feat(kubecm): 添加编译时接口实现检查
LanceAdd Oct 13, 2025
aaa9ad1
feat(kubecm): 添加注释
LanceAdd Oct 13, 2025
6863030
Merge branch 'master' into fix/gcfg-watcher
hailaz Oct 14, 2025
b7716a9
Merge branch 'master' into fix/gcfg-watcher
hailaz Oct 15, 2025
4f75591
Apply suggestion from @Copilot
hailaz Oct 15, 2025
f9659bf
Apply suggestion from @Copilot
hailaz Oct 15, 2025
c7c72c9
feat: 重构配置适配器,使用WatcherRegistry管理观察者
hailaz Oct 15, 2025
5d4a6a6
Apply gci import order changes
github-actions[bot] Oct 15, 2025
60d38de
refactor: 清理代码,移除多余的空行
hailaz Oct 15, 2025
463b5bb
Merge branch 'master' into fix/gcfg-watcher
hailaz Oct 15, 2025
258ab00
test(os/gcfg): 用 WaitGroup 替代 time.Sleep 同步 watcher 测试
hailaz Oct 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions contrib/config/nacos/nacos.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/vo"

"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
Expand All @@ -32,9 +33,10 @@ type Config struct {

// Client implements gcfg.Adapter implementing using nacos service.
type Client struct {
config Config // Config object when created.
client config_client.IConfigClient // Nacos config client.
value *g.Var // Configmap content cached. It is `*gjson.Json` value internally.
config Config // Config object when created.
client config_client.IConfigClient // Nacos config client.
value *g.Var // Configmap content cached. It is `*gjson.Json` value internally.
watchers *gmap.StrAnyMap // Watchers for watching file changes.
}

// New creates and returns gcfg.Adapter implementing using nacos service.
Expand All @@ -46,8 +48,9 @@ func New(ctx context.Context, config Config) (adapter gcfg.Adapter, err error) {
}

client := &Client{
config: config,
value: g.NewVar(nil, true),
config: config,
value: g.NewVar(nil, true),
watchers: gmap.NewStrAnyMap(true),
}

client.client, err = clients.CreateConfigClient(map[string]any{
Expand Down Expand Up @@ -131,6 +134,13 @@ func (c *Client) addWatcher() error {
if c.config.OnConfigChange != nil {
go c.config.OnConfigChange(namespace, group, dataId, data)
}
c.watchers.Iterator(func(k string, v any) bool {
if watcherFunc, ok := v.(func(ctx context.Context)); ok {
adapterCtx := NewNacosAdapterCtx().WithOperation(OperationUpdate).WithNamespace(namespace).WithGroup(group).WithDataId(dataId).WithSetContent(data)
go watcherFunc(adapterCtx.Ctx)
}
return true
})
}

if err := c.client.ListenConfig(c.config.ConfigParam); err != nil {
Expand All @@ -139,3 +149,27 @@ func (c *Client) addWatcher() error {

return nil
}

func (c *Client) AddWatcher(name string, f func(ctx context.Context)) {
c.watchers.Set(name, f)
}

// RemoveWatcher removes the watcher for the specified configuration file.
func (c *Client) RemoveWatcher(name string) {
c.watchers.Remove(name)
}

// GetWatcherNames returns all watcher names.
func (c *Client) GetWatcherNames() []string {
return c.watchers.Keys()
}

// notifyWatchers notifies all watchers.
func (c *Client) notifyWatchers(ctx context.Context) {
c.watchers.Iterator(func(k string, v any) bool {
if fn, ok := v.(func(ctx context.Context)); ok {
go fn(ctx)
}
return true
})
}
123 changes: 123 additions & 0 deletions contrib/config/nacos/nacos_adapter_ctx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package nacos

import (
"context"

"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/gctx"
)

const (
// KeyNamespace is the context key for namespace
KeyNamespace gctx.StrKey = "namespace"
// KeyGroup is the context key for group
KeyGroup gctx.StrKey = "group"
// KeyDataId is the context key for dataId
KeyDataId gctx.StrKey = "dataId"
)

const (
// OperationUpdate represents the update operation
OperationUpdate = "update"
)

// NacosAdapterCtx is the context adapter for Nacos configuration
type NacosAdapterCtx struct {
Ctx context.Context
}

// NewNacosAdapterCtx creates and returns a new NacosAdapterCtx.
// If context is provided, it will be used; otherwise, a background context is created.
func NewNacosAdapterCtx(ctx ...context.Context) *NacosAdapterCtx {
if len(ctx) > 0 {
return &NacosAdapterCtx{
Ctx: ctx[0],
}
}
return &NacosAdapterCtx{Ctx: context.Background()}
}

// GetNacosAdapterCtx creates a new NacosAdapterCtx with the given context
func GetNacosAdapterCtx(ctx context.Context) *NacosAdapterCtx {
return &NacosAdapterCtx{Ctx: ctx}
}

// WithOperation sets the operation in the context
func (n *NacosAdapterCtx) WithOperation(operation ...string) *NacosAdapterCtx {
if len(operation) > 0 {
n.Ctx = context.WithValue(n.Ctx, gcfg.OperationWrite, operation[0])
}
return n
}

// WithNamespace sets the namespace in the context
func (n *NacosAdapterCtx) WithNamespace(namespace ...string) *NacosAdapterCtx {
if len(namespace) > 0 {
n.Ctx = context.WithValue(n.Ctx, KeyNamespace, namespace[0])
}
return n
}

// WithGroup sets the group in the context
func (n *NacosAdapterCtx) WithGroup(group ...string) *NacosAdapterCtx {
if len(group) > 0 {
n.Ctx = context.WithValue(n.Ctx, KeyGroup, group[0])
}
return n
}

// WithDataId sets the dataId in the context
func (n *NacosAdapterCtx) WithDataId(dataId ...string) *NacosAdapterCtx {
if len(dataId) > 0 {
n.Ctx = context.WithValue(n.Ctx, KeyDataId, dataId[0])
}
return n
}

// WithSetContent sets the content in the context
func (n *NacosAdapterCtx) WithSetContent(content ...string) *NacosAdapterCtx {
if len(content) > 0 {
n.Ctx = context.WithValue(n.Ctx, gcfg.KeySetContent, content[0])
}
return n
}

// GetNamespace retrieves the namespace from the context
func (n *NacosAdapterCtx) GetNamespace() string {
if v := n.Ctx.Value(KeyNamespace); v != nil {
return v.(string)
}
return ""
}

// GetGroup retrieves the group from the context
func (n *NacosAdapterCtx) GetGroup() string {
if v := n.Ctx.Value(KeyGroup); v != nil {
return v.(string)
}
return ""
}

// GetDataId retrieves the dataId from the context
func (n *NacosAdapterCtx) GetDataId() string {
if v := n.Ctx.Value(KeyDataId); v != nil {
return v.(string)
}
return ""
}

// GetSetContent retrieves the content from the context
func (n *NacosAdapterCtx) GetSetContent() string {
if v := n.Ctx.Value(gcfg.KeySetContent); v != nil {
return v.(string)
}
return ""
}

// GetOperation retrieves the operation from the context
func (n *NacosAdapterCtx) GetOperation() string {
if v := n.Ctx.Value(OperationUpdate); v != nil {
return v.(string)
}
return ""
}
15 changes: 15 additions & 0 deletions contrib/config/nacos/nacos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package nacos_test

import (
"context"
"net/url"
"testing"
"time"
Expand All @@ -16,6 +17,7 @@ import (

"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/guid"
Expand Down Expand Up @@ -76,6 +78,16 @@ func TestNacosOnConfigChangeFunc(t *testing.T) {
gtest.Assert("gf", g.Cfg().MustGet(gctx.GetInitCtx(), "app.name").String())
},
})
if watcherAdapter, ok := adapter.(gcfg.WatcherAdapter); ok {
watcherAdapter.AddWatcher("test", func(ctx context.Context) {
adapterCtx := nacos.GetNacosAdapterCtx(ctx)
gtest.Assert("public", adapterCtx.GetNamespace())
gtest.Assert("test", adapterCtx.GetGroup())
gtest.Assert("config.toml", adapterCtx.GetDataId())
gtest.Assert(nacos.OperationUpdate, adapterCtx.GetOperation())
gtest.Assert("gf", g.Cfg().MustGet(gctx.GetInitCtx(), "app.name").String())
})
}
g.Cfg().SetAdapter(adapter)
t.Assert(g.Cfg().Available(ctx), true)
appName, err := g.Cfg().Get(ctx, "app.name")
Expand All @@ -97,5 +109,8 @@ func TestNacosOnConfigChangeFunc(t *testing.T) {
t.AssertNil(err)
_, err = g.Client().Post(ctx, configPublishUrl+"&content="+url.QueryEscape(res2))
t.AssertNil(err)
if watcherAdapter, ok := adapter.(gcfg.WatcherAdapter); ok {
t.Assert(watcherAdapter.GetWatcherNames()[0], "test")
}
})
}
10 changes: 10 additions & 0 deletions os/gcfg/gcfg_adaper.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,13 @@ type Adapter interface {
// you can implement this function if necessary.
Data(ctx context.Context) (data map[string]any, err error)
}

// WatcherAdapter is the interface for configuration watcher.
type WatcherAdapter interface {
// AddWatcher adds a watcher function for specified `pattern` and `resource`.
AddWatcher(name string, fn func(ctx context.Context))
// RemoveWatcher removes the watcher function for specified `pattern` and `resource`.
RemoveWatcher(name string)
// GetWatcherNames returns all watcher names.
GetWatcherNames() []string
}
34 changes: 32 additions & 2 deletions os/gcfg/gcfg_adapter_content.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package gcfg
import (
"context"

"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
Expand All @@ -17,14 +18,16 @@ import (
// 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.
jsonVar *gvar.Var // The pared JSON object for configuration content, type: *gjson.Json.
watchers *gmap.StrAnyMap // 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),
jsonVar: gvar.New(nil, true),
watchers: gmap.NewStrAnyMap(true),
}
if len(content) > 0 {
if err := a.SetContent(content[0]); err != nil {
Expand All @@ -42,6 +45,8 @@ func (a *AdapterContent) SetContent(content string) error {
return gerror.Wrap(err, `load configuration content failed`)
}
a.jsonVar.Set(j)
adapterCtx := NewAdapterContentCtx().WithOperation(OperationSet).WithSetContent(content)
a.notifyWatchers(adapterCtx.Ctx)
return nil
}

Expand Down Expand Up @@ -74,3 +79,28 @@ func (a *AdapterContent) Data(ctx context.Context) (data map[string]any, err err
}
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.Set(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.Keys()
}

// notifyWatchers notifies all watchers.
func (a *AdapterContent) notifyWatchers(ctx context.Context) {
a.watchers.Iterator(func(k string, v any) bool {
if fn, ok := v.(func(ctx context.Context)); ok {
go fn(ctx)
}
return true
})
}
Comment thread
hailaz marked this conversation as resolved.
70 changes: 70 additions & 0 deletions os/gcfg/gcfg_adapter_content_ctx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// 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 provides reading, caching and managing for configuration.
package gcfg

import (
"context"
)

// AdapterContentCtx is the context for AdapterContent.
type AdapterContentCtx struct {
// Ctx is the context with configuration values
Ctx context.Context
}

// NewAdapterContentCtx creates and returns a new AdapterContentCtx.
// If ctx is provided, it uses that context, otherwise it creates a background context.
func NewAdapterContentCtx(ctx ...context.Context) *AdapterContentCtx {
if len(ctx) > 0 {
return &AdapterContentCtx{
Ctx: ctx[0],
}
}
return &AdapterContentCtx{Ctx: context.Background()}
}

// GetAdapterContentCtx creates and returns an AdapterContentCtx with the given context.
func GetAdapterContentCtx(ctx context.Context) *AdapterContentCtx {
return &AdapterContentCtx{Ctx: ctx}
}

// WithOperation sets the operation in the context and returns the updated AdapterContentCtx.
// If operation is not provided, it does nothing.
func (a *AdapterContentCtx) WithOperation(operation ...string) *AdapterContentCtx {
Comment thread
hailaz marked this conversation as resolved.
Outdated
if len(operation) > 0 {
a.Ctx = context.WithValue(a.Ctx, KeyOperation, operation[0])
}
return a
}

// WithSetContent sets the content in the context and returns the updated AdapterContentCtx.
// If content is not provided, it does nothing.
func (a *AdapterContentCtx) WithSetContent(content ...string) *AdapterContentCtx {
if len(content) > 0 {
a.Ctx = context.WithValue(a.Ctx, KeySetContent, content[0])
}
return a
}

// GetOperation retrieves the operation from the context.
// Returns empty string if not found.
func (a *AdapterContentCtx) GetOperation() string {
if v := a.Ctx.Value(KeyOperation); v != nil {
return v.(string)
}
return ""
}

// GetSetContent retrieves the content from the context.
// Returns empty string if not found.
func (a *AdapterContentCtx) GetSetContent() string {
if v := a.Ctx.Value(KeySetContent); v != nil {
return v.(string)
}
return ""
}
Loading
Loading