node で grpc を使ってみる + TypeScript でブラウザ実行しようとした
grpcを手元で動かすのにシンプルな例を探していたが、全然見つからなかったのでメモがてら書いておく。
(Googleの公式サンプル、非常にわかりづらすぎる… https://grpc.io/docs/tutorials/basic/node.html)
まず通信定義を書く。 service の rpc を定義する。
syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
そのサーバーを実装する。事前に npm install -S grpc
などしておく。
const PROTO_PATH = __dirname + '/helloworld.proto'
const grpc = require('grpc')
const { helloworld } = grpc.load(PROTO_PATH)
function sayHello(call, callback) {
callback(null, { message: 'Hello ' + call.request.name })
}
const server = new grpc.Server()
server.addService(helloworld.Greeter.service, { sayHello })
server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure())
server.start()
node server.js
で localhost:50051 にサーバーが立つ。
これを呼び出すクライアントを書く。今回は grpc-caller
を使う。(npm install しておく)
const path = require('path')
const caller = require('grpc-caller')
const PROTO_PATH = __dirname + '/helloworld.proto'
const client = caller('0.0.0.0:50051', PROTO_PATH, 'Greeter')
client.sayHello({ name: 'Bob' }, (err, res) => {
console.log(res)
})
grpcサーバーを立てた状態で node client.js
で実行すると { message: 'Hello Bob' }
と出るはず。
ここまでは簡単
ブラウザで実行する
ここから先、調べたが typescript の例しかなかったので、 typescript を使う。バニラJS のみでシンプルに確認作業を終えたかったが、確かにgrpcの目的としては型がない環境を想定しないので、ここは諦める。
grpc
は native module なので、ブラウザで呼び出すために grpc-web-client
を使う。
ついでに、helloworld.proto からクライアントコードも生成する必要がある。(この辺 github のリポジトリ名が grpc-web だったりして別パッケージ化と思ったが同一のようだった)
ってことで、 protoc コマンドをインストールして、ついでにその typescript plugin をいれる。
brew install protobuf # Mac
npm install -S ts-protoc-gen
こんなシェルスクリプト書く
# Path to this plugin
PROTOC_GEN_TS_PATH="./node_modules/.bin/protoc-gen-ts"
# Directory to write generated code to (.js and .d.ts files)
OUT_DIR="./out"
protoc \
--plugin="protoc-gen-ts=${PROTOC_GEN_TS_PATH}" \
--js_out="import_style=commonjs,binary:${OUT_DIR}" \
--ts_out="service=true:${OUT_DIR}" \
helloworld.proto
sh gen.sh
で実行すると out にこんなコードが出力される
⋊> ~/s/protobuf-playground on master tree out 23:44:20
out
├── helloworld_pb.d.ts
├── helloworld_pb.js
├── helloworld_pb_service.d.ts
└── helloworld_pb_service.js
これを Typescript から使うコードはこうなる。
import { grpc } from 'grpc-web-client'
import { HelloRequest } from './out/helloworld_pb'
import { Greeter, GreeterClient } from './out/helloworld_pb_service'
const HOST = 'http://localhost:50051'
const req = new HelloRequest()
req.setName('johndoe')
const client = new GreeterClient('http://localhost:50051')
client.sayHello(req, (err, ret) => {
if (err) {
throw err
}
console.log(ret)
})
yarn tsc --init
でtsconfig.jsonを生成
webpack と ts-loader いれて雑にビルド設定を書く(フロントエンドのいつものアレ)
module.exports = {
mode: 'development',
entry: './web-client.ts',
output: {
path: __dirname + '/web',
filename: 'bundle.js'
},
resolve: {
extensions: ['.ts', '.tsx', '.js']
},
module: {
rules: [{ test: /\.tsx?$/, loader: 'ts-loader' }]
}
}
で、ここで自分はこういうエラーがでたが問題はなかった(tsconfigの設定が悪そう。moduleResolution とかあのへん。今回は本題ではないので追跡しない)
ERROR in /Users/mz/sandbox/protobuf-playground/web-client.ts
./web-client.ts
[tsl] ERROR in /Users/mz/sandbox/protobuf-playground/web-client.ts(1,22)
TS2307: Cannot find module 'grpc-web-client'.
で, 適当に index.html 置いてこの js を読み込むと動くはず…
ここで謝らないといけないことがある。これでおそらく実行できているのだが、localhostでhttpのみの環境でやっていたので、実際に grpc の疎通を確認できなかった。(gprcはhttp/2必須)
こんなエラーが出て頓挫した。
fetch.js:42 OPTIONS http://localhost:50051/helloworld.Greeter/SayHello 0 ()
Fetch.send @ fetch.js:42
Fetch.sendMessage @ fetch.js:70
GrpcClient.send @ client.js:230
unary @ unary.js:35
sayHello @ helloworld_pb_service.js:33
(anonymous) @ web-client.ts:12
./web-client.ts @ bundle.js:811
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:69
(anonymous) @ bundle.js:72
detach.js:22 Uncaught Error: Response closed without headers
at Object.onEnd (helloworld_pb_service.js:41)
at eval (unary.js:26)
at Array.eval (client.js:182)
at runCallbacks (detach.js:10)
at eval (detach.js:34)
たぶん http/2 だったら動いてる感じのエラーなので、あとで確認して追記する。h2o とオレオレ証明書とかそういう作業になると思う
感想
動作確認だけだったらとりあえず最初の grpc-caller の例でよくて、 gprc-server の開発の初手としてはややこしいブラウザは後回しで良さそう。結局やるのはそうなんだけど…
あと、どこ探しても Golang + TypeScript の例しか見つからなかった。いろんな例を眺めてコードちょっとずつ書きながら、いろんな情報の継ぎ接ぎで書いたので参照元とか思い出せない…
ここまでのコードはこれ https://github.com/mizchi-sandbox/protobuf-playground
Author And Source
この問題について(node で grpc を使ってみる + TypeScript でブラウザ実行しようとした), 我々は、より多くの情報をここで見つけました https://qiita.com/mizchi/items/2ce545c85894bcae1380著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .