ストーリー
あなたが設計したマイクロサービスのプロトタイプを見た 高橋アーキテクト が、1つの質問をしました。
同期通信
リクエストを送信し、レスポンスが返るまで待つ通信方式です。
REST(HTTP)
// REST: 最もシンプルなサービス間通信
interface OrderService {
// 注文を作成(同期)
createOrder(data: OrderData): Promise<Order>;
}
// 実装例
async function createOrder(data: OrderData): Promise<Order> {
// 在庫サービスに同期で確認
const stock = await fetch("http://inventory-service/check", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ productId: data.productId, quantity: data.quantity }),
});
if (!stock.ok) throw new Error("在庫不足");
// 決済サービスに同期で決済
const payment = await fetch("http://payment-service/charge", {
method: "POST",
body: JSON.stringify({ amount: data.totalAmount }),
});
if (!payment.ok) throw new Error("決済失敗");
return saveOrder(data);
}
gRPC
// gRPC: 高速なバイナリ通信(Protocol Buffers)
// proto定義
// service InventoryService {
// rpc CheckStock (StockRequest) returns (StockResponse);
// }
import { InventoryServiceClient } from "./generated/inventory_grpc_pb";
const client = new InventoryServiceClient("inventory-service:50051");
async function checkStock(productId: string, quantity: number): Promise<boolean> {
const request = new StockRequest();
request.setProductId(productId);
request.setQuantity(quantity);
return new Promise((resolve, reject) => {
client.checkStock(request, (err, response) => {
if (err) reject(err);
else resolve(response.getAvailable());
});
});
}
REST vs gRPC 比較
| 観点 | REST | gRPC |
|---|---|---|
| プロトコル | HTTP/1.1 | HTTP/2 |
| データ形式 | JSON(テキスト) | Protocol Buffers(バイナリ) |
| 速度 | 普通 | 高速(5〜10倍) |
| ストリーミング | 困難 | ネイティブサポート |
| ブラウザ対応 | 完全 | 限定的(gRPC-Web) |
| デバッグ容易性 | 高い(JSON可読) | 低い(バイナリ) |
| 型安全性 | OpenAPIで補完 | スキーマ定義で保証 |
非同期通信
メッセージを送信したら、レスポンスを待たずに次の処理に進む通信方式です。
メッセージキュー
// メッセージキューによる非同期通信
interface MessageBroker {
publish(topic: string, message: unknown): Promise<void>;
subscribe(topic: string, handler: (message: unknown) => Promise<void>): void;
}
// 注文サービス(Producer)
async function createOrder(data: OrderData): Promise<Order> {
const order = await saveOrder(data);
// 非同期でイベントを発行(レスポンスを待たない)
await messageBroker.publish("order.created", {
orderId: order.id,
productId: data.productId,
quantity: data.quantity,
userId: data.userId,
});
return order; // すぐにレスポンスを返す
}
// 在庫サービス(Consumer)
messageBroker.subscribe("order.created", async (event: OrderCreatedEvent) => {
await reserveStock(event.productId, event.quantity);
});
// 通知サービス(Consumer)
messageBroker.subscribe("order.created", async (event: OrderCreatedEvent) => {
await sendOrderConfirmation(event.userId, event.orderId);
});
同期 vs 非同期:判断基準
同期が適切な場面:
├─ レスポンスが即座に必要(在庫確認 → 画面表示)
├─ 処理結果を待つ必要がある(決済の成否)
└─ リクエスト-レスポンスの1:1通信
非同期が適切な場面:
├─ 結果を待たなくて良い(通知送信)
├─ 1つのイベントを複数サービスが処理(1:N通信)
├─ 時間のかかる処理(PDF生成、データ集計)
└─ サービス間の結合度を下げたい
| 判断基準 | 同期 | 非同期 |
|---|---|---|
| 即時レスポンス | 必要 | 不要 |
| 結合度 | 高い(直接呼び出し) | 低い(ブローカー経由) |
| 信頼性 | 相手がダウン → 失敗 | キューにバッファリング |
| スケーラビリティ | 制限あり | 高い |
| デバッグ容易性 | 高い | 低い |
| 複雑さ | 低い | 高い |
ハイブリッドアプローチ
実際のシステムでは、同期と非同期を組み合わせます。
async function processOrder(data: OrderData): Promise<OrderResult> {
// 同期: 在庫確認(結果が即座に必要)
const stockAvailable = await inventoryService.checkStock(data.productId);
if (!stockAvailable) throw new Error("在庫不足");
// 同期: 決済(結果を待つ必要あり)
const payment = await paymentService.charge(data.amount);
if (!payment.success) throw new Error("決済失敗");
// 注文を保存
const order = await orderRepository.save(data);
// 非同期: 後続処理(結果を待たなくて良い)
await eventBus.publish("order.completed", {
orderId: order.id,
userId: data.userId,
});
// → 通知サービスがメール送信
// → 分析サービスが集計更新
// → 配送サービスが配送手配
return { orderId: order.id, status: "CONFIRMED" };
}
まとめ
| ポイント | 内容 |
|---|---|
| 同期通信 | REST, gRPC。即時レスポンスが必要な場面 |
| 非同期通信 | メッセージキュー。結合度を下げたい場面 |
| gRPCの利点 | 高速、型安全、ストリーミング対応 |
| ベストプラクティス | 同期と非同期のハイブリッド |
チェックリスト
- 同期通信と非同期通信の違いを説明できる
- RESTとgRPCの使い分けを判断できる
- メッセージキューによる非同期通信を理解した
- 同期・非同期の選択基準を説明できる
次のステップへ
次はAPI Gatewayとサービスメッシュを学びます。サービスが増えると、クライアントとサービス群の間に交通整理が必要になります。
推定読了時間: 30分