17
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

gRPC-Web Hello World Guideをやってみた

Last updated at Posted at 2019-06-30

こちらのガイド【gRPC-Web Hello World Guide】を試してみた備忘録となります。
この記事にあるコードもほぼ参考サイトを流用させていただいております。
間違いなどありましたら指摘お願いします。

1. 試した環境

  • macOS Mojave
  • Docker version 18.09.2, build 6247962
  • protoc : libprotoc 3.7.1
  • protoc-gen-grpc-web-1.0.4
  • node : v10.14.1
  • Google Chrome : 75.0.3770.100 (Official Build) (64-bit)

自分で試したコードはこちら

2. Protocol Buffersの定義ファイルを作成

gRPCのserviceを定義。
あとでこの.protoファイルから.jsファイルを生成します。

helloworld.proto
syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

3. gRPCサービスの実装

nodeで作成

server.js
const PROTO_PATH = __dirname + '/helloworld.proto';

const grpc = require('grpc');
const protoLoader = require('@grpc/proto-loader');
const packageDefinition = protoLoader.loadSync(
    PROTO_PATH,
    {keepCase: true,
     longs: String,
     enums: String,
     defaults: true,
     oneofs: true
    });
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
const helloworld = protoDescriptor.helloworld;

function doSayHello(call, callback) {
  callback(null, {
    message: 'Hello! ' + call.request.name
  });
}

function getServer() {
  const server = new grpc.Server();
  server.addService(helloworld.Greeter.service, {
    sayHello: doSayHello,
  });
  return server;
}

if (require.main === module) {
  const server = getServer();
  server.bind('0.0.0.0:9090', grpc.ServerCredentials.createInsecure());
  server.start();
}

exports.getServer = getServer;

4. プロキシの設定

Envoy Proxyを使います。

envoy.yaml
admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          codec_type: auto
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route:
                  cluster: greeter_service
                  max_grpc_timeout: 0s
              cors:
                allow_origin:
                - "*"
                allow_methods: GET, PUT, DELETE, POST, OPTIONS
                allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                max_age: "1728000"
                expose_headers: custom-header-1,grpc-status,grpc-message
                # enabled: true #201912追記 ここの設定が削除されていました
          http_filters:
          - name: envoy.grpc_web
          - name: envoy.cors
          - name: envoy.router
  clusters:
  - name: greeter_service
    connect_timeout: 0.25s
    type: logical_dns
    http2_protocol_options: {}
    lb_policy: round_robin
    # win/mac hosts: Use address: host.docker.internal instead of address: localhost in the line below
    hosts: [{ socket_address: { address: host.docker.internal, port_value: 9090 }}]
envoy.Dockerfile
FROM envoyproxy/envoy:latest
COPY ./envoy.yaml /etc/envoy/envoy.yaml
CMD /usr/local/bin/envoy -c /etc/envoy/envoy.yaml

5. クライアントのコードを作成

5-1. client.js

client.js
const {HelloRequest, HelloReply} = require('./helloworld_pb.js');
const {GreeterClient} = require('./helloworld_grpc_web_pb.js');

const client = new GreeterClient('http://localhost:8080');

const request = new HelloRequest();
request.setName('World');

client.sayHello(request, {}, (err, response) => {
  console.log(response.getMessage());
});

helloworld_pb.jshelloworld_grpc_web_pb.jshelloworld.protoから自動生成されます。

5-2. package.json

package.json
{
  "name": "grpc-web-simple-example",
  "version": "0.1.0",
  "description": "gRPC-Web simple example",
  "devDependencies": {
    "@grpc/proto-loader": "^0.3.0",
    "google-protobuf": "^3.6.1",
    "grpc": "^1.15.0",
    "grpc-web": "^1.0.0",
    "webpack": "^4.16.5",
    "webpack-cli": "^3.1.0"
  }
}

5-3. index.html

index.html
<!DOCTYPE html>
<meta charset="UTF-8">
<title>gRPC-Web Example</title>
<script src="dist/main.js"></script>

6. protocから.jsファイルを生成する

.protoファイルの定義からjsファイルを作成するためには、protocprotoc-gen-grpc-webが必要

6-1. protocをインストール

こちらを参考にインストールしました
http://google.github.io/proto-lens/installing-protoc.html

macで試したのでbrewでインストールしました。

bash
$ brew install protobuf
$ which protoc #インストールされたか確認
/usr/local/bin/protoc

6-2. protoc-gen-grpc-webをダウンロード

こちらのリンクからmac用のprotoc-gen-grpc-web-1.0.4-darwin-x86_64をダウンロードさせていただきました。

Screen Shot 2019-06-30 at 16.png

ダウンロードしたらPATHの通っているフォルダに移動させる

bash
$ sudo mv ~/Downloads/protoc-gen-grpc-web-1.0.4-darwin-x86_64 \
/usr/local/bin/protoc-gen-grpc-web
chmod +x /usr/local/bin/protoc-gen-grpc-web

これでprotocprotoc-gen-grpc-webが使える状態になったので、以下のコマンドでjsファイルを生成する

bash
$ protoc -I=. helloworld.proto \
  --js_out=import_style=commonjs:. \
  --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.

helloworld.protoからhelloworld_pb.jshelloworld_grpc_web_pb.jsが自動生成されたことを確認しました。

bash
$ ls -l helloworld*
-rw-r--r--  1 kengookumura  staff   1084 Jun 30 15:19 helloworld.proto
-rw-r--r--  1 kengookumura  staff   6645 Jun 30 16:45 helloworld_grpc_web_pb.js
-rw-r--r--  1 kengookumura  staff  14533 Jun 30 16:45 helloworld_pb.js

7. jsファイルをwebpackでバンドルする

bash
yarn
yarn webpack client.js --mode development
#または、
npm i
npx webpack client.js --mode development

dist/main.jsが作られていればOK

8. サンプルを実行

ここまでで実行の準備ができましたので、実際に動かしてみます。

server.js実行

bash
node server.js #ポート9090でリッスンします

Envoyプロキシを実行

bash
docker build -t helloworld/envoy -f ./envoy.Dockerfile .
docker run -d -p 8080:8080 helloworld/envoy

index.htmlを表示するためのwebサーバーをなんでもいいので起動する

bash
#nodeで簡易サーバ
yarn add -D node-static
yarn static -p 8081
#phpで簡易サーバ
php -S 0.0.0.0:8081
#pythonで簡易サーバ
python3 -m http.server 8081

Screen Shot 2019-06-30 at 18.12.45.png

Hello! Worldとコンソールに出ていたら成功しています。


Readmeの手順にそって行いましたが、参考にさせていただいたコードの方では、Hello! Worldの出力部分以外の処理もあり、

https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/helloworld/server.js
https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/helloworld/client.js

Screen Shot 2019-06-30 at 17.28.51.png

Hey! World0Hey! World4の部分で繰り返し処理のサンプル。
Got error, ...の部分でエラー処理のサンプルのようですので、こちも参考にすると良いと思いました。


gRPC-Webのchrome拡張も試してみた

こちらを試してみました

gRPC-Web Developer Tools - Chrome Web Store

githubはこちら

SafetyCulture/grpc-web-devtools

client.js
const {HelloRequest, HelloReply} = require('./helloworld_pb.js');
const {GreeterClient} = require('./helloworld_grpc_web_pb.js');

const enableDevTools = window.__GRPCWEB_DEVTOOLS__ || (() => {});
const client = new GreeterClient('http://localhost:8080');
enableDevTools([
    client,
]);

const request = new HelloRequest();
request.setName('World');

client.sayHello(request, {}, (err, response) => {
  console.log(response.getMessage());
});
client.js
// ここの部分を追加、更新しています
const enableDevTools = window.__GRPCWEB_DEVTOOLS__ || (() => {});
const client = new GreeterClient('http://localhost:8080');
enableDevTools([
    client,
]);

このように、helloworld.protoで定義した内容に対して、値がわかりやすいように表示してくれました。

Screen Shot 2019-06-30 at 18.41.06.png

最後まで読んでいただいてありがとうございました。

17
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?