処理時間が長くてウェブページを閉じられない管理機能の解説と改善方法
はじめに
ウェブシステムの中には実行に非常に時間がかかる処理が実装されている場合があります。 この処理では主にデータの大量処理や複雑な計算処理を実行します。 この処理自体はありふれたものなのですが、これらの処理はブラウザを閉じるか別のページに移動すると中断されてしまう可能性があるため、ユーザーはブラウザを開いたまま待機しなければならないという問題が生じます。
この問題を日常生活に例えると、買い物中にレジで質問が発生し、担当者が確認のために席を外すと、その担当者が戻るまでレジの処理が止まり、自分だけでなく後ろに並んでいる人も待たされることになるような状況です。このような待ち時間が数分ならば許容されるかもしれませんが、30分から1時間も待たされることになると、多くの利用者に迷惑をかけてしまいます。
また、昨今のインフラ的にもこのような処理は不利になります。 というのも、CDN やロードバランサーには処理のタイムアウトが設定されていることが多く、一定時間を経過すると自動的にリクエストが打ち切られて終了してしまいます。値を設定することも可能なことはありますが、特に CDN は大きな値を設定できるようになっていません。 例えば、Amazon CloudFront の場合は最大で60秒になります。
この記事では、そのようなレスポンスに時間のかかる処理を効率的に管理し、ユーザーにストレスをかけない実装方法について解説します。
時間がかかる原因と改善方針
ウェブサーバーでの処理の流れは次のようになっています。まず、1. ウェブサーバーに処理の実行を依頼します。次に、2. ウェブサーバーでその処理を実行し、3. 処理完了後に結果としてレスポンスを返します。2. の段階で時間がかかると利用者にレスポンスが返ってこないため、ブラウザを開き続けて待機する必要があります。
大量のデータを処理する場合、アルゴリズムの改善だけでは処理時間の短縮には限界があります。そのため、ブラウザを開き続けて待機する状況を改善するにはこの処理の流れ自体を変更する必要があります。
先ほど買い物の会計時の例を挙げましたが、実際には別の方に作業を依頼することでお店全体の業務をスムーズにこなすように動いています。 具体的には以下のような作業を行っています。
- 会計担当者は質問に回答できる別の担当者に仕事を依頼して、自身は会計処理に注力します
- 会計担当者が自分自身で対応する場合でも、別の方に会計を頼んで後続の会計処理を可能としてくれます
自分1人で作業を完結させるのでは無く、別の人と分担・並行して作業にあたることで自然と効率的に作業を行っています。 そのため、コンピュータでの処理の場合も同様の概念を持ち込んで、作業を分担して行うことで利用者を長時間拘束せずに実行結果を提供することが可能です。
作業の分担の方法には色々な方法がありますが、メッセージキューを利用する方法がよく知られています。
メッセージキューについて
人と人との間であれば、会話やしぐさなどを使ってコミュニケーションが可能であり、個別に物事を認識して自発的に作業をすることができます。 しかし、コンピュータで分担した処理を行うようにするにはそのための仕組みを整える必要があります。
メッセージキューは異なるシステム間で情報のやり取りをする場合に利用される仕組みです。 現実世界に置き換えると、部署間の情報をやり取りするホワイトボードと付箋(カンバン)と考えると分かりやすいかもしれません。
メッセージキューを利用した処理の流れの一例は以下のようになります。
ウェブサーバーは利用者からのリクエストを受けると、その内容をメッセージキューに登録して即座に利用者に「処理を受け付けました」というレスポンスを返します(1~3)。 この中では「メッセージキューとデータベースに処理を登録する」という処理しか行っていないため、リクエストを受け付けてからレスポンスを返すまでの時間は非常に短くなります。
時間のかかる処理を実施するプログラムは、メッセージキューを監視して新しい処理をする必要があるかを常に見張っています(X)。 新しい処理をする必要がない時はただ待機しており、仕事が来たら働いて結果を保存して(Y)、再び待機する状態に戻ります。
ウェブサーバー側は処理の詳細は分かりませんが、自身が「どの仕事を依頼した」という情報だけは持っています。 そのため、該当処理情報を保存した Database にアクセス可能です。 この情報をもとにして依頼した処理の実行状況・実行結果を利用者に提供することができます(4~6)。
メッセージキューには複数の実装があり、RabbitMQ や Apache Kafka、Amazon SQS などが有名です。 しかし、小規模なシステムであればこれらの専門的な実装ではなくRDB テーブルを疑似的にキューとして利用したり、Redis の持つ publish / subscribe の機能を利用してメッセージキューを実現することもあります。
ウェブフレームワークの中にはこのような遅延実行の仕組みをデフォルトで提供しているものもあります。 例えば PHP の Laravel Framework であればジョブを定義して実行することができます。 利用するメッセージキューは複数の選択肢から自分で選ぶことが可能です。
https://laravel.com/docs/11.x/scheduling#scheduling-queued-jobs
おわりに
時間がかかる処理を実行するためにバッチ処理がよく使われますが、こちらは決まった時間で実行されることに対して、メッセージキューを利用した処理はユーザー起点で任意の時間に処理を行うことができます。 また、バッチ処理に処理の実態を書かず、バッチ処理は「単にメッセージキューにメッセージを登録するだけ」という使い方をすることで、処理をより汎用的にすることもできます。
メッセージキューを利用するシステムを構築しようとすると、ただのウェブシステムよりも複雑になります。 そのため、この仕組みを導入しないこともあるとは思いますが、システム運用の時間が経つに連れて取り扱うデータ量も多くなり、処理の完了までに長い時間待つ必要が出てくる、といったこともあります。 将来を見通すのは難しいですが、必要に応じてメッセージキューを導入したシステムが設計・構築できると、よりよりユーザー体験に一歩近付けるでしょう。