trpc.group/trpc-go/trpc-go@v1.0.3/docs/developer_guide/develop_plugins/config.zh_CN.md (about)

     1  [English](config.md) | 中文
     2  
     3  # 怎么开发一个 config 类型的插件
     4  
     5  本指南将介绍如何开发一个依赖配置进行加载的 config 类型的插件。 
     6  
     7  `config` 包提供两套不同的配置接口,`config.DataProvider` 和  `config.KVConfig`。
     8  本指南以开发 `KVConfig` 类型的配置为例,`DataProvider` 类型的配置与之类似。
     9  
    10  开发该插件需要实现以下两个子功能:
    11  
    12  - 实现插件依赖配置进行加载,详细说明请参考 [plugin](/plugin/README.zh_CN.md)
    13  - 实现 `config.KVConfig` 接口,并将实现注册到 `config` 包
    14  
    15  下面以 [trpc-config-etcd](https://github.com/trpc-ecosystem/go-config-etcd) 为例,来介绍相关开发步骤。
    16  
    17  ## 实现插件依赖配置进行加载
    18  
    19  ### 1. 确定插件的配置
    20  
    21  下面是在 "trpc_go.yaml" 配置文件中设置 "Endpoint" 和 "Dialtimeout" 的配置示例:
    22  
    23  ```yaml
    24  plugins:                 
    25    config:
    26      etcd:
    27        endpoints:
    28          - localhost:2379
    29        dialtimeout: 5s
    30  ```
    31  
    32  ```go
    33  const (
    34      pluginName = "etcd"
    35      pluginType = "config"
    36  )
    37  ```
    38  
    39  插件是基于[etcd-client](https://github.com/etcd-io/etcd/tree/main/client/v3) 封装的, 因此完整的配置见 [Config](https://github.com/etcd-io/etcd/blob/client/v3.5.9/client/v3/config.go#L26)。
    40  
    41  ### 2. 实现 `plugin.Factory` 接口
    42  
    43  ```go
    44  // etcdPlugin etcd Configuration center plugin.
    45  type etcdPlugin struct{}
    46  
    47  // Type implements plugin.Factory.
    48  func (p *etcdPlugin) Type() string {
    49      return pluginType
    50  }
    51  
    52  // Setup implements plugin.Factory.
    53  func (p *etcdPlugin) Setup(name string, decoder plugin.Decoder) error {
    54      cfg := clientv3.Config{}
    55      err := decoder.Decode(&cfg)
    56      if err != nil {
    57          return err
    58      }
    59      c, err := New(cfg)
    60      if err != nil {
    61          return err
    62      }
    63      config.SetGlobalKV(c)
    64      config.Register(c)
    65      return nil
    66  }
    67  ```
    68  
    69  ### 3. 调用 `plugin.Register` 把插件自己注册到 `plugin` 包
    70  
    71  ```go
    72  func init() {
    73  	plugin.Register(pluginName, NewPlugin())
    74  }
    75  ```
    76  
    77  ## 实现 `config.KVConfig` 接口,并将实现注册到 `config` 包
    78  
    79  ### 1. 实现 `config.KVConfig` 接口
    80  
    81  插件暂时只支持 Watch 和 Get 读操作,不支持 Put和 Del 写操作。
    82  
    83  ```go
    84  // Client etcd client.
    85  type Client struct {
    86      cli *clientv3.Client
    87  }
    88  
    89  // Name returns plugin name.
    90  func (c *Client) Name() string {
    91      return pluginName
    92  }
    93  
    94  // Get Obtains the configuration content value according to the key, and implement the config.KV interface.
    95  func (c *Client) Get(ctx context.Context, key string, _ ...config.Option) (config.Response, error) {
    96      result, err := c.cli.Get(ctx, key)
    97      if err != nil {
    98          return nil, err
    99      }
   100      rsp := &getResponse{
   101          md: make(map[string]string),
   102      }
   103  
   104      if result.Count > 1 {
   105      // TODO: support multi keyvalues
   106          return nil, ErrNotImplemented
   107      }
   108  
   109      for _, v := range result.Kvs {
   110          rsp.val = string(v.Value)
   111      }
   112      return rsp, nil
   113  }
   114  
   115  // Watch monitors configuration changes and implements the config.Watcher interface.
   116  func (c *Client) Watch(ctx context.Context, key string, opts ...config.Option) (<-chan config.Response, error) {
   117      rspCh := make(chan config.Response, 1)
   118      go c.watch(ctx, key, rspCh)
   119      return rspCh, nil
   120  }
   121  
   122  // watch adds watcher for etcd changes.
   123  func (c *Client) watch(ctx context.Context, key string, rspCh chan config.Response) {
   124      rch := c.cli.Watch(ctx, key)
   125      for r := range rch {
   126          for _, ev := range r.Events {
   127              rsp := &watchResponse{
   128                  val:       string(ev.Kv.Value),
   129                  md:        make(map[string]string),
   130                  eventType: config.EventTypeNull,
   131              }
   132              switch ev.Type {
   133                  case clientv3.EventTypePut:
   134                      rsp.eventType = config.EventTypePut
   135                  case clientv3.EventTypeDelete:
   136                      rsp.eventType = config.EventTypeDel
   137                  default:
   138              }
   139              rspCh <- rsp
   140          }
   141      }
   142  }
   143  
   144  // ErrNotImplemented not implemented error
   145  var ErrNotImplemented = errors.New("not implemented")
   146  
   147  // Put creates or updates the configuration content value to implement the config.KV interface.
   148  func (c *Client) Put(ctx context.Context, key, val string, opts ...config.Option) error {
   149      return ErrNotImplemented
   150  }
   151  
   152  // Del deletes the configuration item key and implement the config.KV interface.
   153  func (c *Client) Del(ctx context.Context, key string, opts ...config.Option) error {
   154      return ErrNotImplemented
   155  }
   156  ```
   157  
   158  ### 2. 将实现的 `config.KVConfig` 注册到 config 包
   159  
   160  `*etcdPlugin.Setup` 函数中已经调用了 `config.Register` 和 `config.SetGlobalKV`。
   161  
   162  ```go
   163  config.SetGlobalKV(c)
   164  config.Register(c)
   165  ```