ASP.NETで非常に重い処理を実行する

Category: fx aspnet_ja

Question

femp on Thu, 13 Feb 2014 08:25:36


業務用のWebアプリケーションでは、帳票出力やExcel出力といった要件はよくあると思いますが、この出力処理に何十秒もかかる(ほど出力データ量が多い)場合、ASP.NETではどのように設計するのが一般的なのでしょうか。

出力要求のHTTPリクエストで、ファイル出力処理用のスレッドを作って、一旦ブラウザにHTTPレスポンスを返し、Ajax等で定期的に進捗状況を監視する・・・という方法を取っているASP.NETアプリケーションを触っているんですが、本当に大丈夫なのか疑問を感じています。

やはり、ASP.NET内部でやろうとせずに、別プロセスで動作するサービスか何かを作って、帳票出力はそいつに任せるといったアプローチが一般的でしょうか。

Replies

SurferOnWww on Thu, 13 Feb 2014 08:54:19


スレッドプールの飽和が問題になると思いますが、それを緩和するための手段として、ASP.NET の非同期プログラミングモデルを使用するという方法があります。

MSDN マガジンの以下のページが、非同期プログラミングの目的、メリット、仕組みなどをサンプルコードを使って詳しく説明しており、参考になると思います。

ASP.NET の非同期プログラミングを使ったスケール変換可能なアプリケーション
http://msdn.microsoft.com/ja-jp/magazine/cc163463.aspx


上記の記事の中の、非同期 HTTP ハンドラのサンプルはかなり複雑で分かりにくいので、以下のページに簡単なサンプルを作ってみました。よろしければこちらも見てください。

非同期 HTTP ハンドラ
http://surferonwww.info/BlogEngine/post/2012/09/23/asynchronous-http-handler.aspx

なちゃ on Thu, 13 Feb 2014 14:11:31


バックグラウンドでスレッドを使って実行する方法は、確実性という点ではあまり高くありません。
現実的には数十秒程度で終わる処理ならあまり実害はないことが多いですが、安全な方法ではありません。

ごく簡単に言うと、ASP.NET環境では、アプリケーションのライフサイクルが独自に管理されており、それのあずかり知らないところで勝手にスレッドを使って処理をしていても、それを安全に面倒見てはくれないためです。

プロセスやサービスを外部に作成して別プロセスで処理させる方が、そういう意味で安全性は高まります。

あと、SurferOnWwwさんが書かれている非同期処理の方法は、何と言いうか(今現在質問者さんが書かれている状況や話とは)若干観点が異なる話ですので、混乱しないように念のため補足しておきます。

ASP.NETでの非同期処理(非同期ページや非同期ハンドラなど)というのは、HTTPクライアントからみると同期処理で、あくまでサーバ側でのスレッドプールを有効活用するための仕組みです。今回で言うと、ブラウザから見ればバックグラウンドで処理をさせるのではなく、ブラウザからの要求は処理が完了するまで返ってこないイメージになります。

例えば、帳票の作成処理は、Webサーバとは別の帳票サーバで行うようになっており、Webサーバからは帳票サーバをさらにWebサービスとして呼び出す、という構造だったとします。

IE→Webサーバ→帳票サーバ

この時、Webサーバから帳票サーバを同期呼び出しすると、帳票作成処理が終わるまで、Webサーバのスレッドが待機状態になります。これでは貴重なスレッドプールのスレッドを占有してしまうため、帳票サーバを非同期呼び出しし、帳票サーバからのレスポンスが帰ってくるまでは、スレッドをいったんスレッドプールに返せるようにした仕組みが、ASP.NETにおける非同期処理(非同期ページや非同期ハンドラなど)です。

このように、例えば帳票作成処理などの、時間のかかる処理が、外部リソースの非同期呼び出しとして実行できる場合のものですので、Webサーバ自身でがりがりCPUを使って処理するような場合には使えません(基本的に長いIO待ちが発生する場合用と思ってください)。

また、上にも書きましたが、非同期ページや非同期ハンドラを呼び出す側(ブラウザ)から見ると、普通の同期呼び出しになります。


SurferOnWww on Thu, 13 Feb 2014 15:57:28


> (今現在質問者さんが書かれている状況や話とは)若干観点が異なる話ですので、

質問者さんの観点は具体的にどういうことで、それとどのように異なるのでしょうか?

質問者さんの観点は、なちゃさんが質問者さんに直接聞くなどして確認されたのか、それともなちゃさんの推測なのかも併記いただけますようお願いします。

なちゃ on Thu, 13 Feb 2014 16:31:37


--------------

出力要求のHTTPリクエストで、ファイル出力処理用のスレッドを作って、一旦ブラウザにHTTPレスポンスを返し、Ajax等で定期的に進捗状況を監視する・・・という方法を取っているASP.NETアプリケーションを触っているんですが、本当に大丈夫なのか疑問を感じています。

--------------

という状況で気にしていることや、長時間かかる処理をASP.NETでは一般にどのように実装するか?という事象について、ASP.NETの非同期処理はちょっと目的の観点が異なりますよね?

時間のかかる処理を別サーバなどで実行して、それを呼び出しているときに、CPUが十分に活用できずスケールしない、という状況であれば非同期処理が候補に挙がってくる可能性はもちろんありますが(別にそれを否定したりしません)。

単に普通に読めば、質問者さんが「今」気にしているのはそこではなさそうだ、というだけです。

加えて、今の質問者さんは、クライアント側から見て非同期的な処理が頭にあると思われるので、ASP.NETの非同期処理を特に詳細な説明なく出すと混乱するかもしれないと思ったので補足しただけです。

SurferOnWww on Thu, 13 Feb 2014 17:33:56


質問者さんの質問に対して、なちゃさんと自分とで着目しているところが違うだけだと思います。

【なちゃさん】(今のやり方で)本当に大丈夫なのか疑問。 ⇒ バックグラウンドでスレッドを使って実行する方法は、確実性という点ではあまり高くありません・・・/長時間かかる処理をASP.NETでは一般にどのように実装するか? ⇒ ・・・プロセスやサービスを外部に作成して別プロセスで処理させる方が、そういう意味で安全性は高まります。

【自分】出力処理に何十秒もかかる場合 ASP.NETではどのように設計するのが一般的? ⇒ スレッドプールの飽和が問題になると思いますが、それを緩和するための手段として、ASP.NET の非同期プログラミングモデルを使用するという方法があります。

いかがですか?

femp on Thu, 13 Feb 2014 23:59:19


すみません、私の説明不足でした。

まず、元々の質問内容が「どのような設計が一般的か」「今私が触っているアプリの設計は問題無いのか」ですので、どのような観点からの指摘でも助かります。
ただ、これは私が明示しておらず申し訳ないのですが、なちゃ様が指摘されているように、「HTTPクライアントからみると同期処理」となると、何十秒もかかる処理では問題となります。

ここでいくつか疑問がわきましたので、続けて質問させて下さい。

【質問A】
観点は、SurferOnWww様に提示して頂いた非同期ハンドラが目的としている、スレッドプールの飽和に関してです。
非同期ハンドラの解説を読んでちょっと分からなかったのですが、
 ①非同期ハンドラを使う
 ②同期ハンドラでTaskクラスで取得したスレッドを使って処理し、HTTPレスポンスは一旦即座に返す
という2種の方法(後者は私が最初に挙げたもの)では、スレッドプールの飽和という観点で違いがありますか?
②でもスレッドプールの飽和対策自体は実現できていますか?

【質問B】
質問Aの①と②がいずれもスレッドプール飽和対策になっており、スレッドプールの飽和だけを問題視するのであれば、②よりも①の方が実装が簡単なので、優れています。
そうであるとしたとき、HTTPクライアントからも非同期に見える(できれば毎秒HTTPリクエストを投げて進捗を監視したい)ようにする場合、非同期ハンドラを使うメリットはあるのでしょうか。
(例えば、帳票出力処理を担当する別プロセスをキックするためのHTTPリクエストを受け取るのは、同期ハンドラで良いのか、非同期ハンドラだともっと良いことがあるのか)

Keiichi Oumi on Fri, 14 Feb 2014 01:42:55


こんにちは。

「どのような観点からの指摘でも」ということなので・・・


気になったのは、
「何十秒もかかる処理」があったとして、

1)ASP.NET アプリケーションデザインとしての方法・問題
2)システム(アプリケーション)デザインとしての方法・問題

どちらについて、より関心があるのか?が不明であるという事でした。多分両方を同時にとなると、色々発散していくのではないかなと思いました。

次に、「本当に大丈夫なのか疑問を感じています」ということで、この不安をもう少し掘り下げ、不安が何に起因しているのか?を考える必要もあるのかなと思います。

・利用者が「待つ」事自体の是非、要件から見てどうなのか。「何十秒も待つ」事の何が問題なのか?
・現在の仕組みが、トラフィックやサーバーリソースを考えた場合、許容範囲はどの程度になりそうか。
・Ajaxが持つ問題はないのか?
等々


例えば、
・レスポンスが帰ってくるまでに、30秒待つ
・レスポンスはすぐに帰ってくるが、30秒間刻々と進捗が表示される。
では結果的に、処理結果の完了を期待し、利用者は待ってしまう?といった事もありえます。
「実は15分待つ処理があり、以外に利用される」といった場合は、進捗表示自体の意味(や方法)を問われる可能性もあります。

また例えば、「即座に別のオペレーションに取り掛かれる事」という要件があった場合、利用者の注意を引く進捗状況の表示は是か非か、といった事もアーキテクチャ(やテクノロジ)選択する上で必要になるのではないかなと思います。
このあたりは、「ASP.NET内部でやろうとせずに、別プロセスで動作するサービスか何かを作って、帳票出力はそいつに任せるといったアプローチが一般的でしょうか」に掛かってくる可能性があります。


といったような事からも「現在の仕組み」が、
・要件を満足しているのか?(うまく業務に専念できるデザインなのか)
・テクノロジーとしての問題を何か抱えているのか?
・リソースに余裕はどの程度あるのか?
・将来的に、環境変動(トラフィック等)があった場合、どの程度満足できるのか?
・仕組みの変更(別プロセスでの処理等の採用)等があった場合に、どの程度容易に変更できるのか?
・その他

などを整理してみるという事も必要かもしれません。

SurferOnWww on Fri, 14 Feb 2014 08:08:06


> ①非同期ハンドラを使う
> ②同期ハンドラでTaskクラスで取得したスレッドを使って処理し、HTTPレスポンス
>   は一旦即座に返すという2種の方法(後者は私が最初に挙げたもの)では、スレ
>   ッドプールの飽和という観点で違いがありますか?
> ②でもスレッドプールの飽和対策自体は実現できていますか?

以下のページの「解説」によると、② は ThreadPool を使うそうなので、飽和対策にはなっていないと思います。

ThreadPool クラス
http://msdn.microsoft.com/ja-jp/library/system.threading.threadpool(v=vs.110).aspx


では、Thread を使ったらどうかという話もあるかもしれませんが、以下のページの A4) によると "don't even think about it." だそうです。理由も書いてあるので読んでください。

Performing Asynchronous Work, or Tasks, in ASP.NET Applications
http://blogs.msdn.com/b/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx


そもそも、② のような方法は、なちゃさんの言われるように、好ましくないと自分も思います。Web で以下のようなページを見つけましたが、個人的にはその意見に納得してます。

Using ThreadPool.QueueUserWorkItem in ASP.NET in a high traffic scenario
http://stackoverflow.com/questions/1325718/using-threadpool-queueuserworkitem-in-asp-net-in-a-high-traffic-scenario

"don't use the ThreadPool or a background Thread instance in ASP.NET. It's not at all like spinning up a thread in a Windows Forms application, where you do it to keep the UI responsive and don't care about how efficient it is. In ASP.NET, your concern is throughput, and all that context switching on all those worker threads is absolutely going to kill your throughput whether you use the ThreadPool or not."

というわけで、上に紹介した 2 つ目のページの最初の方に書いてあるように "I suggest that you use PageAsyncTask to execute work asynchronously, and enjoy the rest of your day." に賛成です。


それが質問者さんにとって NG であれば、Keiichi Oumi さんの言われるように、要件の整理など原点に立ち戻ってシステムのデザインを再検討したほうがよさそうな気がします。

なちゃ on Fri, 14 Feb 2014 12:06:09


ASP.NETでの非同期処理の仕組み(非同期ハンドラや非同期ページ)は、「非同期のワーク」をASP.NET上で効率よく扱うための仕組みです。
また、そのしくみがうまく動作するためには、対象となる「非同期のワーク」がIOバウンドであることが重要です。

「Performing Asynchronous Work, or Tasks, in ASP.NET Applications」とあるように、この話は、「非同期のワーク」が前提としてあり、それを効率よく実行するには、ASP.NETの非同期の仕組み(非同期ハンドラや非同期ページ)を使いなさいということです。

「長時間かかる処理は非同期で実行しなさい」ではありません。たとえばCPUバウンドの処理を、非同期化しても効率は悪化するだけです。
時間のかかる処理が別のWebサービス呼び出しなど、IOバウンドの処理であれば、非同期化すると効率化できる可能性はあります。

また、ASP.NETの非同期のしくみは長時間処理の解決策そのものではありません(長時間処理を、特定の方法で実装している場合に、それを効率的に処理できる手法ではありますが)。
ASP.NETで長時間処理を行う場合は、一般的に非同期の仕組みを使うというものではありません(繰り返しますが、長時間処理の実装方式によっては、これが有効な場合ももちろんあります)。

という辺りを質問者さんが誤解するとまずいので、念のため補足しておきます。

femp on Mon, 17 Feb 2014 00:52:30


皆さんありがとうございます。
非同期ハンドラ、というものに対してもやもやしていたものがようやく晴れてきました。
※なちゃ様の補足が助かりました。まさに混乱していたポイントです。
原理が分かればあとは要件との相談次第ですね。
大変参考になりました。