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 ®istry.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)了解更多。