gRPCの通信速度を計ってみた

Writer: &nbsp; &nbsp; &nbsp; Do Nguyen Thanh
Translator: Yosuke Okusa
Director: &nbsp;&nbsp; Yosuke Okusa
First Post:&nbsp; 2020-09-03


## 通信の話

クライアントからサーバーへ、またはインスタンスからインスタンスへの通信には、HTTPリクエストのREST APIを使うのが一般的です。 
しかし、ストリーミングデータ伝送など、低遅延かつ高速な通信が要求されるプロジェクトでは、新しいRPCフレームワーク「**gRPC API**」が使われる傾向にあります。


### gRPCとは？
そもそも gRPC API とは、Googleのプロトコルバッファー（「Protobuf」と略されることも）と HTTP/2 を利用したRPCフレームワークです。
**簡単に言えば、RPCのアップグレード版のようなもの**です。
通常のRPCフレームワークのように、ローカルでサブルーチンを呼び出すのと似た形で、リモートのサブルーチンを呼び出すことができます。
仕組み自体は「Remote Invocation」とか「Remote Method Invocation(RMI)」などと言われることもありますね。医療機器みたいな略称です。


また、このgRPCを使うことで以下のようなメリットが考えられます。

- XMLやJSONを使用するよりも、軽量で高速なので高効率。
- データ構造をプロトコルファイルとして定義し、使用言語と通信するファイルを自動的に生成してくれる。
- 様々な言語がサポートされている。（詳細は[gRPCの公式ドキュメント](https://grpc.io/docs/)を参照ください。）

この記事では、gRPCとWebAPI（RESTを使用）をUnityで動かし、HTTP/2とHTTPの通信速度の調査と測定結果を比較していきます。
gRPCが実際どんなものなのか、見ていきましょう。


## サンプル作成の準備

計測には、社内トレーニングも兼ねて自前で用意したサンプルを使います。
今回は、ユーザー同士でチャットができるネイティブアプリとサーバーを作りました。

使ったライブラリ・フレームワーク：

- Protocol Buffers
- grpc_unity_package
- grpc (for C#)
- Node.js
- Express.js


参考リンク：

- [grpc/grpc](https://github.com/grpc/grpc)
- [gRPCとProtocol Buffersによるアプリケーション間通信 / Unity | npaka | note](https://note.com/npaka/n/n050b1aba7772)
- [REST vs. gRPC: Battle of the APIs](https://code.tutsplus.com/tutorials/rest-vs-grpc-battle-of-the-apis--cms-30711)
- [Authentication - gRPC](https://grpc.io/docs/guides/auth/)
- [gRPC + NodeJS = Chat Example | Fexco Tech Blog](https://techblog.fexcofts.com/2018/07/20/grpc-nodejs-chat-example/)
 
 

## ミッション I. サンプルのチャットアプリを作る
今回は速度の計測が目的なので、gRPCの導入方法などについては割愛します。
サンプルアプリとサーバーの相関は画像のような構造です。
![idea.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677497/77cd89ac-1a3b-aee6-4a3a-5f9bda5bc149.png)



#### マッチングサーバー
![matching_server.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677497/ae9693d7-b8d4-0304-010b-dcfdc106351b.png)
サーバー画面では、サーバーのマッチングが表示されます。シンプル。
ここでは**gRPCによって接続が開かれます。**


#### クライアントアプリ
![client.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677497/506759a3-9c5e-bff2-5e8f-8fb969b7071e.png)
クライアントアプリでは、マッチングサーバーに接続してリクエストが作成されます。
簡単に機能を紹介しておきます。

1. ユーザーが参加するルームを作るとき、ホストが作成される。
2. ユーザーは作成されたルームを選択してチャットに参加できる。

 
## ミッション II. gRPCとRESTの比較
### 1. サーバーの通信テスト：JSON data (REST)と Binary data (gRPC)の比較
以下のようにprotoファイルを作成します。

```proto:nodejs/gRPC/protos/simple.proto
syntax = "proto3";

option csharp_namespace = "Simple.Grpc";

package simple;

// Request 
message SimpleRequest{
    string username = 1;
    string message = 2;
}

// Response 
message SimpleResponse{
    string username = 1;
    string message = 2;
}

// RPC method
service SimpleService{
    rpc SimpleSend (SimpleRequest) returns (SimpleResponse) {}
}
```

#### gRPCで通信するサーバーコードの作成（NodeJS）

```javascript:nodejs/gRPC/server.js
let grpc = require("grpc");
var protoLoader = require("@grpc/proto-loader");

const server = new grpc.Server();
const SERVER_ADDRESS = "10.11.21.231:5001";

// Load protobuf
let proto = grpc.loadPackageDefinition(
    protoLoader.loadSync("protos/simple.proto", {
        keepCase: true,
        longs: String,
        enums: String,
        defaults: true,
        oneofs: true
    })
);


function SimpleSend(call, callback) {
    console.log(call.request);
    callback(null, { username: call.request.username, message: call.request.message });
}

// Define server with the methods and start it
server.addService(proto.simple.SimpleService.service, { SimpleSend: SimpleSend });

server.bind(SERVER_ADDRESS, grpc.ServerCredentials.createInsecure());

server.start();
#endFile


# install node module `npm install`
# install grpc loader `npm install --save grpc @grpc/proto-loader`
node ./nodejs/gRPC/server.js
```


#### ExpressJSで通信するサーバーコードの作成

```javascript:nodejs/Express/server.js
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json({ limit: '10mb' }));
app.use(bodyParser.urlencoded({ extended: true, limit: '10mb' }));
app.use(bodyParser.json());
app.use(function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With,Content-Type, Accept");
    next();
});
app.listen(5002, '10.11.21.231', function () {

    /***
     * Router
     ***/
    app.post('/', (req, res) => {
        console.log({ response: req.body });
        return res.send({ response: req.body })
    });

});
#endFile

# install node module `npm install`
node ./nodejs/Express/server.js
```

### 2. 実測
![measurement_scene.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677497/c360284e-3d93-7491-09c4-df316ca11b6e.png)
まずはなんの変哲もない条件での通信をテストしてみます。


### 3. マルチキャストしてみる
![multiplecast.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677497/09b602aa-afdc-0dc0-daf8-244ff0aee744.png)
さらに、複数ユーザーがこのアプリを使った時の通信速度も計測しました。
 
 
## 測定結果
##### CPU/GPU負荷
![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677497/817a9eb1-8ec3-cb33-0db7-710a67a19326.png)

##### gRPCとRESTの実測値
![image8_fix.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677497/78b0a1b7-d683-ea61-88db-bbd69ee45eb0.png)

![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677497/f7ada7a5-520c-e8d5-9853-1d5a649d9aab.png)

##### gRPCでのマルチキャスト結果
![image6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677497/04bf1430-481e-02b2-875d-a09a023cc0ad.png)


実測値をみると、結構違いがあるように思えますね。
やはりgRPCは軽くて速い、ということが言えそうです。


## まとめ

### 数値でも体感でも結果自体は良好。
序盤でも挙げましたが、良い点に関しては実際に言われている通りという印象を受けます。

- gRPCとREST（Express）の処理速度には大きな違いがあると言える。
- 他のプロトコルと比べて、３〜１０分の１くらいコンパクトに開発ができる。
- 様々なプログラミング言語をサポートしていて、クラスを簡単に生成できる。

３つ目については、GOなどのライブラリもあるようですね。
また、今回はUnityで実験しましたが、ネイティブアプリだけではなく、Web用のライブラリもあるようなので、こちらでも実測してみたいです。
[grpc/rpc-web](https://github.com/grpc/grpc-web)