実は何度目かの入門。
普段から使わないとすぐ忘れてしまうのでHello worldなサンプルを動かすまでの過程をメモで残すことにした。
なるべくコードはシンプルになるように意識している。
目次
書いていたら長くなってしまったので目次つけます
- 目次
- 自身の環境
- とりあえずgRPCを使ってHelloWorldするだけのメモ
- hello.protoを作成する
- @grpc/proto-loaderを利用すればコード生成は行わずとも実行ができる
- プロトコル定義ファイルからコードを生成して利用する
自身の環境
- M1 MacBook Air
- Node.js(v14.17.1)
- 今回は
yarn
を使って作業しています
とりあえずgRPCを使ってHelloWorldするだけのメモ
M1 macだと grpc-tools
をインストールしようとした際に node-pre-gyp
関連でエラーになる。
よって下記のように依存関係はインストールしている。
npm_config_target_arch=x64 yarn add grpc-tools @grpc/grpc-js google-protobuf
下記の記事を参照している
hello.protoを作成する
hello.proto
を作成
syntax = "proto3"; package hello; service HelloWorld { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
@grpc/proto-loaderを利用すればコード生成は行わずとも実行ができる
最初色々とインターネットでgRPCについて調べていたとき、大抵はここでprotoファイルをもとにコードの生成を行ったりしているのだが、Node.jsでは @grpc/proto-loader
というパッケージを利用することで、上で定義した hello.proto
ファイルをもとに動的に生成できる仕組みが用意されているようだ。
(つまり @grpc/proto-loader
を利用する場合、コード生成は行わなくともコードを実行することができる)
というわけで@grpc/proto-loader
を利用することで、下記のコードサンプルはそのまま動く
server側のNode.jsのサンプル (@grpc/proto-loader利用)
const PROTO_PATH = './hello.proto'; const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, }); const hello_proto = grpc.loadPackageDefinition(packageDefinition).hello; const sayHello = (call, callback) => { console.log('Server: Call sayHello RPC method'); callback(null, { message: 'gRPC Sample: ' + call.request.name }); }; const main = () => { const server = new grpc.Server(); server.addService(hello_proto.HelloWorld.service, { sayHello: sayHello }); server.bindAsync( '0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => { server.start(); console.log('Server: Start'); } ); }; main();
下記の protoLoader.loadSync
というところで hello.proto
を読んでいるのがわかる。
const PROTO_PATH = './hello.proto'; const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, }); const hello_proto = grpc.loadPackageDefinition(packageDefinition).hello;
client側のNode.jsのサンプル(@grpc/proto-loader利用)
そして、client側のコード。このコードを実行することで上に書いたサーバプログラムにリクエストが送られ、レスポンスが返される。
const PROTO_PATH = './hello.proto'; const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, }); const hello_proto = grpc.loadPackageDefinition(packageDefinition).hello; const main = () => { const client = new hello_proto.HelloWorld( '0.0.0.0:50051', grpc.credentials.createInsecure() ); client.sayHello({ name: 'HELLO WORLD!' }, function (err, response) { console.log(response.message); }); }; main();
これで下記のようにプログラムをそれぞれプログラムを実行すると、 gRPC Sample: HELLO WORLD!
という文字を始め、いくつかのログが出力されるのが確認できる。
node server.js
node client.js
# gRPC Sample: HELLO WORLD! はこちら側で出力される
プロトコル定義ファイルからコードを生成して利用する
次はhello.proto
をもとにコードを生成して利用する方法についてメモしていく
の前に、grpcを使う上で利用することになるライブラリについて書いておく。
(ザックリ調べた内容をメモしているだけの素人メモなのでご了承ください)
grpcと@grpc/grpc-js
Node.jsからgrpcを利用する場合、grpc
と @grpc/grpc-js
というのがある。
最初は grpc
が利用されていたようで、後発が @grpc/grpc-js
らしいのだが、下記のnpmのページに行ってみるとわかるように、現在 grpc
は非推奨となっており、 @grpc/grpc-js
への以降が推奨されている。
このサンプルでも @grpc/grpc-js
を利用する。
protoファイルから@grpc/grpc-js向けのコードを生成する方法
コードを生成する際に --grpc_out=grpc_js:./gen/proto
というオプションを付けているのだが、これをつけることで@grpc/grpc-js
用のコードを生成される。
(最初このオプションを付け忘れていて、ライブラリが見つからない的なエラーになって10分ほど迷っていた)
# 生成したコードを格納するためのディレクトリ mkdir -p gen/proto # コード生成 yarn run grpc_tools_node_protoc -I. --js_out=import_style=commonjs,binary:./gen/proto --grpc_out=grpc_js:./gen/proto ./hello.proto
これで gen/proto
配下にコードが生成される。
次にこの生成されたコードを利用するコードをserver・clientそれぞれで書いていく。
server側のNode.jsのサンプル
これはclient側のコードにも言えることだが、先ほどhello.proto
で定義した name
や message
が getName()
や getMessage()
という形で利用している点が先ほどのサンプルとは異なる。
なおコード生成の時点で hello_grpc_pb.js
と hello_pb.js
という2種類のファイルが生成されている。
これらのコードをそれぞれ読み込んでコードを実行していくようになる。
hello_grpc_pb.js
は gRPCに対応するserverとclientを提供するサービスらしい。- ( 生成されたコードを見ると
service HelloWorld
に関する記述あり)
- ( 生成されたコードを見ると
hello_pb.js
はHelloRequest
とHelloReply
というそれぞれ定義されたmessage
に対応したコードのようだ。- (注意:なんとなく生成されたコードを読んだ雰囲気で書いています。)
const grpc = require('@grpc/grpc-js'); const { HelloWorldService } = require('./gen/proto/hello_grpc_pb'); const { HelloReply } = require('./gen/proto/hello_pb'); const sayHello = (call, callback) => { const reply = new HelloReply(); console.log('Server: Call sayHello RPC method'); const message = 'gRPC Sample: ' + call.request.getName(); reply.setMessage(message); callback(null, reply); }; const main = () => { const server = new grpc.Server(); server.addService(HelloWorldService, { sayHello: sayHello }); server.bindAsync( '0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => { server.start(); console.log('Server: Start'); } ); }; main();
client側のNode.jsのサンプル
client側も同じく hello_grpc_pb.js
と hello_pb.js
をそれぞれ読み込んでいる。
const grpc = require('@grpc/grpc-js'); const { HelloWorldClient } = require('./gen/proto/hello_grpc_pb'); const { HelloRequest } = require('./gen/proto/hello_pb'); const main = () => { const request = new HelloRequest(); const client = new HelloWorldClient( '0.0.0.0:50051', grpc.credentials.createInsecure() ); request.setName('HELLO WORLD!'); client.sayHello(request, function (err, response) { console.log(response.getMessage()); }); }; main();
以上、Hello Worldなサンプルでした。