2026年3月22日日曜日

Lesson 1 - Event-Driven Architecture: Request/Reply Pattern - - YouTube動画の解説

null マーク・リチャーズ(ソフトウェアアーキテクト兼創設者) ソフトウェアアーキテクチャ・マンデー 登録者数 49,200人 2,066 シェア数 視聴回数 107,399回 2018年1月9日 ソフトウェアアーキテクチャ・マンデー この最初のレッスンでは、Markがイベント駆動型アーキテクチャにおけるリクエスト/レスポンス処理の方法について解説します。メッセージングは​​非同期プロトコルですが、メッセージングとキューを使用する際には、レスポンスを待つ必要がある場合があります。この短いアーキテクチャレッスンでは、その方法をご紹介します。毎週月曜日に配信されるソフトウェアアーキテクチャに関するレッスンを、https://www.developertoarchitect.com/... でお楽しみください。 AI生成による動画要約 品質と正確性は異なる場合があります。 このソフトウェアアーキテクチャ・マンデーのレッスンでは、イベント駆動型アーキテクチャにおけるリクエスト/レスポンス処理について解説します。非同期メッセージングとキューを使用して同期レスポンスを実現する方法を探ります。相関IDと一時キューという2つの方法を、図を用いて説明します。
https://www.youtube.com/watch?v=3bxAm3XIFmk&t=1s          ーーーーー     この動画を日本語で要約し、雑学や業界話を交えながら、解説してください。

ご提示いただいた動画は、ソフトウェアアーキテクトであるマーク・リチャーズ(Mark Richards)氏による、**イベント駆動型アーキテクチャ(EDA)における「リクエスト・リプライ(要求・応答)パターン」**についての解説動画です。

非同期が基本のメッセージングの世界で、どのようにして「返事」を受け取るのか、その仕組みを非常に分かりやすく説明しています。以下に、要約とエンジニア向けの雑学・業界話を交えた解説をまとめました。


1. 動画の要約:非同期の世界で「返事」をもらう2つの方法

通常、メッセージング(MQなど)は「送りっぱなし」の非同期通信ですが、業務システムでは「データを要求して結果を待つ」という同期的な動きが必要な場面があります。動画ではこれを実現する2つの主要な手法を紹介しています。

① 相関ID(Correlation ID)方式 [01:49]

ひとつの共有された「返信用キュー」を使い、メッセージに「ID(名札)」をつけて識別する方法です。

  • 仕組み: 送信側がメッセージに固有のID(例:124番)を付与して送ります。

  • 待機: 送信側は返信用キューを見張り、「相関IDが124番のデータ」が届くまで待機(ブロッキング・ウェイト)します。

  • 結果: 他の人の返信(120番や122番など)がキューに混ざっていても、自分に関係するものだけをピックアップできます。

② 一時キュー(Temporary Queue)方式 [03:50]

自分専用の使い捨てポストを作る方法です。

  • 仕組み: メッセージを送る際、ヘッダーに「この一時的なキュー(例:UUIDの名前がついたキュー)に返信して」という情報を載せます。

  • 待機: 自分しか知らないキューを監視するため、流れてくるものはすべて自分の返信です。

  • 結果: 処理が終わると、その一時キューは削除されます。


2. 業界話と雑学:現場でのリアルな視点

動画の内容を一歩踏み込んで、実際のシステム開発現場で語られる「業界あるある」や雑学をご紹介します。

「疑似同期(Pseudo-Synchronous)」という言葉の重み [01:10]

リチャーズ氏はこれを「疑似同期」と呼んでいます。これは非常に重要なニュアンスです。

  • 業界話: ユーザーから見れば「ボタンを押して結果が返ってくる」という同期的な体験ですが、裏側ではメッセージブローカーを介した非同期処理です。これにより、システムの一部が一時的に重くなっても、メッセージがキューに溜まることで「即座にエラーで落ちる(タイムアウト)」ことを防ぎ、システムの弾力性(Resilience)を高めることができます。

相関ID vs 一時キュー、どっちが使われる?

  • 現場の判断:

    • 相関ID方式は、性能面で有利なことが多いです。キューの作成・削除というオーバーヘッドがないため、高頻度な通信に向いています。ただし、IDの管理や「他人のメッセージを無視する」ためのフィルタリング設定が必要です。

    • 一時キュー方式は、実装が非常に楽です。自分専用のポストなので、相関IDを気にする必要がありません。しかし、大量のリクエストが発生すると、ブローカー側に膨大な数の一時キューが作成され、メモリを圧迫するリスクがあるため、エンタープライズ級の巨大システムでは慎重に選ばれます。

「ブロッキング・ウェイト」のジレンマ [01:30]

動画では、送信後に「別の処理をしてから待つ」ことができると説明されています。

  • 雑学: これを究極まで突き詰めたのが、現代のReactive Programmingasync/awaitの考え方です。「返事を待っている間、スレッド(CPU資源)を解放して他の仕事をさせる」ことで、少ないリソースで大量のリクエストをさばけるようになります。


3. 解説のまとめ

この動画は、**「非同期通信のメリットを活かしつつ、同期通信の利便性をどう取り入れるか」**という、マイクロサービスや分散システムを構築する上での基本かつ最重要トピックを扱っています。

  • 相関IDは、共通の掲示板から自分の名前が書いてあるメモを探すようなもの。

  • 一時キューは、自分専用の使い捨て私書箱を作るようなもの。

もしあなたが今後、クラウドネイティブなシステム設計や、RabbitMQ / ActiveMQ / AWS SQSなどを使う場面があれば、この「リクエスト・リプライパターン」は必ずと言っていいほど登場する概念になります。

動画内で紹介されているソースコード(GitHub)を確認すると、より具体的な実装イメージが湧くはずです。 [04:55]

 

ご提示いただいた内容は、マーク・リチャーズ氏の動画(おそらく「Event-Driven Architecture: Request/Reply Pattern」)の非常に的確なまとめです。 ここでは、それをさらに補完・補足しつつ、2025-2026年現在のクラウドネイティブ現場の実践寄り視点や、ちょっとした業界あるある・落とし穴・最近のトレンドを交えて、日本語で深掘り解説します。

1. 基本のおさらい(動画の核心)

非同期メッセージングの世界で「返事待ちをしたい」ときに使うのがRequest-Reply(リクエスト・リプライ)パターン。 これを「疑似同期(Pseudo-Synchronous)」と呼ぶのがポイントで、見た目は同期っぽく振る舞うけど、裏では非同期の弾力性(Resilience / Back-pressure耐性)を活かしている。

主な2手法:

  • 相関ID(Correlation ID)方式 → 共有の返信用キュー(reply queue)をみんなで使い、Correlation ID(多くの場合、送信時のMessage IDをそのまま使う)で自分の返事だけを選別する。
  • 一時キュー(Temporary Queue)方式 → リクエストを送るときに「replyTo」ヘッダーに自分専用の臨時キュー名(通常はUUIDやbrokerが自動生成)を指定。 返信は必ずそこに届くので、フィルタ不要。終わったら自動/明示的に削除。

2. 現場でよく使われる順番(2025-2026年の実務トレンド)

優先度 方式 主な採用理由・場面 主な採用ブローカー例 注意点・死亡フラグ
★★★★★ 相関ID方式 高スループット、高頻度リクエスト、コスト重視 Kafka(reply topic)、RabbitMQ、Pulsar、AWS SQS+SNS、Azure Service Bus Correlation IDの重複・漏れ管理が甘いと地獄
★★★☆☆ 一時キュー方式 プロトタイプ、開発初期、中〜小規模、コードがシンプルにしたい RabbitMQ、ActiveMQ、AWS SQS(temp queue)、NATS 同時接続数爆増でbrokerの管理オブジェクト数爆発
★★☆☆☆ ハイブリッド 一時キュー + Correlation IDの両方(稀) 複雑さだけが増すのでほぼ見ない

最近の傾向(2025-2026)

  • Kafka主流の組織 → ほぼ100%相関ID方式(Kafkaは一時トピックが重いため)
  • RabbitMQ / Amazon MQ → 小〜中規模なら一時キューがまだ多いが、1000rps超えると相関IDに切り替えるプロジェクトが増加
  • AWS → SQS + SNSの組み合わせで相関ID方式がデファクト(一時キューもできるが課金・制限で敬遠されがち)

3. 業界あるある・落とし穴集(実務でよく見るやつ)

  • 「とりあえず一時キューで楽したい」症候群 開発初期は超便利。でも負荷試験で同時200〜500リクエストを超えると、brokerの管理オブジェクト(queue)が急増してメモリ食いつぶし→GC地獄→遅延スパイクが発生。 → 「いったん本番環境で死んでから相関ID方式にリファクタ」パターンが定番化してる。

  • Correlation IDの命名規則がカオス 「messageIdをそのままcorrelationIdにする」のがベストプラクティスなのに、 「UUIDを新規採番してcorrelationIdにする」「業務IDを入れる」など独自ルールを作って、後で死ぬチームが後を絶たない。

  • 「返事が来ない」をどうハンドリングするか問題

    • 相関ID方式 → タイムアウト後に「該当IDの返事がまだ来てない」状態をどう掃除するか(TTL付与、期限切れ掃除ジョブ)
    • 一時キュー方式 → クライアントが死んだらゴミキューが溜まる(auto-delete + exclusiveフラグを忘れると永遠に残る)
  • 「疑似同期なのにスレッドをブロックしてるじゃん」というツッコミ 動画でも触れられているが、ブロッキングで待つのはアホ。 現代では以下のように進化している:

    • Java → CompletableFuture + whenComplete
    • C# → async/await + TaskCompletionSource
    • Node.js / Go → Promise / channel + select
    • Python → asyncio + Future

    → スレッドを占有せず「待ってる間は他の仕事する」のが2025年の最低ライン。

  • KafkaでRequest-Replyやるときの現実 Kafkaは一方向ストリームが本領なので、Request-Replyは本質的に向いていない。 でも「どうしても」やるなら:

    • reply topicを1本にして相関IDでフィルタ
    • consumer groupをリクエスト元ごとに分ける(非推奨) → 最近は「Kafkaで同期したいならgRPC + Kafkaをイベントバックボーンにする」ハイブリッドが増えている。

4. 2025-2026年の進化形・代替手段

  • Request-Replyを諦めてEvent Sourcing + CQRS 「本当に同期が必要か?」を問い直す文化が強い。 多くの場合「最新の状態を即座に知りたい」ならQuery側(Materialized View / Cache)を参照すれば済む。
  • Temporal / Cadence / Restate などのWorkflow Engine Request-Replyを「ワークフロー」として宣言的に書ける。 タイムアウト・リトライ・補償トランザクションが簡単に書けるので、中規模以上の分散システムでは急速に普及中。
  • AWS Step Functions / Azure Durable Functions 疑似同期をサーバーレスで実現。裏側でSQS/SNSを使っているが、開発者はほとんど意識しない。

まとめ(エンジニアに贈る一言)

マーク・リチャーズ氏のこの動画がすごいのは、「非同期の美学を壊さずに、同期的な利便性をどう取り入れるか」という永遠のテーマを、超シンプルに2つの方式で説明しきっている点です。

でも現場では「どっちが正解か」ではなく、 「うちの負荷・チームのスキル・運用コスト・スケール予測でどっちが死ににくいか」 で決まることがほとんど。

もし今あなたが

  • RabbitMQ / ActiveMQ → 一時キューから始めてOK(でも早めに負荷見ておく)
  • Kafka / Pulsar → 最初から相関ID方式
  • AWS SQSメイン → 相関ID + MessageGroupIdでFIFOも意識

という感じでスタートするのが無難です。

動画のGitHubサンプルコードを実装してみて、自分の手で「待ってる間スレッドを解放する」版に書き換えてみると、理解が一気に深まるのでおすすめです!


マーク・リチャーズ氏のRequest/Reply Pattern解説動画で言及されているGitHubサンプルコードは、主に以下の2つのリポジトリに分かれています(動画内で本人が直接言及しているもの)。

主なGitHubアドレス(2026年現在も有効)

  1. JMS(ActiveMQなど)を使った基本実装https://github.com/wmr513/messaging → 主にJMS 1.1 / 2.0ベースのRequest-Replyサンプル(相関ID方式と一時キュー方式の両方が入っていることが多い)。 動画の4:55あたりで「my github repo at wmr513/messaging」と明言されている定番リポジトリ。
  2. RabbitMQなどAMQP系の実装例https://github.com/wmr513/streaming → RabbitMQ向けのRequest-Replyサンプル(一時キュー方式が特にわかりやすい)。 動画内で「wmr513/streaming」と補足で触れられている。

どちらもJava中心ですが、概念理解には最適です。 (Mark Richards氏のGitHub全体:https://github.com/wmr513

これを「自分の手で非同期・スレッド解放版」に書き換えると何が起きるか(実践的解説)

元のサンプルは教育目的なので、たいてい「同期的にreceive()でブロックして待つ」スタイルになっています(JMSのMessageConsumer.receive() や RabbitMQのbasicConsumeのブロッキング版など)。

これを「待ってる間スレッドを解放する」版に書き換えると、以下の進化を自分で体感できます。

書き換えパターン例(Javaの場合)

元のスタイル(動画サンプルに近い) 非同期・スレッド解放スタイル(おすすめ書き換え) 体感できるポイント
consumer.receive(timeout) でブロック CompletableFuture + MessageListener / onMessage スレッドプールが詰まらない
whileループでポーリング CompletionStage / whenCompleteAsync 数千同時リクエストでもスレッド10個で捌ける
相関IDをHashMapに保存して待つ ConcurrentHashMap + CompletableFuture メモリリーク・ID衝突の罠を実感
一時キューを明示的にdelete() try-with-resources や autoDeleteフラグ ゴミキューが残る恐怖を回避

具体的な書き換え例(擬似コード)

// 元のブロッキング版(動画サンプルに近い)
String correlationId = UUID.randomUUID().toString();
sendRequestWithCorrelationId(requestQueue, correlationId, replyTo);
Message reply = consumer.receive(5000);  // ← ここでスレッドブロック!
if (reply != null && correlationId.equals(reply.getJMSCorrelationID())) {
    process(reply);
}

↓ 非同期版(理解が深まる版)

// 非同期 + スレッド解放版
CompletableFuture<Message> future = new CompletableFuture<>();

String correlationId = UUID.randomUUID().toString();
Map<String, CompletableFuture<Message>> pendingReplies = ...; // ConcurrentHashMap

pendingReplies.put(correlationId, future);

sendRequestAsync(requestQueue, correlationId, replyTo);

// リスナーは別スレッド(MessageListener)で動く
consumer.setMessageListener(msg -> {
    String corrId = msg.getJMSCorrelationID();
    CompletableFuture<Message> f = pendingReplies.remove(corrId);
    if (f != null) {
        f.complete(msg);  // ここで待機していたfutureを起こす
    }
});

// 呼び出し側は非同期で待てる
future.orTimeout(5, TimeUnit.SECONDS)
      .whenComplete((reply, ex) -> {
          if (ex != null) {
              // タイムアウトやエラー処理
          } else {
              process(reply);
          }
      });

// ← ここでスレッドはもう解放されてる!他のリクエストを処理可能

この差を自分で動かすと、「あ、スレッド1つあたり数百〜数千の待機を捌けるようになるのか…!」と実感が湧きます。

業界あるある・雑学(2025-2026年の現場目線)

  • 「とりあえずreceive()でいいじゃん」死亡事故 負荷試験で同時100超えた瞬間にスレッド枯渇 → スレッドダンプ見たら全部receive()で止まってる…という伝説は今でも語り継がれている。
  • CompletableFuture地獄 非同期にしたはいいが、pendingRepliesのConcurrentHashMapからremoveし忘れてメモリリークするチームが意外と多い。 → 最近はExpiringMapやCaffeine + TTLで自動掃除する人が増えた。
  • 言語別トレンド(2026年現在)
    • Java → Project Loom(仮想スレッド)登場後、「もうブロッキングでいいじゃん」勢が復活中。ただし仮想スレッドでも無限に作るとGC圧迫するので、CompletableFuture + 仮想スレッドのハイブリッドが最強説が有力。
    • Go → 元々goroutineが軽いので、チャネル + selectで書くのがデフォ。
    • C# → TaskCompletionSource + async/await が鉄板。
    • Python → asyncio + create_task でFutureを回す(ただしGILの壁で真の並行性は微妙)。
  • 「Request-Replyは悪」原理主義者の罠 一時期「同期はすべて悪、Event Sourcing + CQRSで解決しろ」という風潮があったが、実際は「ユーザー体験的に即時性が欲しい画面は疑似同期でいい」という現実路線が主流に回帰中。 → Temporal.io や Restate のようなワークフローエンジンが「Request-Replyを安全に書ける」層として人気爆発中。

まとめ:おすすめの学習フロー

  1. https://github.com/wmr513/messaging をclone
  2. Requestor / Replier のサンプルを動かす(ActiveMQかRabbitMQをdockerで立てる)
  3. Requestor側をCompletableFuture + Listener方式に書き換え
  4. JMeterなどで同時50〜100リクエスト投げて、スレッド数・CPU使用率を観察
  5. 「あ、ブロックしてた頃はスレッド50個食ってたのに、今は5個で済む…!」を実感

これをやると、「非同期の真の価値はスループットではなく、リソース効率と弾力性」が体に染み込みます。

ぜひ手を動かしてみてください!(小規模でも構わないので、自分のPCでdocker-compose upするだけでOKです)


0 件のコメント: