English | 中文
This guide will introduce how to develop a config type plugin that relies on configuration for loading.
The config
package provides two sets of different configuration interfaces, config.DataProvider
and config.KVConfig
.
This guide uses the development of the KVConfig
type configuration as an example, and the development of the DataProvider
type configuration is similar.
Developing this plugin requires implementing the following two sub-functions:
- Implement loading the plugin by relying on configuration. For details, please refer to plugin.
- Implement the
config.KVConfi
g interface and register the implementation with theconfig
package.
The following steps will introduce the relevant development steps using trpc-config-etcd as an example.
The following is an example of setting the "Endpoint" and "Dialtimeout" configurations in the "trpc_go.yaml" configuration file:
plugins:
config:
etcd:
endpoints:
- localhost:2379
dialtimeout: 5s
The plugin is encapsulated based on etcd-client, so the complete configuration is in Config.
// etcdPlugin etcd Configuration center plugin.
type etcdPlugin struct{}
// Type implements plugin.Factory.
func (p *etcdPlugin) Type() string {
return pluginType
}
// Setup implements plugin.Factory.
func (p *etcdPlugin) Setup(name string, decoder plugin.Decoder) error {
cfg := clientv3.Config{}
err := decoder.Decode(&cfg)
if err != nil {
return err
}
c, err := New(cfg)
if err != nil {
return err
}
config.SetGlobalKV(c)
config.Register(c)
return nil
}
func init() {
plugin.Register(pluginName, NewPlugin())
}
The plugin currently only supports Watch and Get read operations and does not support Put and Del write operations.
// Client etcd client.
type Client struct {
cli *clientv3.Client
}
// Name returns plugin name.
func (c *Client) Name() string {
return pluginName
}
// Get Obtains the configuration content value according to the key, and implement the config.KV interface.
func (c *Client) Get(ctx context.Context, key string, _ ...config.Option) (config.Response, error) {
result, err := c.cli.Get(ctx, key)
if err != nil {
return nil, err
}
rsp := &getResponse{
md: make(map[string]string),
}
if result.Count > 1 {
// TODO: support multi keyvalues
return nil, ErrNotImplemented
}
for _, v := range result.Kvs {
rsp.val = string(v.Value)
}
return rsp, nil
}
// Watch monitors configuration changes and implements the config.Watcher interface.
func (c *Client) Watch(ctx context.Context, key string, opts ...config.Option) (<-chan config.Response, error) {
rspCh := make(chan config.Response, 1)
go c.watch(ctx, key, rspCh)
return rspCh, nil
}
// watch adds watcher for etcd changes.
func (c *Client) watch(ctx context.Context, key string, rspCh chan config.Response) {
rch := c.cli.Watch(ctx, key)
for r := range rch {
for _, ev := range r.Events {
rsp := &watchResponse{
val: string(ev.Kv.Value),
md: make(map[string]string),
eventType: config.EventTypeNull,
}
switch ev.Type {
case clientv3.EventTypePut:
rsp.eventType = config.EventTypePut
case clientv3.EventTypeDelete:
rsp.eventType = config.EventTypeDel
default:
}
rspCh <- rsp
}
}
}
// ErrNotImplemented not implemented error
var ErrNotImplemented = errors.New("not implemented")
// Put creates or updates the configuration content value to implement the config.KV interface.
func (c *Client) Put(ctx context.Context, key, val string, opts ...config.Option) error {
return ErrNotImplemented
}
// Del deletes the configuration item key and implement the config.KV interface.
func (c *Client) Del(ctx context.Context, key string, opts ...config.Option) error {
return ErrNotImplemented
}
The *etcdPlugin.Setup
function has already called config.Register
and config.SetGlobalKV
.
config.SetGlobalKV(c)
config.Register(c)