如何调试 gRPC

像 curl 一样调用 grpc

grpcurl

https://github.com/fullstorydev/grpcurl 可以像 curl 一样在命令行调用 gRPC 请求。官方用 C++ 实现的 grpc_cli 也提供了类似的功能。更多相似工具见 Awesome gRPC#tools-cli

例如对于 grpc-go 官方 helloworld 例子,执行可以得到响应:

$ grpcurl -proto ./helloworld.proto -plaintext -d '{"name":"hahaha"}' 127.0.0.1:50051 helloworld.Greeter/SayHello
{
  "message": "Hello hahaha"
}

server-reflection

如果没有 proto 文件,会报错:Failed to compute set of methods to expose: server does not support the reflection API。

这时需要使用 server-reflection,这样即使客户端在没有打桩代码(stub),依然可以调用服务。
修改 server 代码:

--- a/examples/helloworld/greeter_server/main.go
+++ b/examples/helloworld/greeter_server/main.go
@@ -40,6 +40,7 @@ import (
        "google.golang.org/grpc"
        pb "google.golang.org/grpc/examples/helloworld/helloworld"
+       "google.golang.org/grpc/reflection"
 )

 const (
@@ -61,6 +62,8 @@ func main() {
        }
        s := grpc.NewServer()
        pb.RegisterGreeterService(s, &pb.GreeterService{SayHello: sayHello})
+       // Register reflection service on gRPC server.
+       reflection.Register(s)
        if err := s.Serve(lis); err != nil {
                log.Fatalf("failed to serve: %v", err)
        }

再执行就可以成功调用:

$ grpcurl -plaintext -d '{"name":"hahaha"}' 127.0.0.1:50051 helloworld.Greeter/SayHello
{
    "message": "Hello hahaha"
}

像 Postman 一样调用 grpc

grpcui

https://github.com/fullstorydev/grpcui 是在 gRPCurl 基础上封的一层 UI。更多相似工具见 Awesome gRPC#tools-gui

对于 helloworld 服务,执行:

$ grpcui -proto ./helloworld.proto -plaintext localhost:50051

可打开 web 界面如下图:

grpcui.jpg

如何观察 grpc 流量

grpcdump

https://github.com/rmedvedev/grpcdump 可以嗅探网卡流量,将 grpc 消息打印出来。

例如对于 route_guide,执行:

$ sudo grpcdump -i lo -p 10000 -proto-files ./route_guide.proto -json

可以获得输出:

INFO[0000] Starting sniff ethernet packets at interface lo on port 10000
{"src":"127.0.0.1:59740","dst":"127.0.0.1:10000","path":"/routeguide.RouteGuide/GetFeature","body":"latitude:409146138 longitude:-746188906","headers":{":authority":"localhost:10000",":method":"POST",":path":"/routeguide.RouteGuide/GetFeature",":scheme":"http","content-type":"application/grpc","grpc-timeout":"9999967u","te":"trailers","user-agent":"grpc-go/1.35.0"}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"Berkshire Valley Management Area Trail, Jefferson, NJ, USA\" location:\u003clatitude:409146138 longitude:-746188906\u003e","headers":{}}{"src":"127.0.0.1:59740","dst":"127.0.0.1:10000","path":"/routeguide.RouteGuide/GetFeature","body":"","headers":{":authority":"localhost:10000",":method":"POST",":path":"/routeguide.RouteGuide/GetFeature",":scheme":"http","content-type":"application/grpc","grpc-timeout":"9999990u","te":"trailers","user-agent":"grpc-go/1.35.0"}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"location:\u003c\u003e","headers":{}}
{"src":"127.0.0.1:59740","dst":"127.0.0.1:10000","path":"/routeguide.RouteGuide/ListFeatures","body":"lo:\u003clatitude:400000000 longitude:-750000000\u003e hi:\u003clatitude:420000000 longitude:-730000000\u003e","headers":{":authority":"localhost:10000",":method":"POST",":path":"/routeguide.RouteGuide/ListFeatures",":scheme":"http","content-type":"application/grpc","grpc-timeout":"9999989u","te":"trailers","user-agent":"grpc-go/1.35.0"}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"Patriots Path, Mendham, NJ 07945, USA\" location:\u003clatitude:407838351 longitude:-746143763\u003e","headers":{}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"101 New Jersey 10, Whippany, NJ 07981, USA\" location:\u003clatitude:408122808 longitude:-743999179\u003e","headers":{}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"U.S. 6, Shohola, PA 18458, USA\" location:\u003clatitude:413628156 longitude:-749015468\u003e","headers":{}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"5 Conners Road, Kingston, NY 12401, USA\" location:\u003clatitude:419999544 longitude:-740371136\u003e","headers":{}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA\" location:\u003clatitude:414008389 longitude:-743951297\u003e","headers":{}} {"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"287 Flugertown Road, Livingston Manor, NY 12758, USA\" location:\u003clatitude:419611318 longitude:-746524769\u003e","headers":{}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"4001 Tremley Point Road, Linden, NJ 07036, USA\" location:\u003clatitude:406109563 longitude:-742186778\u003e","headers":{}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"352 South Mountain Road, Wallkill, NY 12589, USA\" location:\u003clatitude:416802456 longitude:-742370183\u003e","headers":{}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"Bailey Turn Road, Harriman, NY 10926, USA\" location:\u003clatitude:412950425 longitude:-741077389\u003e","headers":{}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"193-199 Wawayanda Road, Hewitt, NJ 07421, USA\" location:\u003clatitude:412144655 longitude:-743949739\u003e","headers":{}}
{"src":"127.0.0.1:10000","dst":"127.0.0.1:59740","path":"","body":"name:\"406-496 Ward Avenue, Pine Bush, NY 12566, USA\" location:\u003clatitude:415736605 longitude:-742847522\u003e","headers":{}}
{"src":"127.0.0.1:59740","dst":"127.0.0.1:10000","path":"/routeguide.RouteGuide/RecordRoute","body":"latitude:-600000000 longitude:-1660000000","headers":{":authority":"localhost:10000",":method":"POST",":path":"/routeguide.RouteGuide/RecordRoute",":scheme":"http","content-type":"application/grpc","grpc-timeout":"9999985u","te":"trailers","user-agent":"grpc-go/1.35.0"}}
...

tcpdump + Wireshark

使用 tcpdump 抓包,将抓包的数据导入 Wireshark 查看。注意 Wireshark 最好使用 v3 版本及以上,确保支持 protobuf 格式。

例如对于 route_guide

首先使用 tcpdump 抓包 data.cap。

$ sudo tcpdump -vv -i lo port 10000 -w data.cap

其次使用 Wireshark 将 data.cap 打开。

打开 Preferences(首选项) > Protocols > Protobuf 下的 Edit 菜单,设置 Protobuf Search Paths 告诉 Wireshark 从哪里搜索 proto 文件。

wireshark_protobuf.jpg

package-list(分组列表) 窗口中右键点击 Decode as… 打开窗口,在 Current(当前) 列下选择 HTTP2,点击 OK

最终效果如下图:

wireshark_protobuf2.jpg

gRPC-Go

开启 log

根据 https://pkg.go.dev/google.golang.org/grpc#readme-how-to-turn-on-logging

export GRPC_GO_LOG_VERBOSITY_LEVEL=99
export GRPC_GO_LOG_SEVERITY_LEVEL=info

可以开启 gRPC-Go log。

注意即使这样设置了也可能不生效,因为通过 grpclog.SetLoggerV2 方法可以替换默认 log 实现,新的实现不一定遵守这套约定。比如这个 disable grpc client logging

开启 HTTP/2 log

根据 net/http,Go 1.6 以后开启 http2 debug。

GODEBUG=http2client=0  # disable HTTP/2 client support
GODEBUG=http2server=0  # disable HTTP/2 server support
GODEBUG=http2debug=1   # enable verbose HTTP/2 debug logs
GODEBUG=http2debug=2   # ... even more verbose, with frame dumps

如何调试 gRPC
https://0x11.fun/post/grpc-debug/
作者
0x11
发布于
2022年1月24日
许可协议