最近在折腾Tensorflow Serving,这货有REST和gRPC两种API,之前压测了一波REST,又翻了翻源码,发现他REST的实现方式是,接收JSON进来反序列化,再拼一个protobuf出来喂给底层的Session进行计算,最后再把结果序列化为JSON返回,这一来一回中间折腾JSON就有不必要的性能消耗了,最好还是用gRPC接口,于是就有了压测gRPC接口的需求。

因为gRPC是走HTTP2的嘛,天真的我曾试图把之前gRPC客户端Marshal出来的二进制直接用nghttp2的h2load工具喂给tf serving服务端,这种投机取巧的做法果然翻了车,由于我太菜,不管怎么折腾,server端一直返回错误,wireshark抓包对比了半天发现真的gRPC请求和用h2load伪造出来的请求帧有很多不同的地方,不是那么容易伪造,于是只能找找有没有讲究一点的工具能直接做gRPC的压测了。

然而HTTP服务的压测工具很多,但能搞定gRPC压测的却不多,就找到了本次的主角 https://github.com/bojand/ghz

起初发现它需要指定个proto传进去,我内心是拒绝的,因为之前为了生成tf serving的gRPC客户端,我可是把那它近百个互相import来import去的proto文件折腾好几天才成功生成,全是血泪史(哪天闲着没事可能会总结一下)。走投无路之下,只能硬着头皮用了,然后,真香。

故事水完了,下面就是喜闻乐见的操作环节了。

1. 工具准备

总共分两步,
第一,从https://github.com/bojand/ghz/releases下载工具本体,解压出来,是个二进制可执行文件。

第二,把protoc装好,下一步可能要用。macOS就用brew装吧。(都用上gRPC了应该都装过了)

2. 生成protoset文件

如果你用的proto文件里import了其他proto,可以考虑生成protoset文件,并且在用ghz时用它来代替原始的proto,这样才能正常解析。
我的生成命令如下(后面传入的proto文件路径有化简),供参考

protoc --include_imports -I ./tensorflow -I ./serving \
 --descriptor_set_out=bundle.protoset apis/*.proto

这个命令关键要加--include_imports这个参数,也是ghz的文档中没有提到的,加了这个参数之后,生成的protoset文件是自包含的,不会出其他幺蛾子。

如果你生成出的文件在接下来用ghz命令时中有类似no descriptor found for "google/protobuf/wrappers.proto"这样的报错,就要检查下protoc生成时是不是有与报错有关的proto文件目录没用-I包含进来,或者是没加--include_imports

如果就是要出狂战斧不想执行这步,想直接用proto文件,那么需要在用ghz压测时尝试使用--import-paths参数,把依赖的proto目录传进去,理论上能行,但我没试过,你得自己踩踩坑。

3. 使用ghz进行压测

我这里用的命令如下,供参考,你得根据自己的需要调整

./ghz --skipTLS --insecure --protoset ./bundle.protoset \
-B ./grpc_payload --call tensorflow.serving.PredictionService/Predict \ 
127.0.0.1:8500

参数的含义

  • --skipTLS --insecure 我的服务端不支持tls验证,所以需要跳过tls验证,如果你需要可以开
  • --protoset ./bundle.protoset 指定刚才用protoc生成出来的protoset文件
  • -B ./grpc_payload 这个grpc_payload是我在自己grpc客户端里Marshal出来的祖传二进制,就是前面说用h2load失败的那个,废物利用了一下,如果你参数比较简单,可以用-D传个json文件进去让它自己转成pb
  • --call tensorflow.serving.PredictionService/Predict调用的方法名,改成你自己的方法名
  • 127.0.0.1:8500目标gRPC服务的ip和端口

更多用法具体可以参考ghz提供的示例 https://ghz.sh/docs/examples

另外这个工具似乎还有web界面,你可以自己试试看。

就酱。