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

     1  [English](naming.md) | 中文
     2  
     3  ## 前言
     4  
     5  像 tRPC-Go 大部分其他模块一样,名字服务模块也支持插件化。本文假定你已经阅读了 naming 包的 [README](/naming/README.zh_CN.md)。
     6  
     7  ## 插件化设计
     8  
     9  tRPC-Go 提供了 [`Selector`](/naming/selector) interface 作为名字服务的入口,并提供了一个默认实现 [`TrpcSelector`](/naming/selector/trpc_selector.go)。`TrpcSelector` 把 [`Discovery`](/naming/discovery)、[`ServiceRouter`](/naming/servicerouter)、[`Loadbalance`](/naming/loadbalance) 和 [`CircuitBreaker`](/naming/circuitbreaker) 组合起来。对每一个小模块,框架都提供了其对应的默认实现。
    10  
    11  通过[插件化](/plugin)方式,用户可以对 `Selector` 或它的各个小模块单独进行自定义。下面我们依次看看这是如何做到的。
    12  
    13  ## `Selector` 插件
    14  
    15  `Selector` interface 的定义如下:
    16  ```go
    17  type Selector interface {
    18  	Select(serviceName string, opts ...Option) (*registry.Node, error)
    19  	Report(node *registry.Node, cost time.Duration, err error) error
    20  }
    21  ```
    22  
    23  `Select` 方法通过 service name 返回对应的节点信息,可以通过 `opts` 传入一些选项。`Report` 上报调用情况,这些信息可能会影响之后 `Selector` 的结果,比如,对错误率太高的节点进行熔断。
    24  
    25  下面是一个简单的固定节点的 `Selector` 实现:
    26  ```go
    27  func init() {
    28      plugin.Register("my_selector", &Plugin{Nodes: make(map[string]string)})
    29  }
    30  
    31  type Plugin struct {
    32      Nodes map[string]string `yaml:"nodes"`
    33  }
    34  
    35  func (p *Plugin) Type() string { return "selector" }
    36  func (p *Plugin) Setup(name string, dec plugin.Decoder) error {
    37      if err := dec.Decode(p); err != nil {
    38          return err
    39      }
    40      selector.Register(name, p)
    41      return nil
    42  }
    43  
    44  func (p *Plugin) Select(serviceName string, opts ...selector.Option) (*registry.Node, error) {
    45      if node, ok := p.Nodes[serviceName]; ok {
    46          return &registry.Node{Address: node, ServiceName: serviceName}, nil
    47      }
    48      return nil, fmt.Errorf("unknown service %s", serviceName)
    49  }
    50  
    51  func (p *Plugin) Report(*registry.Node, time.Duration, error) error {
    52      return nil
    53  }
    54  ```
    55  使用时,需要匿名 import 上面的 plugin 包保证 `init` 函数成功注册 `Plugin`,并在 `trpc_go.yaml` 中加入下面的配置项:
    56  ```yaml
    57  client:
    58    service:
    59      - name: xxx
    60        target: "my_selector://service1"
    61        # ... 忽略其他配置
    62      - name: yyy
    63        target: "my_selector://service2"
    64        # ... 忽略其他配置
    65  
    66  plugins:
    67    selector:
    68      my_selector:
    69        nodes:
    70          service1: 127.0.0.1:8000
    71          service2: 127.0.0.1:8001
    72  ```
    73  这样,client `xxx` 就会访问到 `127.0.0.1:8000`,client `yyy` 则会访问到 `127.0.0.1:8001`。
    74  
    75  ## `Discovery` 插件
    76  
    77  `Discovery` 的接口定义如下:
    78  ```go
    79  type Discovery interface {
    80      List(serviceName string, opt ...Option) (nodes []*registry.Node, err error)
    81  }
    82  ```
    83  `List` 根据 service name 列出一组 nodes 供后续 ServiceRouter 和 LoadBalance 选择。
    84  
    85  `Discovery` 插件的代码实现与 `Selector` 类似,这里不再赘述。
    86  
    87  为了让 Discovery 生效,你还需要在下面两项选择其一:
    88  - 如果你使用默认的 `TrpcSelector`,需要在 yaml 中加入下面配置:
    89    ```yaml
    90    client:
    91      service:
    92        - name: service1  # 注意,这里 name 直接填了 service1,而不是 xxx,我们将直接用该字段进行寻址
    93          # target: ...  # 注意,这里不能使用 target,而是要用上面的 name 字段去寻址
    94          discovery: my_discovery
    95    ```
    96  - 如果默认的 `TrpcSelector` 不满足你的需求,可以像上节一样自定义 Selector,但是,你必须正确处理 `Select` 方法的 `Option`,即 `selector.WithDiscovery`。
    97  
    98  ## `ServiceRouter` `LoadBalance` 和 `CircuitBreaker` 插件
    99  
   100  其他这些插件的实现方式与 `Discovery` 类似。要么使用 `TrpcSelector` 并在 `yaml.client.service[i]` 中设置对应的字段;要么在你自己实现的 `Selector` 中处理 `selector.WithXxx`。
   101  
   102  ## Polaris Mesh 插件
   103  
   104  tRPC-Go 支持 Polaris Mesh 插件,你可以在[这里](https://github.com/trpc-ecosystem/go-naming-polarismesh)了解更多。