RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

オンライン-バッチ間の実行時間インピーダンスミスマッチのメッセージキュー(MQ)活用による解消

f:id:tech-rakus:20210104171422p:plain

こんにちは。 株式会社ラクスで先行技術検証を行っている「技術推進課」の堀内(id:yhoriuchi)です。 今回、先行技術検証の取り組みである「技術推進プロジェクト」で調査を行なったメッセージキュー(MQ)について紹介します。

調査項目としてMQを選定した理由とゴール

よくある話として、画面操作からのバッチ実行制御(実行順序制御、リトライ制御、エラー制御など)は非同期に動作するため実装が複雑化しやすいという問題を抱えています。 この「非同期な実行制御」をメッセージキュー製品で解決し、コードベースの簡素化が可能かの結論を出すことをゴールとして進めました。

MQとは何か

非同期型の通信方式のことです。システム間で通信を行う際、キューを挟んでメッセージをやり取りすることで非同期通信を実現します。 技技術的には1980年代から利用されており、歴史がある技術領域となっています。 近年ではマイクロサービスアーキテクチャや分散処理システムの非同期通信において利用されることが多いため、このコンテキストでは聞いたこと・使ったことのある方もいるかもしれません。

下記にMQを使わない場合、使った場合の違いを簡単な絵で説明します。

MQを使わない場合のイメージ

AさんからBさんへの作業依頼を直接行うことがイメージとして近いです。 Bさんが不在だったり、忙しい場合は作業依頼を受け付けてもらうことができないため、依頼できるようになるまでAさんは待ち続ける必要があります。

f:id:yhoriuchi:20201222143106p:plain:w400

MQを使った場合のイメージ

AさんとBさんとの間に「机」を設置して作業依頼を行うようなイメージです。Aさんは作業依頼を机に置いておくことでBさんに依頼を出すことができます。 Bさんは都合の良いタイミングで依頼を受け取れば良いですし、AさんはBさんの都合に合わせて待つ必要も無くなります。

f:id:yhoriuchi:20201222143103p:plain:w400

このように送信側(Aさん)と受信側(Bさん)はMQ(机)を挟むことでお互いが好きなタイミングで処理を行うことができるようになります。 さらには受信側に障害が発生していたとしても、送信側はそれを意識することなく作業依頼を送ることができるため、障害に強いシステムにすることができます。

MQが無い場合のシステム

画面からCSVをアップロードする場合を想定し、手っ取り早く実現することを考えると下記のようになります。

f:id:yhoriuchi:20201222151136p:plain:w500

①ファイルをアップロードする
②Webアプリケーションがリクエストを受け取り、バッチ処理を起動する
バッチ処理の中でDBへの書き込みを行う
④一連の処理が終わった後、ユーザーに結果を返す

この場合、リクエストを受けてバッチが実行されるため、ユーザーはリクエストを行うとすぐに処理が開始し、処理が終わればすぐに結果を受け取ることができます。また、特別な制御を行わないので実装量も少なくなります。 しかし、リクエストのたびにバッチ処理が実行されるため、たくさんのリクエストを同時に受け付けるとサーバーが高負荷になりやすいというデメリットもあります。

これを回避するために取られる手段としてDBを用いた実行制御が考えられます。
次のようなイメージです。

f:id:yhoriuchi:20201222151057p:plain:w700

このようにWebアプリとバッチの間にジョブ管理用のテーブルを用意し、cronや自作の常駐プロセスでジョブ管理テーブルを常に監視しながらバッチ実行を行うことになります。 DBを使ったジョブ管理も悪くはないのですが、1サーバーを複数の顧客で共有する(マルチテナント)場合は途端に考えないといけないことが増えます。
例えば、A社、B社、C社...と複数ある顧客でバッチ処理が同時に起動しても問題ないのか、サーバーリソースを考えると何社まで同時起動が許容されるか、シーケンシャルに全テナントを処理した方がよいのか、などなど。システム要件にもよるので、どれが良い悪いではありませんが、考えることが増えると実装にも跳ね返ってくるのが世の常ですよね。

MQを導入した場合

先ほどの画面からのCSVアップロードは下記のような構成になります。

f:id:yhoriuchi:20201222154455p:plain:w500

①ファイルをアップロードする
②Webアプリケーションがリクエストを受け取り、キューに登録する(このタイミングでユーザーにレスポンスを返す)
③ワーカープロセスがキューからメッセージを1つずつ取り出す
④DBへの書き込みを行う
⑤一連の処理が終わった後、ユーザーに結果を通知する

キューを介してWebアプリとバッチ処理であるワーカープロセスを分離することが可能となり、非同期処理を実現できるようになります。 さらにキューからメッセージを1つずつ取り出して処理するため、同時アクセスによる多重起動を気にする必要はありません。 下記のようなイメージです。

f:id:yhoriuchi:20201222155237p:plain:w500

こうなってくるとバッチ処理部分をスケールしたい場合にどうするのか気になるところですが、この場合はキューに接続するバッチをスケールしたい分だけ単純に増やしてあげれば実現できます。基本的にキューのメッセージは排他制御されるため、別のプロセスが同じメッセージを取り合うことはありません。セマフォのような制御を意識する必要もありません。

f:id:yhoriuchi:20201222160619p:plain:w500

さらに、メッセージを取り出すとメッセージは削除されます。何らかの理由でリトライが必要な場合はメッセージをキューに戻す(リキューと呼ぶ)ことも簡単に実現できます。 通常DBでジョブ管理を行うとテーブルをロックしてステータスを「処理中」に更新する、完了したら完了ステータスに更新する、一定の期間が経過したらジョブ管理テーブルから履歴を消す、といったことが必要になってくると思いますが、MQを利用するとこれらの実装は不要となります。

結論

ここまで調べてきた結果、下記の理由から複雑な箇所をMQに任せることができるため、実装が減りコードベースを簡素化できることが分かりました。

  • MQでリクエストを1列に整列できるため、多重起動制御の実装が不要になる。
  • MQのメッセージ登録、取得がジョブのステータス管理に相当するため、ステータス管理を実装する必要がない。
  • メッセージは重複なくキューから受信側に配信されるため、排他制御を意識する必要がない。

利用できるユースケース

CSVファイルアップロードを例に考えてきましたが、MQの利用が向いていると思われるユースケースを考えてみました。 下記のような瞬間的に大量リソースを要求するケースや、機能間の疎結合を実現したい場合に有効となります。

  • 大量のデータ登録/出力など、長時間リソースを消費するケース
    • CSVアップロード/CSVダウンロードなど
  • 画像変換等、瞬間的に大量リソースを使うケース
    • 画像のリサイズ、加工をユーザーリクエストに応じて実施するなど
  • サービスとサービスを分離したいケース
    • マイクロサービスアーキテクチャのような構成で、サービス間にMQを挟むことで分離する

最後に

MQを調べ始めた頃、基本的な用語が分からず変に時間がかかってしまったので、基本用語の説明と検証した結果からの所管を書いて締めくくろうと思います。

基本用語

MQ製品を調べているとプロデューサー/コンシューマーパブリッシャー/サブスクライバー(略してパブサブとも呼ばれる)が説明として出てくることがありますが、この用語を理解しておくと製品理解がスムーズなのでここで説明しておきます。

プロデューサー/コンシューマー

1つのメッセージが1つの受信者だけに配信されるケースでは、送信側を「プロデューサー」、受信側を「コンシューマー」と呼びます。
1対1通信となる設計で使われる用語で、このページで紹介した内容がまさにプロデューサー/コンシューマーの関係で成り立っています。 多くのWebアプリでは直感的に理解しやすい構成だと思います。

パブリッシャー/サブスクライバー

MQでは1つのメッセージを2つ以上の受信者にブロードキャストする仕組みがあり、その場合は送信側と受信側の呼び方が上記と変わります。
送信側が「パブリッシャー」、受信側が「サブスクライバー」となります。さらにキューは「トピック」と呼ばれるようになります。(調べ始めの頃はトピックが何かが分かっておらず、ドキュメントを読むのに苦労していました...)

検証した結果からの所感

今回の検証でMQを使えば、リクエスト毎にプロセスを起動する実装のデメリットを解消しつつ、DBでジョブ管理を行う際の煩わしさから解放されることが分かりました。
サーバーのリソースコントロールが必要な際には是非とも導入しておきたい技術だと思います。
一方で、すでにDBによるジョブ管理を行なっており、実装が複雑になってしまっている場合、MQに置き換えるべきかについてはどうでしょうか。
個人的な結論となりますが、このケースでは無理に置き換えなくて良い考えています。実装が複雑になっていたとしても、それで保守・運用が回っているのであればMQに置き換えるだけのコストメリットはないでしょう。 もしかしたらジョブを追加、変更する頻度が多くて、その都度バグが発生するようなケースがあるかもしれませんが、この場合は作り直しが候補に上がってくると思います。作り直すのであればMQの導入も検討に含めるのが良いでしょう。

Copyright © RAKUS Co., Ltd. All rights reserved.