一般情况下测试 gRPC 服务,都是通过客户端来直接请求服务端。如果客户端还没准备好的话,也可以使用 BloomRPC 这样的 GUI 客户端。
如果环境不支持安装这种 GUI 客户端的话,那么有没有一种工具,类似于 curl 这样的,直接通过终端,在命令行发起请求呢?
答案肯定是有的,就是本文要介绍的 gRPCurl。
gRPC Server
首先来写一个简单的 gRPC Server:
helloworld.proto:
syntax="proto3";packageproto;//Thegreetingservicedefinition.serviceGreeter{//SendsagreetingrpcSayHello(HelloRequest)returns(HelloReply){}}//Therequestmessagecontainingtheuser'sname.messageHelloRequest{stringname=1;}//TheresponsemessagecontainingthegreetingsmessageHelloReply{stringmessage=1;}main.go
packagemainimport("context""fmt""grpc-hello/proto""log""net""google.golang.org/grpc""google.golang.org/grpc/reflection")funcmain(){lis,err:=net.Listen("tcp",":50051")iferr!=nil{log.Fatalf("failedtolisten:%v",err)}server:=grpc.NewServer()//注册grpcurl所需的reflection服务reflection.Register(server)//注册业务服务proto.RegisterGreeterServer(server,&greeter{})fmt.Println("grpcserverstart...")iferr:=server.Serve(lis);err!=nil{log.Fatalf("failedtoserve:%v",err)}}typegreeterstruct{}func(*greeter)SayHello(ctxcontext.Context,req*proto.HelloRequest)(*proto.HelloReply,error){fmt.Println(req)reply:=&proto.HelloReply{Message:"hello"}returnreply,nil}运行服务:
gorunmain.goserverstart...
grpcurl 安装
这里我介绍三种方式:
Mac
brewinstallgrpcurl
Docker
#Downloadimagedockerpullfullstorydev/grpcurl:latest#Runthetooldockerrunfullstorydev/grpcurlapi.grpc.me:443list
go tool
如果有 Go 环境的话,可以通过 go tool 来安装:
goinstallgithub.com/fullstorydev/grpcurl/cmd/grpcurl@latest
grpcurl 使用
在使用 grpcurl 时,需要通过 -cert 和 -key 参数设置公钥和私钥文件,表示链接启用了 TLS 协议的服务。
对于没有启用 TLS 协议的 gRPC 服务,通过 -plaintext 参数忽略 TLS 证书的验证过程。
如果是 Unix Socket 协议,则需要指定 -unix 参数。
查看服务列表:
grpcurl-plaintext127.0.0.1:50051list
输出:
grpc.reflection.v1alpha.ServerReflectionproto.Greeter
查看某个服务的方法列表:
grpcurl-plaintext127.0.0.1:50051listproto.Greeter
输出:
proto.Greeter.SayHello
查看方法定义:
grpcurl-plaintext127.0.0.1:50051describeproto.Greeter.SayHello
输出:
proto.Greeter.SayHelloisamethod:rpcSayHello(.proto.HelloRequest)returns(.proto.HelloReply);
查看请求参数:
packagemainimport("context""fmt""grpc-hello/proto""log""net""google.golang.org/grpc""google.golang.org/grpc/reflection")funcmain(){lis,err:=net.Listen("tcp",":50051")iferr!=nil{log.Fatalf("failedtolisten:%v",err)}server:=grpc.NewServer()//注册grpcurl所需的reflection服务reflection.Register(server)//注册业务服务proto.RegisterGreeterServer(server,&greeter{})fmt.Println("grpcserverstart...")iferr:=server.Serve(lis);err!=nil{log.Fatalf("failedtoserve:%v",err)}}typegreeterstruct{}func(*greeter)SayHello(ctxcontext.Context,req*proto.HelloRequest)(*proto.HelloReply,error){fmt.Println(req)reply:=&proto.HelloReply{Message:"hello"}returnreply,nil}2输出:
packagemainimport("context""fmt""grpc-hello/proto""log""net""google.golang.org/grpc""google.golang.org/grpc/reflection")funcmain(){lis,err:=net.Listen("tcp",":50051")iferr!=nil{log.Fatalf("failedtolisten:%v",err)}server:=grpc.NewServer()//注册grpcurl所需的reflection服务reflection.Register(server)//注册业务服务proto.RegisterGreeterServer(server,&greeter{})fmt.Println("grpcserverstart...")iferr:=server.Serve(lis);err!=nil{log.Fatalf("failedtoserve:%v",err)}}typegreeterstruct{}func(*greeter)SayHello(ctxcontext.Context,req*proto.HelloRequest)(*proto.HelloReply,error){fmt.Println(req)reply:=&proto.HelloReply{Message:"hello"}returnreply,nil}3请求服务:
grpcurl-d'{"name":"zhangsan"}'-plaintext127.0.0.1:50051proto.Greeter.SayHello输出:
packagemainimport("context""fmt""grpc-hello/proto""log""net""google.golang.org/grpc""google.golang.org/grpc/reflection")funcmain(){lis,err:=net.Listen("tcp",":50051")iferr!=nil{log.Fatalf("failedtolisten:%v",err)}server:=grpc.NewServer()//注册grpcurl所需的reflection服务reflection.Register(server)//注册业务服务proto.RegisterGreeterServer(server,&greeter{})fmt.Println("grpcserverstart...")iferr:=server.Serve(lis);err!=nil{log.Fatalf("failedtoserve:%v",err)}}typegreeterstruct{}func(*greeter)SayHello(ctxcontext.Context,req*proto.HelloRequest)(*proto.HelloReply,error){fmt.Println(req)reply:=&proto.HelloReply{Message:"hello"}returnreply,nil}5-d 参数后面也可以跟 @,表示从标准输入读取 json 参数,一般用于输入比较复杂的 json 数据,也可以用于测试流方法。
grpcurl-d@-plaintext127.0.0.1:50051proto.Greeter.SayHello
可能遇到的错误
可能会遇到三个报错:
1、gRPC Server 未启用 TLS:
报错信息:
packagemainimport("context""fmt""grpc-hello/proto""log""net""google.golang.org/grpc""google.golang.org/grpc/reflection")funcmain(){lis,err:=net.Listen("tcp",":50051")iferr!=nil{log.Fatalf("failedtolisten:%v",err)}server:=grpc.NewServer()//注册grpcurl所需的reflection服务reflection.Register(server)//注册业务服务proto.RegisterGreeterServer(server,&greeter{})fmt.Println("grpcserverstart...")iferr:=server.Serve(lis);err!=nil{log.Fatalf("failedtoserve:%v",err)}}typegreeterstruct{}func(*greeter)SayHello(ctxcontext.Context,req*proto.HelloRequest)(*proto.HelloReply,error){fmt.Println(req)reply:=&proto.HelloReply{Message:"hello"}returnreply,nil}7解决:
请求时增加参数:-plaintext,参考上面的命令。
2、服务没有启动 reflection 反射服务
报错信息:
packagemainimport("context""fmt""grpc-hello/proto""log""net""google.golang.org/grpc""google.golang.org/grpc/reflection")funcmain(){lis,err:=net.Listen("tcp",":50051")iferr!=nil{log.Fatalf("failedtolisten:%v",err)}server:=grpc.NewServer()//注册grpcurl所需的reflection服务reflection.Register(server)//注册业务服务proto.RegisterGreeterServer(server,&greeter{})fmt.Println("grpcserverstart...")iferr:=server.Serve(lis);err!=nil{log.Fatalf("failedtoserve:%v",err)}}typegreeterstruct{}func(*greeter)SayHello(ctxcontext.Context,req*proto.HelloRequest)(*proto.HelloReply,error){fmt.Println(req)reply:=&proto.HelloReply{Message:"hello"}returnreply,nil}8解决:
这行代码是关键,一定要包含:
packagemainimport("context""fmt""grpc-hello/proto""log""net""google.golang.org/grpc""google.golang.org/grpc/reflection")funcmain(){lis,err:=net.Listen("tcp",":50051")iferr!=nil{log.Fatalf("failedtolisten:%v",err)}server:=grpc.NewServer()//注册grpcurl所需的reflection服务reflection.Register(server)//注册业务服务proto.RegisterGreeterServer(server,&greeter{})fmt.Println("grpcserverstart...")iferr:=server.Serve(lis);err!=nil{log.Fatalf("failedtoserve:%v",err)}}typegreeterstruct{}func(*greeter)SayHello(ctxcontext.Context,req*proto.HelloRequest)(*proto.HelloReply,error){fmt.Println(req)reply:=&proto.HelloReply{Message:"hello"}returnreply,nil}93、参数格式错误:
报错信息:
gorunmain.goserverstart...0
解决:
-d 后面参数为 json 格式,并且需要使用 '' 包裹起来。
总结:
用这个工具做一些简单的测试还是相当方便的,上手也简单。只要掌握文中提到的几条命令,基本可以涵盖大部分的测试需求了。
扩展阅读:
https://appimage.github.io/BloomRPC/
https://github.com/fullstorydev/grpcurl
文章中的脑图和源码都上传到了 GitHub,有需要的同学可自行下载。
地址: https://github.com/yongxinz/gopher/tree/main/blog