Writer: Do Nguyen Thanh
Translator: Yosuke Okusa
Director: Yosuke Okusa
First Post: 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の公式ドキュメントを参照ください。)
この記事では、gRPCとWebAPI(RESTを使用)をUnityで動かし、HTTP/2とHTTPの通信速度の調査と測定結果を比較していきます。
gRPCが実際どんなものなのか、見ていきましょう。
サンプル作成の準備
計測には、社内トレーニングも兼ねて自前で用意したサンプルを使います。
今回は、ユーザー同士でチャットができるネイティブアプリとサーバーを作りました。
使ったライブラリ・フレームワーク:
- Protocol Buffers
- grpc_unity_package
- grpc (for C#)
- Node.js
- Express.js
参考リンク:
- grpc/grpc
- gRPCとProtocol Buffersによるアプリケーション間通信 / Unity | npaka | note
- REST vs. gRPC: Battle of the APIs
- Authentication - gRPC
- gRPC + NodeJS = Chat Example | Fexco Tech Blog
ミッション I. サンプルのチャットアプリを作る
今回は速度の計測が目的なので、gRPCの導入方法などについては割愛します。
サンプルアプリとサーバーの相関は画像のような構造です。
マッチングサーバー
サーバー画面では、サーバーのマッチングが表示されます。シンプル。
ここではgRPCによって接続が開かれます。
クライアントアプリ
クライアントアプリでは、マッチングサーバーに接続してリクエストが作成されます。
簡単に機能を紹介しておきます。
- ユーザーが参加するルームを作るとき、ホストが作成される。
- ユーザーは作成されたルームを選択してチャットに参加できる。
ミッション II. gRPCとRESTの比較
1. サーバーの通信テスト:JSON data (REST)と Binary data (gRPC)の比較
以下のように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)
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で通信するサーバーコードの作成
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. 実測
3. マルチキャストしてみる
さらに、複数ユーザーがこのアプリを使った時の通信速度も計測しました。
測定結果
CPU/GPU負荷
gRPCとRESTの実測値
gRPCでのマルチキャスト結果
実測値をみると、結構違いがあるように思えますね。
やはりgRPCは軽くて速い、ということが言えそうです。
まとめ
数値でも体感でも結果自体は良好。
序盤でも挙げましたが、良い点に関しては実際に言われている通りという印象を受けます。
- gRPCとREST(Express)の処理速度には大きな違いがあると言える。
- 他のプロトコルと比べて、3〜10分の1くらいコンパクトに開発ができる。
- 様々なプログラミング言語をサポートしていて、クラスを簡単に生成できる。
3つ目については、GOなどのライブラリもあるようですね。
また、今回はUnityで実験しましたが、ネイティブアプリだけではなく、Web用のライブラリもあるようなので、こちらでも実測してみたいです。
grpc/rpc-web