/
apollo.go
148 lines (136 loc) · 4.84 KB
/
apollo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// 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 apollo implements gcfg.Adapter using apollo service.
package apollo
import (
"context"
"github.com/apolloconfig/agollo/v4"
apolloConfig "github.com/apolloconfig/agollo/v4/env/config"
"github.com/apolloconfig/agollo/v4/storage"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"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/util/gconv"
)
// Config is the configuration object for apollo client.
type Config struct {
AppID string `v:"required"` // See apolloConfig.Config.
IP string `v:"required"` // See apolloConfig.Config.
Cluster string `v:"required"` // See apolloConfig.Config.
NamespaceName string // See apolloConfig.Config.
IsBackupConfig bool // See apolloConfig.Config.
BackupConfigPath string // See apolloConfig.Config.
Secret string // See apolloConfig.Config.
SyncServerTimeout int // See apolloConfig.Config.
MustStart bool // See apolloConfig.Config.
Watch bool // Watch watches remote configuration updates, which updates local configuration in memory immediately when remote configuration changes.
}
// Client implements gcfg.Adapter implementing using apollo service.
type Client struct {
config Config // Config object when created.
client agollo.Client // Apollo client.
value *g.Var // Configmap content cached. It is `*gjson.Json` value internally.
}
// New creates and returns gcfg.Adapter implementing using apollo service.
func New(ctx context.Context, config Config) (adapter gcfg.Adapter, err error) {
// Data validation.
err = g.Validator().Data(config).Run(ctx)
if err != nil {
return nil, err
}
if config.NamespaceName == "" {
config.NamespaceName = storage.GetDefaultNamespace()
}
client := &Client{
config: config,
value: g.NewVar(nil, true),
}
// Apollo client.
client.client, err = agollo.StartWithConfig(func() (*apolloConfig.AppConfig, error) {
return &apolloConfig.AppConfig{
AppID: config.AppID,
Cluster: config.Cluster,
NamespaceName: config.NamespaceName,
IP: config.IP,
IsBackupConfig: config.IsBackupConfig,
BackupConfigPath: config.BackupConfigPath,
Secret: config.Secret,
SyncServerTimeout: config.SyncServerTimeout,
MustStart: config.MustStart,
}, nil
})
if err != nil {
return nil, gerror.Wrapf(err, `create apollo client failed with config: %+v`, config)
}
if config.Watch {
client.client.AddChangeListener(client)
}
return client, 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 (c *Client) Available(ctx context.Context, resource ...string) (ok bool) {
if len(resource) == 0 && !c.value.IsNil() {
return true
}
var namespace = c.config.NamespaceName
if len(resource) > 0 {
namespace = resource[0]
}
return c.client.GetConfig(namespace) != nil
}
// 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 (c *Client) Get(ctx context.Context, pattern string) (value interface{}, err error) {
if c.value.IsNil() {
if err = c.updateLocalValue(ctx); err != nil {
return nil, err
}
}
return c.value.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 (c *Client) Data(ctx context.Context) (data map[string]interface{}, err error) {
if c.value.IsNil() {
if err = c.updateLocalValue(ctx); err != nil {
return nil, err
}
}
return c.value.Val().(*gjson.Json).Map(), nil
}
// OnChange is called when config changes.
func (c *Client) OnChange(event *storage.ChangeEvent) {
_ = c.updateLocalValue(gctx.New())
}
// OnNewestChange is called when any config changes.
func (c *Client) OnNewestChange(event *storage.FullChangeEvent) {
// Nothing to do.
}
func (c *Client) updateLocalValue(ctx context.Context) (err error) {
var j = gjson.New(nil)
cache := c.client.GetConfigCache(c.config.NamespaceName)
cache.Range(func(key, value interface{}) bool {
err = j.Set(gconv.String(key), value)
if err != nil {
return false
}
return true
})
cache.Clear()
if err == nil {
c.value.Set(j)
}
return
}