スレッドを使用してIISで長時間実行されるジョブを実行できますか?


85

ASP.Netアプリケーションでは、ユーザーがWebページのボタンをクリックすると、イベントハンドラーを介してサーバー上のオブジェクトがインスタンス化され、オブジェクトのメソッドが呼び出されます。メソッドは外部システムに移動して処理を実行しますが、これにはしばらく時間がかかる場合があります。したがって、私がやりたいのは、そのメソッド呼び出しを別のスレッドで実行して、「リクエストが送信されました」というメッセージでユーザーに制御を戻すことができるようにすることです。ユーザーがオブジェクトのステータスをポーリングし続けることができればさらに良いでしょうが、私はこれをファイアアンドフォーゲットとして行うことをかなり嬉しく思います。

私が知らないのは、ユーザーセッションが期限切れになっても、IISがスレッドの実行を継続できるかどうかです。ユーザーがイベントを起動し、サーバー上でオブジェクトをインスタンス化し、新しいスレッドでメソッドを起動するとします。ユーザーは「リクエストが送信されました」というメッセージに満足し、ブラウザを閉じます。最終的に、このユーザーセッションはIISでタイムアウトしますが、スレッドはまだ実行中であり、作業を行っている可能性があります。IISはスレッドの実行を継続できるようにしますか、それともユーザーセッションの期限が切れるとスレッドを強制終了してオブジェクトを破棄しますか?

編集:回答とコメントから、これを行う最善の方法は、実行時間の長い処理をIISの外部に移動することであると理解しています。他のすべてとは別に、これはappdomainリサイクルの問題を扱います。実際には、限られた時間でバージョン1をリリースする必要があり、既存のフレームワーク内で作業する必要があるため、サービスレイヤーを避けたいため、IIS内のスレッドを起動するだけで済みます。実際には、ここでの「長時間実行」は数分で、Webサイトの同時実行性は低くなるため、問題ないはずです。ただし、次のバージョンでは、別のサービスレイヤーに分割する必要があります。


1
何を処理する必要があるのか​​、ホスティング環境について詳しく教えてください。たとえば、サービスをインストールできますか?
senfo 2009

AsyncおよびAwaitプログラミングメソッド(Visual Studio 2012+)はいつでも使用できます。自分でスレッドを管理するよりもはるかにクリーンです。
SausageFingers 2016年

回答:


65

あなたはあなたが望むことを達成することができますが、それは通常悪い考えです。いくつかのASP.NETブログおよびCMSエンジンは、共有ホスティングシステムにインストール可能であり、インストールする必要のあるWindowsサービスに依存しないことを望んでいるため、このアプローチを採用しています。通常、アプリの起動時にGlobal.asaxで長時間実行されているスレッドを開始し、そのスレッドプロセスでタスクをキューに入れます。

IIS / ASP.NETがリクエストを処理するために利用できるリソースを減らすことに加えて、AppDomainがリサイクルされるときにスレッドが強制終了されるという問題もあり、実行中のタスクの永続性に対処する必要があります。また、AppDomainが復旧したときに、バックアップ作業を開始します。

多くの場合、AppDomainはデフォルトの間隔で自動的にリサイクルされ、web.configなどを更新した場合も同様です。

スレッドが強制終了される永続性とトランザクションの側面をいつでも処理できる場合は、一定の間隔でサイトにリクエストを送信する外部プロセスを使用することで、AppDomainのリサイクルを回避できます。これにより、サイトがリサイクルされた場合に、 X分以内に自動的に再起動することが保証されています。

繰り返しますが、これは通常悪い考えです。

編集:これが実際のこのテクニックのいくつかの例です:

コミュニティサーバー:Windowsサービスとバックグラウンドスレッドを使用して、スケジュールされた間隔でコードを実行 するWebサイトの最初の起動時にバックグラウンドスレッドを作成する

編集(遠い未来から)-最近はHangfireを使用します


1
おかげで、これは洞察に満ちています。リサイクルは間違いなくキラーであり、私はあなたが言っていることから、私はこれを困難で迅速なこととして行うことができますが、それは危険です。
Frans

はい、ここでこれを確認してください: professionalaspnet.com/archive/2007/09/02/… そして、COmmunity Serverが同じことをどのように処理するかについてのスレッドがあります: dev.communityserver.com/forums/t/459942.aspx
Bryan Batchelder

私の場合、IISにリサイクルしないように、またサイトをアイドル状態にしないように指示することができました。これにより、私はこの仮定の下で作業することができ、サイトは展開中および計画されたメンテナンスが発生したときにのみリサイクルされるようになりました。私は管理者タイプのサイトをやっていたので、これは私にとって完璧にうまくいきました。
ブレット2013年

1
これが非常に多くの賛成票を持っているのは奇妙です。このことが可能であるということは、ASP.NETの非常に優れた点の1つです。つまり、すべての要求が、作成できるバックグラウンドスレッドとともに同じAppDomainで処理されるということです。これが「悪い考え」である理由のどれも、私を説得力のあるものとは思わない。AppDomainsはダウンする可能性がありますが、それはASP.NET環境に固有のものではありません。あなたはあなたのホスティング環境(グーグルフォーHostingEnvironment.RegisterObject)でうまく遊ぶ必要があります。
ジョン

3
.NET 4.5.2には、QueueBackgroundWorkItemバックグラウンドタスクを簡単に登録するための新機能が追加されています。回答を再度更新することをお勧めします。
スコットチェンバレン

40

私は受け入れられた答えに同意しません。

Task.Factory.StartNewASP.NETでは、バックグラウンドスレッド(またはで始まるタスク)を使用しても問題ありません。すべてのホスティング環境と同様に、シャットダウンを管理する機能を理解して協力することをお勧めします。

ASP.NETでは、このHostingEnvironment.RegisterObjectメソッドを使用して、シャットダウン時に正常に停止する必要のある作業を登録できます。議論については、この記事とコメントを参照してください。

(Gerardがコメントで指摘してHostingEnvironment.QueueBackgroundWorkItemいるようRegisterObjectに、バックグラウンドアイテムが機能するスケジューラーを登録するために呼び出すこともあります。全体として、新しいメソッドはタスクベースであるため、より優れています。)

悪い考えだとよく耳にする一般的なテーマについては、Windowsサービス(または別の種類の追加プロセスアプリケーション)を展開する代わりの方法を検討してください。

  • Webデプロイで簡単なデプロイはもう必要ありません
  • 純粋にAzureWebサイトにデプロイすることはできません
  • バックグラウンドタスクの性質によっては、プロセスが通信する必要がある可能性があります。つまり、何らかの形式のIPCまたはサービスが共通のデータベースにアクセスする必要があります。

一部の高度なシナリオでは、リクエストと同じアドレス空間でバックグラウンドスレッドを実行する必要がある場合もあることにも注意してください。ASP.NETがこれを実行できるという事実は、.NETを通じて可能になった大きな利点だと思います。


これは素晴らしい。完了するまでに約30秒かかるタスクがあります。作業を完了するのに十分な時間だけアプリプールを遅らせる必要がある場合に最適です。
スキューバスティーブ

1
.NET Frameworkの4.5.2から、あなたもHostingEnvironment.QueueBackgroundWorkItem(アクション<CancellationToken>)を使用することができます
ジェラール炭

私の観察では、IISは実行中のASP.NET Web APIを最終的にシャットダウンし、新しい要求が着信するまでバックアップを開始しないようです。したがって、スレッドがアプリケーションを開始する予定の指定された時間に、実行されていません。私はこれについて間違っていますか?
DavidSopko19年

7

IISスレッドプールのスレッドをこのタスクに使用すると、そのスレッドが将来の要求を処理できなくなるため、使用しないでください。ASP.NET 2.0非同期ページを調べることもできますが、それも正しい答えではありません。代わりに、Microsoft MessageQueuingを調べることでメリットが得られると思われます。基本的に、タスクの詳細をキューに追加し、別のバックグラウンドプロセス(おそらくWindowsサービス)がそのタスクの実行を担当します。しかし、肝心なのは、バックグラウンドプロセスがIISから完全に分離されているということです。


ああ、MSMQ-私の長年のお気に入りのテクノロジーの1つ。洞察とリンクをありがとう。
Frans

6

そのような要件にはHangFireを使用することをお勧めします。その素晴らしいファイアアンドフォーゲットエンジンはバックグラウンドで実行され、さまざまなアーキテクチャをサポートし、永続ストレージに支えられているため信頼性があります。


5

ここに良いスレッドとサンプルコードがあります:http//forums.asp.net/t/1534903.aspx?PageIndex = 2

私は、アプリプールを存続させるために、スレッドから自分のWebサイトのキープアライブページを呼び出すというアイデアをもてあそびました。この方法を使用している場合は、アプリケーションがいつでもリサイクルされる可能性があるため、非常に優れたリカバリ処理が必要であることに注意してください。多くの人が言及しているように、他のサービスオプションにアクセスできる場合、これは正しいアプローチではありませんが、共有ホスティングの場合、これは唯一のオプションの1つである可能性があります。

アプリプールを存続させるために、スレッドの処理中に自分のサイトにリクエストを送信できます。これにより、プロセスが長時間実行される場合にアプリプールを存続させることができます。

string tempStr = GetUrlPageSource("http://www.mysite.com/keepalive.aspx");


    public static string GetUrlPageSource(string url)
    {
        string returnString = "";

        try
        {
            Uri uri = new Uri(url);
            if (uri.Scheme == Uri.UriSchemeHttp)
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
                CookieContainer cookieJar = new CookieContainer();

                req.CookieContainer = cookieJar;

                //set the request timeout to 60 seconds
                req.Timeout = 60000;
                req.UserAgent = "MyAgent";

                //we do not want to request a persistent connection
                req.KeepAlive = false;

                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                Stream stream = resp.GetResponseStream();
                StreamReader sr = new StreamReader(stream);

                returnString = sr.ReadToEnd();

                sr.Close();
                stream.Close();
                resp.Close();
            }
        }
        catch
        {
            returnString = "";
        }

        return returnString;
    }

5

私たちはこの道を歩み始めました、そして私たちのアプリが1つのサーバー上にあったときそれは実際にうまくいきました。複数のマシンにスケールアウトしたい場合(またはWeb garenで複数のw3wpを使用したい場合)、ワークキューの管理方法、エラー処理、再試行、および1つだけを確保するために正しくロックするというトリッキーな問題を再評価して検討する必要がありました。サーバーは次のアイテムを取得します。

...バックグラウンド処理エンジンを作成するビジネスではないことに気付いたので、既存のソリューションを探し、素晴らしいOSSプロジェクトのhangfireを使用して着陸しました。

Sergey Odinokovは、使い始めるのが本当に簡単な本物の宝石を作成しました。これにより、作業の永続化とキューイングの方法のバックエンドを交換できます。Hangfireはバックグラウンドスレッドを使用しますが、ジョブを永続化し、再試行を処理し、ワークキューを可視化します。したがって、hangfireのジョブは堅牢であり、リサイクルされるアプリドメインなどのあらゆる変動に耐えることができます。

その基本的なセットアップでは、ストレージとしてSQLサーバーを使用しますが、スケールアップするときにRedisまたはMSMQに交換できます。また、すべてのジョブとそのステータスを視覚化するための優れたUIに加えて、ジョブを再キューイングすることもできます。

私のポイントは、バックグラウンドスレッドで必要なことを実行することは完全に可能ですが、スケーラブルで堅牢にするための多くの作業があるということです。単純なワークロードには問題ありませんが、状況が複雑になると、この作業を行うよりも、専用のライブラリを使用する方がはるかに好きです。

利用可能なオプションの詳細については、asp.netでバックグラウンドジョブを処理するためのいくつかのオプションをカバーしているScottHanselmanのブログを確認してください。(彼はhangfireに熱烈なレビューを与えました)

また、Johnが参照しているように、アプローチに問題がある理由と、appdomainがアンロードされたときにスレッドでの作業を正常に停止する方法について、PhilHaackのブログを読む価値があります。


2

そのタスクを実行するためのWindowsサービスを作成できますか?次に、Webサーバーからの.NETリモーティングを使用してWindowsサービスを呼び出し、アクションを実行しますか?もしそうなら、私はそうするでしょう。

これにより、IISでリレーする必要がなくなり、処理能力の一部が制限されます。

そうでない場合は、プロセスが完了している間、ユーザーを強制的にそこに座らせます。そうすることで、IISによって完了し、強制終了されないようにすることができます。


2

IISで長時間実行される作業をホストするためのサポートされている方法が1つあるようです。 ワークフローサービスは、特にWindows Server AppFabricと組み合わせて、このために設計されているようです。この設計では、長時間実行される作業の自動永続化と再開をサポートすることで、アプリケーションプールのリサイクルが可能になります。


2

タスクはバックグラウンドで実行でき、リクエストが終了した後でも完了します。キャッチされない例外がスローされないようにしてください。通常は、常に例外をスローする必要があります。新しいスレッドで例外がスローされると、IISワーカープロセス(w3wp.exe)がクラッシュします。これは、要求のコンテキストに存在しなくなったためです。また、インプロセスメモリでバックアップされたセッションを使用している場合は、それらに加えて、実行中の他のバックグラウンドタスクもすべて強制終了します。これは診断が難しいため、この方法はお勧めできません。


1

非同期タスクを実行するための代理プロセスを作成するだけです。Windowsサービスである必要はありません(ただし、ほとんどの場合、これがより最適なアプローチです。MSMQは完全に機能しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.