众所周知,在编写GRPC服务应用时,需要获取相应的proto文件,生成server、client的打桩代码,借此即可访问服务,同时也能启动服务,但这里会有个问题,就是需要获取到对应的proto文件。我想直接知道对方的请求参数和返回参数,并由此来设定mock值,设定完成后,即可直接请求。

基于以上的需要,进行相应的实现,即可得到所需的效果。

动态获取proto

通过接口获取对应服务proto定义的能力,需要对方服务打开proto 反射。

1
2
3
4
5
6
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())

// Register reflection service on gRPC server. "google.golang.org/grpc/reflection"
reflection.Register(s)

Register其实是注册一个服务,里面就一个函数,可以拿到该proto的所有数据,如同在自己的项目中调用reflection库一般。

1
2
3
4
5
// Register registers the server reflection service on the given gRPC server.
func Register(s GRPCServer) {
svr := NewServer(ServerOptions{Services: s})
v1alphagrpc.RegisterServerReflectionServer(s, svr)
}

启动server

启动一个可以接受任何请求的GRPC服务器,这里做一步如同grpc proxy的操作,不去注册固定的服务处理函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
s := grpc.NewServer(grpc.UnknownServiceHandler(func(srv interface{}, stream grpc.ServerStream) error {
// 获取请求流的目的 Method 名称
fullMethodName, ok := grpc.MethodFromServerStream(stream)
if !ok {
return status.Errorf(codes.Internal, "failed to get method from server stream")
}
fmt.Println(fullMethodName)

// 确定该服务是否已被注册,并找到准备好的返回值
resp, err := director(fullMethodName)
if err != nil {
return status.Errorf(codes.InvalidArgument, "failed find string")
}

// 然后将其发回去
err = stream.SendMsg(resp)
if err != nil {
return status.Errorf(codes.Internal, "send msg error")
}

return nil
}))
err = s.Serve(listener)

使用

在前端界面上输入目标服务器的地址,获取到其接口和参数,然后填入mock值。

新增的功能:

  • 自动生成参数,根据值类型;(像例如yapi
  • 引入表达式,可以根据表达式对请求参数和返回值做一些灵活操作
  • 加入到注册中心,自动mock所有可被mock的服务

参考

grpcurl

protoreflect

grpcurl-工具