English | 中文
像 tRPC-Go 大部分其他模块一样,名字服务模块也支持插件化。本文假定你已经阅读了 naming 包的 README。
tRPC-Go 提供了 Selector
interface 作为名字服务的入口,并提供了一个默认实现 TrpcSelector
。TrpcSelector
把 Discovery
、ServiceRouter
、Loadbalance
和 CircuitBreaker
组合起来。对每一个小模块,框架都提供了其对应的默认实现。
通过插件化方式,用户可以对 Selector
或它的各个小模块单独进行自定义。下面我们依次看看这是如何做到的。
Selector
interface 的定义如下:
type Selector interface {
Select(serviceName string, opts ...Option) (*registry.Node, error)
Report(node *registry.Node, cost time.Duration, err error) error
}
Select
方法通过 service name 返回对应的节点信息,可以通过 opts
传入一些选项。Report
上报调用情况,这些信息可能会影响之后 Selector
的结果,比如,对错误率太高的节点进行熔断。
下面是一个简单的固定节点的 Selector
实现:
func init() {
plugin.Register("my_selector", &Plugin{Nodes: make(map[string]string)})
}
type Plugin struct {
Nodes map[string]string `yaml:"nodes"`
}
func (p *Plugin) Type() string { return "selector" }
func (p *Plugin) Setup(name string, dec plugin.Decoder) error {
if err := dec.Decode(p); err != nil {
return err
}
selector.Register(name, p)
return nil
}
func (p *Plugin) Select(serviceName string, opts ...selector.Option) (*registry.Node, error) {
if node, ok := p.Nodes[serviceName]; ok {
return ®istry.Node{Address: node, ServiceName: serviceName}, nil
}
return nil, fmt.Errorf("unknown service %s", serviceName)
}
func (p *Plugin) Report(*registry.Node, time.Duration, error) error {
return nil
}
使用时,需要匿名 import 上面的 plugin 包保证 init
函数成功注册 Plugin
,并在 trpc_go.yaml
中加入下面的配置项:
client:
service:
- name: xxx
target: "my_selector://service1"
# ... 忽略其他配置
- name: yyy
target: "my_selector://service2"
# ... 忽略其他配置
plugins:
selector:
my_selector:
nodes:
service1: 127.0.0.1:8000
service2: 127.0.0.1:8001
这样,client xxx
就会访问到 127.0.0.1:8000
,client yyy
则会访问到 127.0.0.1:8001
。
Discovery
的接口定义如下:
type Discovery interface {
List(serviceName string, opt ...Option) (nodes []*registry.Node, err error)
}
List
根据 service name 列出一组 nodes 供后续 ServiceRouter 和 LoadBalance 选择。
Discovery
插件的代码实现与 Selector
类似,这里不再赘述。
为了让 Discovery 生效,你还需要在下面两项选择其一:
- 如果你使用默认的
TrpcSelector
,需要在 yaml 中加入下面配置:client: service: - name: service1 # 注意,这里 name 直接填了 service1,而不是 xxx,我们将直接用该字段进行寻址 # target: ... # 注意,这里不能使用 target,而是要用上面的 name 字段去寻址 discovery: my_discovery
- 如果默认的
TrpcSelector
不满足你的需求,可以像上节一样自定义 Selector,但是,你必须正确处理Select
方法的Option
,即selector.WithDiscovery
。
其他这些插件的实现方式与 Discovery
类似。要么使用 TrpcSelector
并在 yaml.client.service[i]
中设置对应的字段;要么在你自己实现的 Selector
中处理 selector.WithXxx
。
tRPC-Go 支持 Polaris Mesh 插件,你可以在这里了解更多。