Node-REDでgRPC:1000x10回の処理時間をhttpと比較


gRPC

gRPCはHTTP/2通信でRPC(remote procedure callができるフレームワークです。.protoファイルというインタフェースを定義をサーバとクライアントで共有して、フレームワークがパラメータのチェックなどしてくれます。
他の言語だとOpenAPI/Swaggerのようにコード生成が必要ですが、node.jsではコード生成が不要で、さらにNode-REDを使えばほとんど設定だけで簡単に実装できます。開発中のソフトのエミュレータやテストスタブ/ドライバとして便利に使うことができます。
gRPCはバイナリを送ることやストリーム通信も可能であるなどのメリットがあるのですが、普通の通信でもHTTP/2によって早くなっているはずです。使い慣れたNode-REDがどのくらい早くなるか気になったので、速度を測定してみました。

評価方法

今回はhttpのPOSTとの比較を1000回呼び出しを10セット行って処理時間を比較しました。
なお、今回はWSL(V1)のNode-RED内のローカル通信で行いました。「こんなものかな」という雰囲気をお楽しみください。環境だけでなく、パラメータのデータ形式や通信方式など、評価としては不十分な点も多々ありますので、気になる方はフローを修正してご自身で試していただけたらと思います。

結果:

3倍弱の速度が出ているようです。

HTTP gRPC
1回目 6523 2640
2回目 5987 2133
3回目 5716 2168
4回目 5870 1852
5回目 5987 1784
6回目 5614 1763
7回目 5396 1745
8回目 5441 1889
9回目 5500 1775
10回目 5858 1827
平均(秒) 5.7892 1.9576

フロー

コード

読み込んで使ってください


[{"id":"f6d68618250ca67f","type":"tab","label":"HTTPとgRPC","disabled":false,"info":"","env":[]},{"id":"e95f41214660dee0","type":"inject","z":"f6d68618250ca67f","name":"","props":[{"p":"payload"},{"p":"topic","v":"","vt":"date"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"id\":123456,\"name\":\"test\"}","payloadType":"json","x":130,"y":80,"wires":[["0d5edbe239b1896a"]]},{"id":"f81476510ec5f8e4","type":"debug","z":"f6d68618250ca67f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":660,"y":220,"wires":[]},{"id":"5fb1c2df8eae1e12","type":"http in","z":"f6d68618250ca67f","name":"","url":"/http","method":"post","upload":false,"swaggerDoc":"","x":170,"y":300,"wires":[["37be881fb460ae59"]]},{"id":"0e9cc56c988dc971","type":"http response","z":"f6d68618250ca67f","name":"","statusCode":"","headers":{"Content-Type":"application/json"},"x":620,"y":300,"wires":[]},{"id":"b1f8b6a64988d025","type":"http request","z":"f6d68618250ca67f","name":"","method":"POST","ret":"obj","paytoqs":"ignore","url":"http://localhost:1880/http","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":450,"y":140,"wires":[["4e87001263af2e43"]]},{"id":"a71e4f38fcaca3d3","type":"change","z":"f6d68618250ca67f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"id\":123456,\"name\":\"test\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":250,"y":140,"wires":[["b1f8b6a64988d025"]]},{"id":"4e87001263af2e43","type":"function","z":"f6d68618250ca67f","name":"","func":"msg.counter = msg.counter+1\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":640,"y":140,"wires":[["6c52fd1ba44a5aef"]]},{"id":"6c52fd1ba44a5aef","type":"switch","z":"f6d68618250ca67f","name":"","property":"counter","propertyType":"msg","rules":[{"t":"lt","v":"1000","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":310,"y":220,"wires":[["a71e4f38fcaca3d3"],["c4a1a4474f7478bf"]]},{"id":"1f023ef9fb240d3f","type":"debug","z":"f6d68618250ca67f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":660,"y":540,"wires":[]},{"id":"88a6762b8302f60f","type":"change","z":"f6d68618250ca67f","name":"","rules":[{"t":"set","p":"counter","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":400,"wires":[["d3739dc3caf9689f"]]},{"id":"0b16db712fc56de0","type":"function","z":"f6d68618250ca67f","name":"","func":"msg.counter = msg.counter+1\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":460,"wires":[["90cd9e63946ba227"]]},{"id":"90cd9e63946ba227","type":"switch","z":"f6d68618250ca67f","name":"","property":"counter","propertyType":"msg","rules":[{"t":"lt","v":"1000","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":290,"y":540,"wires":[["d3739dc3caf9689f"],["0709b9bb353b5616"]]},{"id":"0699e370b014662e","type":"grpc-response","z":"f6d68618250ca67f","name":"","x":610,"y":620,"wires":[]},{"id":"f130b9ebdb4402b9","type":"grpc-register-function","z":"f6d68618250ca67f","name":"","server":"8cb71b4d968ba4f2","service":"Greeter","method":"SayHello","x":180,"y":620,"wires":[["a57a172ef664a814"]]},{"id":"d3739dc3caf9689f","type":"grpc-call","z":"f6d68618250ca67f","name":"","server":"8cb71b4d968ba4f2","service":"Greeter","method":"SayHello","chain":"","key":"","x":230,"y":460,"wires":[["0b16db712fc56de0"]]},{"id":"2e42051c498d3367","type":"inject","z":"f6d68618250ca67f","name":"","props":[{"p":"payload"},{"p":"topic","v":"","vt":"date"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"id\":123456,\"name\":\"test\"}","payloadType":"json","x":140,"y":400,"wires":[["88a6762b8302f60f"]]},{"id":"37be881fb460ae59","type":"change","z":"f6d68618250ca67f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"message\":\"OK!\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":300,"wires":[["0e9cc56c988dc971"]]},{"id":"a57a172ef664a814","type":"change","z":"f6d68618250ca67f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"message\":\"OK!\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":620,"wires":[["0699e370b014662e"]]},{"id":"c4a1a4474f7478bf","type":"function","z":"f6d68618250ca67f","name":"","func":"msg.payload = Date.now() -msg.topic;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":220,"wires":[["f81476510ec5f8e4"]]},{"id":"0709b9bb353b5616","type":"function","z":"f6d68618250ca67f","name":"","func":"msg.payload = Date.now() -msg.topic;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":540,"wires":[["1f023ef9fb240d3f"]]},{"id":"0d5edbe239b1896a","type":"change","z":"f6d68618250ca67f","name":"","rules":[{"t":"set","p":"counter","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":80,"wires":[["a71e4f38fcaca3d3"]]},{"id":"cb132ac35c2e449c","type":"comment","z":"f6d68618250ca67f","name":"HTTP呼出し1000回","info":"","x":140,"y":40,"wires":[]},{"id":"bdc6dfb867aa2128","type":"comment","z":"f6d68618250ca67f","name":"HTTPサーバ","info":"","x":110,"y":260,"wires":[]},{"id":"58d487f0614a7632","type":"comment","z":"f6d68618250ca67f","name":"GRPC呼出し1000回","info":"","x":140,"y":360,"wires":[]},{"id":"a7711b6a35746825","type":"comment","z":"f6d68618250ca67f","name":"GRPCサーバ","info":"","x":120,"y":580,"wires":[]},{"id":"8cb71b4d968ba4f2","type":"grpc-server","port":"5001","name":"","server":"localhost","protoFile":"syntax = \"proto3\";\npackage hello;\n \nservice Greeter {\n  rpc SayHello(HelloRequest) returns (HelloReply) {}\n}\n \nmessage HelloRequest {\n  int32 id = 1;\n  string name = 2;\n}\n \nmessage HelloReply {\n  string message = 1;\n}","ca":"","chain":"","key":"","mutualTls":false,"localServer":true}]

参考文献

参考にさせていただきました。ありがとうございます。
* node-red-contrib-grpc
* Node.jsでgRPCを動かそう
* Node.jsではじめるgRPC [@grpc/proto-loader]

おまけ

設定の方法がわからない方は以下をご覧ください。
Node-REDでgRPC:はじめ方