Microsoft IISは、Windows標準のWebサーバーです。GUIで簡単に設定・操作が可能で、多くのWebシステムに採用されています。弊社でもIISの性能管理に関する多数のご要望をいただいており、そのほとんどが業務に欠かせない重要なWebシステムです。そんなIISですが、セッション管理に関するパフォーマンスの注意点をご存じでしょうか。
本記事では、IISのセッション管理の仕様が原因となり、深刻なレスポンス遅延が発生した事例をご紹介します。こちらの事例では、たった1ユーザーのネットワーク品質の問題で、IISのセッション管理の負荷が増大し、Webシステムのレスポンスが通常の20倍遅延するという事態となりました。IISの仕様が改善されない限り、他のWebシステムでも同様の問題が発生する可能性があります。対策も記載しましたので、IISで構築されたWebシステムを開発・保守されている方は、ぜひご一読ください。
また、今回のレスポンス遅延はDynatraceにより分析しました。IISのWebサーバーにOneAgentを導入されている環境でしたら、現時点でIISのセッション管理の負荷がどの程度なのかすぐに確認可能です。該当の環境がある場合は、ぜひ確認してみてください。
Webシステムで処理遅延が発生したお客様より、Dynatraceによる原因分析をご依頼いただきました。ある時間帯以から突然システム全体で処理が遅くなり、IISの再起動によって一時的に復旧したとのことです。
このように、お客様にて確認した範囲では原因が特定できなかったため、原因調査と次回発生時の早期検知方法の確立が求められました。
事象発生時、Dynatraceでは「User action duration degradation」という処理遅延のプロブレムが検出されていました。
プロブレムの詳細を確認すると、通常は平均2.5秒程度のWebシステムのレスポンスが、1分と20倍以上に遅延し、それが1時間も継続していました。また、「根本原因」のセクションには以下のイベントが検出されていました。
[応答時間の劣化を分析]をクリックすると、下図のように応答時間の内訳が表示されます。今回の事象では、IIS modulesの応答時間が長く、特に「Session」モジュールの応答時間が長い状況でした。
[失敗率の劣化の分析]ボタンをクリックすると、下図のようにエラーメッセージや、エラーが発生しているリクエストのURLが表示されます。今回の事象では、HTTP 500(Internal Server Error)が出力され、「The request queue limit of the session is exceeded.」というメッセージが出力されていました。
それでは、応答時間の割合として最も高かった、「Session」モジュールとは何でしょうか。これは、セッションデータを管理するモジュールです。セッションデータとは、ブラウザがWebシステムにアクセスしてから終了するまでサーバー側で保持しておくデータです。
例えば、ECサイトで商品をカートに入れた場合、他のページに遷移してもカートの中身が変わりません。これは、サーバーがブラウザごと=セッションごとのデータを保存しているためです。このセッションごとのデータを管理しているのが、IISの場合には「Session」モジュールです。
それでは、「Session」モジュールの処理が遅延するのはどのような場合でしょうか。IISの「Session」モジュールの遅延についてインターネットで検索すると、「セッションロック」にたどり着きました。
参考文献: Stack Overflow「Session lock causes ASP.Net websites to be slow」
セッションロックとは、セッションデータを使用する際に他の処理が読み込めないように制限する機能です。例えば、先ほどのECサイトの例では、カートの中身を変更しながら別タブなどでカートの中身を確認しようとすると、どちらが正しい情報か分からなくなってしまいます。そこで、カートの中身を変更する処理が終わるまではカートの中身を確認する処理の実行を待ってもらうようにロックをかけます。
IISの場合、このセッションロックが非常に多く発生します。なぜなら、IISではデフォルトで、セッションデータを変更しない場合でも、すべての処理がセッションデータのロックを取得する仕様となっているためです。
ユーザーが1操作ずつ、読み込みが終わるのを待ってから次の操作に移る場合には問題となりません。しかし、ユーザーが処理完了を待ちきれずに別の操作をしたり、F5キーなどでページを再読み込みすると、前の処理がセッションデータをロックしているため、前の処理が終わるまで待たされます。また、各処理はセッションロックが解除されるのを頻繁に確認するため、処理待ちが蓄積すると、Sessionモジュールには大きな負荷がかかります。
たしかに、セッションデータの整合性を保つためにセッションロックは必要です。しかし、問題なのは、データの参照だけでもロックを取得する仕様となっているという部分です。この仕様により、ページの読み込みのたびにセッションロックが発生し、Sessionモジュールの負荷が発生しているという事実はあまり知られていないのではないでしょうか。
今回のレスポンス遅延では、IISのSessionモジュールの応答時間が長かったため、あるユーザーが処理を待たずに別の操作や再読み込みを繰り返したのではないかと推測しました。また、続いて、どのユーザーのどのような操作が原因だったのか、という確認に進みました。
根本原因となるユーザーの調査にあたり、着目したのが、もうひとつの根本原因であるエラーメッセージです。このエラーメッセージについてインターネットで検索すると、ASP.NET 4.7の機能拡張に関する情報が見つかりました。
参考文献: Microsoft社「Throttle concurrent requests per session」
従来のバージョンでは、特定のユーザーが一度に複数のページを開いたり、再読み込みを繰り返すと、セッションロック待ちのキューが蓄積され続けていました。そこで、ASP.NET 4.7では、キューに入れられたリクエスト数をカウントし、制限(デフォルトは50個)を超えた場合はリクエストを強制終了するように変更されました。エラーメッセージ「The request queue limit of the session is exceeded.」は、あるセッションでキューの数が実際に制限値を超えた状況を示しています。
そのため、このエラーが発生した処理のリクエスト元ユーザーを確認し、該当ユーザーの操作内容を確認しました。
IISログやDynatraceの分散トレースで該当のエラーが発生しているリクエストをご確認いただいたところ、特定の1ユーザーでエラーが多発していました。さらに、同じURLに対して同じユーザーからマイクロ秒単位まで同じタイミングでリクエストを受け付けていました。
IISのセッションロックの仕様上、すべてのリクエストはセッションロックを取得しようとしますが、まったく同じタイミングでロックを取得しようとするとどうなるのでしょうか。推測ですが、デッドロックのように複数のリクエストがロックをつかみあって処理が進まない可能性もあります。今回の原因は、このマイクロ秒単位まで同じタイミングで発行された複数のリクエストであると推定しました。
それでは、なぜマイクロ秒単位まで同じタイミングでリクエストが発生したのでしょうか。該当リクエストが発生したユーザー操作の確認のため、Dynatraceのセッションリプレイも確認しました。セッションリプレイとは、ユーザーのクリック、スクロール、文字入力などの操作を記録し、ビデオのように再生する機能です。今回のように、サーバーが受け付けたリクエストだけでなく、ユーザー操作まで追いたい場合に効果を発揮します。
テスト環境で同様の操作を実施しても、事象は再現されませんでした。
結局、下記のような状況下で発生した事象と結論付けられました。
ユーザー操作の確認結果から、サーバー側の動作に問題はなく、クライアント側の通信速度が問題であるという結論に至りました。
サーバー側でできる対策としては、IISのセッションロックを取得しないように設定変更が可能です。IISではデフォルトで、すべてのリクエストがセッションロックを取得します。しかし、セッション情報を変更しないページの場合、読み取り専用(ReadOnly)と宣言すると、セッションロックを取得しなくなります。これにより、たとえマイクロ秒単位まで同じセッションから同じタイミングでリクエストが発行されたとしても、同時並行で処理ができるので、リクエストの滞留を抑制可能です。
実装方法は下記のとおりです。
設定方法の詳細は以下の参考文献も確認してください。
参考文献: Microsoft社「Locking Session-Store Data」
参考文献: Stack Overflow「Session lock causes ASP.Net websites to be slow」
また、同様の事象の早期発見のため、エラーメッセージ「The request queue limit of the session is exceeded.」についてアラートを設定しました。
本記事では、IISのセッション管理の仕様として、下記のようなパフォーマンス上の注意点を解説しました。
また、IISのWebサーバーにOneAgentを導入済みの環境であれば、以下の観点から「セッション管理」の負荷状況をご確認いただけます。
これらの確認により、IISの「セッション管理」の負荷が高いと判断されるWebシステムについては、対策としてセッション情報を変更しないページの場合、読み取り専用(ReadOnly)と宣言するなど、対処方法をご検討いただければと存じます。
本記事が少しでも皆様のお役に立てば幸いです。