rpcx 官方技术博客

查看rpcx服务器定义的所有服务以及它们的定义

2020.01.22

追溯到一二十年前,大家还在使用SOA的时候,访问一个服务后缀加上?wsdl会得到webservice的WSDL定义,用以描述webservice服务。 现在grpc也增加了Reflection机制,用以描述grpc服务的元数据。

现在 rpcx 宣布也提供了 Reflection 的机制,用以提供服务端注册的服务的元数据信息。

通过 rpcx 的 Reflection 功能,你可以:

  • 得到服务端注册的服务列表
  • 每个服务提供的方法
  • 每个方法的签名
  • 每个方法的参数的定义

先前,rpcx服务的用户(消费者)如果想访问服务,需要向服务提供者请求一份服务的方法实现和参数定义,这是一件麻烦的事情。一来需要和服务提供者进行沟通、请求、批准,而是服务提供者还需要抹去方法的具体实现,只提供方法的签名和参数的定义即可,所以服务提供者还需要维护一个提供给使用者的包。

现在,只要服务端配置了Reflection插件,服务的使用者只需调用特定的方法就可以获得所有注册的服务以及它们的方法签名,还是参数和返回值的定义。这样一来,服务器的使用者可以根据返回的服务的定义调用这些服务,不再需要向服务的提供者请求所需的包。

而且,服务器的使用者还可以随时的查看服务端的注册的服务,以及方法的定义,方便监控和调试。

首先,需要服务端配置这个插件,如果不配置这个插件,客户端是不可能访问到这些元数据的。

服务端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
	"flag"

	example "github.com/rpcx-ecosystem/rpcx-examples3"
	"github.com/smallnest/rpcx/reflection"
	"github.com/smallnest/rpcx/server"
)

var (
	addr = flag.String("addr", "localhost:8972", "server address")
)

func main() {
	flag.Parse()

	s := server.NewServer()

	p := reflection.New()
	s.Plugins.Add(p)

	s.Register(new(example.Arith), "")
	s.Register(p, "")
	s.Serve("tcp", *addr)
}

上面这个例子配置了reflection插件,配置插件很简单,就两行: 创建和加入到插件容器。

这个服务端提供了一个Arith服务。

同时,为了让客户端能通过rpcx协议查询这些注册的服务,还需要注册Reflection服务:s.Register(p, "")

通过上面的步骤,就配置好了Reflection插件。 其实就需要三步:

1
2
3
    p := reflection.New()
	s.Plugins.Add(p)
	s.Register(p, "")

主要插件需要在Register之前配置好

客户端

我们可以通过rpcx的客户端访问注册的所有服务,或者特定的某个服务。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
	"context"
	"flag"
	"fmt"

	"github.com/smallnest/rpcx/client"
)

var (
	addr = flag.String("addr", "localhost:8972", "server address")
)

func main() {
	flag.Parse()

	d := client.NewPeer2PeerDiscovery("tcp@"+*addr, "")
	xclient := client.NewXClient("Reflection", client.Failtry, client.RandomSelect, d, client.DefaultOption)
	defer xclient.Close()

	var reply string
	err := xclient.Call(context.Background(), "GetService", "Arith", &reply)
	if err != nil {
		panic(err)
	}

	fmt.Printf("all registered services: %s\n", reply)
}

上面的例子访问服务器,调用Reflection服务的GetService方法,获取Arith元数据,也就是Arith的定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
type Arith struct{}


type Args struct {
	A float64
	B float64
}

type Reply struct {
	C float64
}

type (s *Arith) Add(ctx context.Context, arg *Args, reply *Reply) error {
	return nil
}

type Args struct {
	A float64
	B float64
}

type Reply struct {
	C float64
}

type (s *Arith) Mul(ctx context.Context, arg *Args, reply *Reply) error {
	return nil
}



type (s *Arith) Say(ctx context.Context, arg *string, reply *string) error {
	return nil
}

可以看到它把Arith的三个方法AddMulSay都输出出来了,还有参数的定义。

上面的返回还有一点点问题。 因为AddMul的参数是一样的,它输出了重复的参数的定义,你需要手工删除重复的定义,这是需要加强的地方。

如果你想得到所有的服务,你可以调用GetServices方法:

1
err := xclient.Call(context.Background(), "GetServices", "", &reply)

相关的例子可以在reflection找到