gRPCとは
🚀 gRPCとは
Section titled “🚀 gRPCとは”gRPCは、Googleが開発した高性能なRPC(Remote Procedure Call)フレームワークです。Protocol Buffersを使用して、効率的な通信を実現します。
🎯 なぜgRPCが必要なのか
Section titled “🎯 なぜgRPCが必要なのか”❌ RESTful APIの課題
Section titled “❌ RESTful APIの課題”❌ 問題のある実装:
// RESTful API: JSON形式で通信// リクエストPOST /api/usersContent-Type: application/json
{ "name": "Alice", "email": "alice@example.com", "age": 25}
// レスポンス{ "id": 1, "name": "Alice", "email": "alice@example.com", "age": 25}
// 問題点:// - テキスト形式のため、サイズが大きい// - 型安全性がない// - パフォーマンスが限定的⚠️ 影響:
- 📈 通信量の増加
- ⚠️
パフォーマンスの低下 - ❌
型安全性の欠如
✅ gRPCによる解決
Section titled “✅ gRPCによる解決”✅ 改善された実装:
// Protocol Buffers: バイナリ形式で通信syntax = "proto3";
service UserService { rpc GetUser(GetUserRequest) returns (User); rpc CreateUser(CreateUserRequest) returns (User);}
message GetUserRequest { int32 id = 1;}
message CreateUserRequest { string name = 1; string email = 2; int32 age = 3;}
message User { int32 id = 1; string name = 2; string email = 3; int32 age = 4;}✅ メリット:
- 📦 バイナリ形式のため、サイズが小さい
- ✅
型安全性が保証される - ⚡ 高い
パフォーマンス - 🔄
ストリーミング対応
🎯 gRPCの特徴
Section titled “🎯 gRPCの特徴”📦 1. Protocol Buffers
Section titled “📦 1. Protocol Buffers”📋 定義: 構造化データをシリアライズするための言語非依存のフォーマットです。
✅ メリット:
- 📦 サイズ: JSONより約3-10倍小さい
- ⚡ 速度: シリアライズ/デシリアライズが高速
- ✅ 型安全性: スキーマにより型が保証される
- 🔄 後方互換性: スキーマの進化に対応
🌐 2. HTTP/2ベース
Section titled “🌐 2. HTTP/2ベース”📋 特徴:
- 🔄 マルチプレクシング(複数のリクエストを並列処理)
- 📤 サーバープッシュ
- 📦 ヘッダー圧縮
- 🔄
ストリーミング
⚡ なぜgRPCは速いのか(仕組みまで)
Section titled “⚡ なぜgRPCは速いのか(仕組みまで)”gRPCの速さは「通信プロトコル」と「データ表現」の両方で発生します。
1. バイナリシリアライズ(Protocol Buffers)
- JSONは「文字列」であり、サイズもパースコストも大きい
- Protocol Buffersは「数値タグ+バイナリ」で構造を表現する
- 送受信サイズが小さく、シリアライズ/デシリアライズが高速
2. HTTP/2の多重化(マルチプレクシング)
- HTTP/1.1は同一コネクション内でリクエストが詰まりやすい
- HTTP/2は1本のTCP上で複数ストリームを同時処理できる
- 待ち時間が減り、スループットが上がる
3. ヘッダー圧縮(HPACK)
- RESTはヘッダーが毎回冗長になりやすい
- HTTP/2はヘッダーを圧縮し、繰り返しを省く
- 小さなリクエストほど効果が大きい
4. 接続回数の削減
- gRPCは長時間の接続を前提に設計されている
- ハンドシェイク回数が減り、往復遅延が縮む
5. 双方向ストリーミング
- リクエスト/レスポンスの分離ではなく、ストリームで連続送信できる
- 大量データやリアルタイム通信でオーバーヘッドが減る
🔄 3. 4つの通信パターン
Section titled “🔄 3. 4つの通信パターン”1. Unary RPC(単一リクエスト・単一レスポンス):
rpc GetUser(GetUserRequest) returns (User);2. Server Streaming(単一リクエスト・複数レスポンス):
rpc ListUsers(ListUsersRequest) returns (stream User);3. Client Streaming(複数リクエスト・単一レスポンス):
rpc CreateUsers(stream CreateUserRequest) returns (CreateUsersResponse);4. Bidirectional Streaming(複数リクエスト・複数レスポンス):
rpc Chat(stream ChatMessage) returns (stream ChatMessage);gRPCの実装例
Section titled “gRPCの実装例”Node.jsでの実装
Section titled “Node.jsでの実装”プロトファイル:
syntax = "proto3";
package user;
service UserService { rpc GetUser(GetUserRequest) returns (User); rpc CreateUser(CreateUserRequest) returns (User); rpc ListUsers(ListUsersRequest) returns (stream User);}
message GetUserRequest { int32 id = 1;}
message CreateUserRequest { string name = 1; string email = 2; int32 age = 3;}
message ListUsersRequest { int32 page = 1; int32 page_size = 2;}
message User { int32 id = 1; string name = 2; string email = 3; int32 age = 4;}サーバー実装:
import * as grpc from '@grpc/grpc-js';import * as protoLoader from '@grpc/proto-loader';import path from 'path';
const PROTO_PATH = path.join(__dirname, '../proto/user.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true,});
const userProto = grpc.loadPackageDefinition(packageDefinition).user as any;
// サーバーの実装const server = new grpc.Server();
server.addService(userProto.UserService.service, { GetUser: (call: any, callback: any) => { const userId = call.request.id; const user = getUserById(userId);
if (!user) { callback({ code: grpc.status.NOT_FOUND, message: `User ${userId} not found`, }); return; }
callback(null, user); },
CreateUser: (call: any, callback: any) => { const { name, email, age } = call.request; const user = createUser({ name, email, age }); callback(null, user); },
ListUsers: (call: any) => { const { page, page_size } = call.request; const users = getUsers(page, page_size);
users.forEach((user: any) => { call.write(user); });
call.end(); },});
server.bindAsync( '0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), (error, port) => { if (error) { console.error('Server failed to start:', error); return; } server.start(); console.log(`Server running on port ${port}`); });クライアント実装:
import * as grpc from '@grpc/grpc-js';import * as protoLoader from '@grpc/proto-loader';import path from 'path';
const PROTO_PATH = path.join(__dirname, '../proto/user.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true,});
const userProto = grpc.loadPackageDefinition(packageDefinition).user as any;
const client = new userProto.UserService( 'localhost:50051', grpc.credentials.createInsecure());
// Unary RPCclient.GetUser({ id: 1 }, (error: any, user: any) => { if (error) { console.error('Error:', error); return; } console.log('User:', user);});
// Server Streamingconst stream = client.ListUsers({ page: 1, page_size: 10 });stream.on('data', (user: any) => { console.log('User:', user);});stream.on('end', () => { console.log('Stream ended');});stream.on('error', (error: any) => { console.error('Stream error:', error);});⚖️ gRPC vs RESTful API
Section titled “⚖️ gRPC vs RESTful API”| 項目 | gRPC | RESTful API |
|---|---|---|
| 通信形式 | Protocol Buffers(バイナリ) | JSON(テキスト) |
パフォーマンス | 高い | 中程度 |
型安全性 | 高い | 低い |
ストリーミング | 対応 | 限定的(SSE、WebSocket) |
| ブラウザサポート | 限定的(gRPC-Web) | 完全 |
| 学習コスト | 高い | 低い |
🎯 使い分け
Section titled “🎯 使い分け”✅ gRPCを使うべき場合:
- 🔄
マイクロサービス間の通信 - ⚡ 高い
パフォーマンスが必要 - ✅
型安全性が重要 - 🔄
ストリーミングが必要
✅ RESTful APIを使うべき場合:
- 🌐 ブラウザからのアクセス
- 📝 シンプルな
API - 🌍 広くサポートされている形式が必要
gRPCのポイント:
- 📦 Protocol Buffers: 効率的なシリアライゼーション
- 🌐 HTTP/2: マルチプレクシングと
ストリーミング - 🔄 4つの通信パターン: Unary、Server Streaming、Client Streaming、Bidirectional Streaming
- ⚡ 高いパフォーマンス: バイナリ形式により高速
- ✅ 型安全性: スキーマにより型が保証される
適切にgRPCを使用することで、高性能なAPIを構築できます。