しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET でセッション単位の同時リクエスト数を制限する

ASP.NET の SessionState Module の実装では、セッションにロックがかかっている場合にはタイマーで polling して待つ実装になってますが、ここで F5 とかされまくってしまったら IIS のリソースを食いすぎてしまうので、.NET 4.7 から上限が設定されました。

このあたりの変更に関しては、ちゃんと GitHub にドキュメントが公開されています。

In the .NET Framework 4.6.2 and earlier, ASP.NET executes requests with the same Sessionid sequentially, and ASP.NET always issues the Sessionid through cookie by default. If a page takes a long time to respond, it will significantly degrade server performance just by pressing F5 on the browser. In the fix, we added a counter to track the queued requests and terminate the requests when they exceed a specified limit. The default value is 50. If the the limit is reached, a warning will be logged in the event log, and an HTTP 500 response may be recorded in the IIS log.

dotnet/throttle-concurrent-requests-per-session.md at master · Microsoft/dotnet · GitHub

.NET 4.6.2 までは無制限にリクエストを待機させる実装でしたが、.NET 4.7 からは 50 リクエストまでに制限されました。それ以上のリクエストが来ると HttpException を投げるので 500 になります。

互換性を重視する場合には int.MaxValue の値を設定すればよいです。微妙に前回の Async SessionState にも関係してくる部分なので、ついでに試しておくことにしました。

Async SessionState Module にも同じ実装が入っているので、互換性はちゃんとあります。

試す場合には 50 リクエストというのは難易度が高いので、今回は 2 とか少なくして試します。Web.config にキーを追加すれば、その値が上限になるという簡単な設定方法です。

<appSettings>
  <add key="aspnet:RequestQueueLimitPerSession" value="2" />
</appSettings>

名前からわかるようにセッション単位の制限なので、50 というのは割と多いかもしれません。この辺りは適宜調整をすればよいのではないかと思いました。

Async SessionState で用意したように、10 秒ぐらい待たせるアクションに対してリクエストを複数回投げると、以下のように 500 エラーが返ってきます。

f:id:shiba-yan:20171126022631p:plain

セッションのロックで待つケースは大体不具合だと思いますし、結構長時間待たされてリクエストも複数回投げられてしまうはずなので、上限を設定しておくのは悪くない方法だと思いました。

Redis Session Provider を使っている場合には、ちょいちょいロック周りで問題が発生しているみたいですし、そういった不具合の緩和策としても多少は役に立つかもしれません。理想としては素早く処理を終わらせることですけどね。