StackOverflowで興味深い問題を処理しています。
「すぐにやりたい」という小さなタスクがたくさんあります。例としては、「関連質問」リストの更新があります。過去に行ったことは、これらのタスクを一部のユーザーのページロードに便乗させることです。
これは決して理想的ではありませんでしたが、それほど目立ちませんでした。 SOが1,000,000個の疑問符を通過したので、これらの不運なユーザーはそれを感じ始めています。
自然な解決策は、これらのタスクを実際にバックグラウンドにプッシュすることです。私が検討しているこれを行うには、2つの広範な方法があります。
基本的に、いくつかの(非 ThreadPool 、IISに干渉しないようにするための)スレッドをスピンアップし、それらが Funcs に示すいくつかのコレクションにサービスを提供します。
ここでの大きな利点はシンプルさです。マーシャリングについて心配する必要はありません。また、外部サービスが稼働していることを確認する必要もありません。
また、すべての共通コードにもアクセスできます。
悪い点は、バックグラウンドスレッドを使用しないことです。私が知っている異論はすべて、飢餓IIS(ThreadPoolを使用している場合)とスレッドがランダムに停止する(AppPoolリサイクルのため)ことに集中しています。
ランダムスレッドの停止を問題のないものにするための既存のインフラストラクチャがあり(タスクを検出することは基本的に中止されています)、スレッド数の制限(およびThreadPool以外のスレッドの使用)も難しくありません。
StackOverflowに移動 、これは実際にはここでは取り上げられていません。
一部のサードパーティソリューション、またはカスタムソリューション。
基本的には、プロセスの境界を越えてタスクをマーシャリングしてサービスを提供し、それを忘れてしまいます。おそらく、いくつかのコードをリンクするか、生のSQL +接続文字列に制限しています。
長所は、これを行う「正しい方法」です。
短所は、私たちができることは非常に制限されているか、このサービスをコードベースと同期させるために何らかのシステムを構築する必要があるということです。また、何らかの方法ですべての監視とエラーログをフックする必要があります。これは、[IIS内]オプションで無料で取得できます。
サービスアプローチに他の利点または問題はありますか?
簡単に言えば、アプローチ#1を実行不可能にする予期せぬ、乗り越えられない問題はありますか?そうであれば、アプローチ#2を調査する必要がある優れたサードパーティサービスはありますか?
数週間前、私はSOについて 同様の質問 を尋ねました。一言で言えば、今の私のアプローチは、Windowsサービスを開発することでした。 NServiceBus(本質的にはMSMQ)を使用して、Webアプリからサービスへの要求をマーシャリングします。以前はWCFを使用していましたが、分散トランザクションをWCF上で正しく機能させることは、常にお尻の痛みのように思えました。 NServiceBusがうまくいったので、トランザクションでデータをコミットしてタスクを作成でき、そのときにサービスが稼働しているかどうか心配する必要はありませんでした。簡単な例として、Eメール(たとえば、登録Eメール)を送信する必要がある場合は、トランザクションでユーザーアカウントを作成し、Windowsサービスにシグナルを送信します(Eメールを送信します)。サービス側のメッセージハンドラーがメッセージを取得し、それに応じて処理します。
ASP .NET 4.0とAppFabricがリリースされたため、上記のメカニズムの実行可能な代替手段がいくつかあります。上記の質問に戻って、AppFabricのAppInitialize(via net。パイプ)だけでなくASP .NET 4.0の自動起動機能により、WebサービスとしてのWindowsサービスの開発が実行可能な代替手段になりました。さまざまな理由でこれを始めました(最大の1つ)展開であることはもはやお尻の痛みではありません):
この方法を使用する場合(元の投稿からのコピーと貼り付けはご容赦ください)、バックグラウンドロジックを別のWebアプリケーションで実行することを検討します。これにはいくつかの理由があります。
これを行うと、マーシャリングの側面に戻ります。 WCF、NServiceBus/RabbitMQ/ActiveMQなど、Vanilla MSMQ、RESTful API(MVCなど)はすべてオプションです。 Windows Workflow 4.0を使用している場合、Webアプリが使用できるホストエンドポイントを公開できます。
サービスに対するWebホスティングアプローチはまだかなり新しいものであり、それが正しい選択であったかどうかを判断するのは時が経てません。しかし、これまでのところとても良いです。ちなみに、AppFabricを使用したくない場合(奇妙な理由でWindows Server Web Editionがサポートされていないため、私は使用できませんでした)、Guの投稿に記載されている自動開始機能は適切に機能します。ただし、applicationhost.configファイルから離れてください。その投稿のすべては、IISコンソール(メインサーバーレベルの構成エディター))を介して設定できます。
注:最初はこのメッセージにいくつかのリンクを投稿していましたが、悲しいかな、これはこの交換への私の最初の投稿であり、サポートされているリンクは1つだけです。基本的に他に2つあり、Googleに「Death to Windows Services ... Long Live AppFabric!」および「auto-start-asp-net-applications」。申し訳ありません。
Windowsでバックグラウンドサービスを実行する3つ目の方法は実際にあり、UNIXの世界では非常に一般的です。 3番目の方法は、インフラストラクチャの一部を実行するCRON
ジョブです。 Windowsでは、これはtask scheduler
と呼ばれ、定期的にコードを実行するのに非常に一般的です。これを使用するには、事前に定義されたスケジュールで実行されるコマンドラインアプリを作成します。これの利点は、プロセスがサービスのように稼働し続けているかどうか心配する必要がないことです。なんらかの理由で失敗した場合、次回起動するだけだからです。
特定のタスクのマーシャリングについては、これらのタスクを永続的なバイナリストレージに保存するだけで十分です。コマンドラインアプリがストレージからそれらを選択して実行するまで。 Cassandraデータベースを特定のユーザーのバックグラウンドタスクをCassandraデータベースに詰め込むためのセッション状態プロバイダーとして使用し、コマンドラインはそれらを取り出し、ユーザーのために実行します。
これは典型的なマーシャリングソリューションではなかったかもしれませんが、スケジュールされたタスクがシャットダウン、ネットワークの問題、および中央であったため、すべてのマシンがタスクを実行できるため、私にとって非常にうまく機能し、非常にエレガントなソリューションであることがわかりました保管。
恥知らずなプロモーションですが、これは私のプロジェクトであり、私が手短に説明した解決策は、私がプロジェクトを作成した理由です: http://github.com/managedfusion/fluentcassandra/
これは、Webファームと共に水平方向にスケーリングし、すでに知っているWebテクノロジスタック.
仕組みは次のとおりです。
http://mydomain.com/system/cron
。やったー!これで、30秒ごとに呼び出されるルートができました。また、リクエストの処理に5分かかる場合は、ユーザーのページリクエストの一部ではないため、誰も気にしません。
cron
アクションは非常にシンプルに見えます。特定の頻度で実行するメソッドのリストがあります。リクエストが届くと、実行する必要のあるメソッドがあるかどうかを確認し、適切なメソッドを呼び出します。 これは、データベースでスケジュールを制御できることを意味します。このサイトでは、他の重要な構成データがすでに多数ある可能性があります。
さらに重要なことは(あなたにとって)、これは、ジョブが固定されたスケジュールで呼び出される必要がないことを意味します。メソッドをいつ実行するかを決定するために必要なロジックを記述できます。
注:質問や懸念がある場合は、コメントを追加してください。詳しく説明させていただきます。
私は現在のアプリケーションでこれを行うためのほぼすべての可能な方法を試して使用しました。私は、あなたが現在行っているのと同じことから始めました。データを入力し、次にそれをキャッシュするというユーザー要求に便乗しています。これも悪い考えだと気づきました(特に、複数のWebサーバーにスケーリングすると、より多くのユーザーがヒットします)。
また、ASP.NETアプリのURLにヒットするスケジュールされたジョブもありました。これはまともなソリューションですが、1台のWebサーバーを超えてスケーリングした分が壊れ始めています。
現在、私は2つの異なる方法を使用しています。どちらも、すばらしい小さなライブラリであるQuartz.NETを使用しています。 1つ目は、ASP.NETとインプロセスで実行されるQuartz.NETです。global.asaxで設定され、数分ごとに実行されます。これを使用してASP.NETキャッシュを帯域外で更新します。これは、ASP.NETの一部として実行される唯一の理由です。
2つ目は、Quartz.NETをラップするためにDaemonMasterというライブラリを作成したことです。これにより、DLLをディレクトリにドロップし、Windowsサービスで実行することが簡単になります。回避するのに役立つことがわかりました。 Windowsサービスでの作業の厄介な部分のいくつかと、Quartz.NET APIのクリーンアップもあります。DaemonMasterを介して実行されるサービスには2つの異なるフレーバーがあり、1つ目は毎晩またはすべてのX minutsを実行する必要があるジョブです。 ASP.NETアプリケーションから受信したデータに基づいて、他のジョブがキューから動作します。ASP.NETアプリは、RabbitMQにJSONオブジェクトをドロップし、サービスはRabbitMQをポーリングしてデータを処理します。
これに基づいて、Windowsサービス(およびDaemonMasterをチェックアウト)を使用することをお勧めします。必要に応じて、RabbitMQなどのキューを使用して、ASP.NETアプリからサービスにデータを渡すことで、これらすべてのソリューションの中で最良に機能しました。キャッシュをロードしている場合、ASP.NETで実行することは理にかなっていますが、そうでない場合はそうは思いません。
私はそれを正しい方法で行い、「キュー」を監視するWindowsサービスを実行しています。 MSMQを使用したプログラミングは、目玉にホットポーカーを突き刺すようなものなので、「キュー」と言います。
Railsでの Delayed :: Job の単純さに夢中になりました。同様のことが.NETでも簡単に行えます。
基本的には、あらゆる種類のSomethingOperation
(Perform()
メソッドを持つもの)を追加します。次に、関連するパラメーターをシリアル化し、優先順位を付けて、ある種のデフォルトの再試行動作を設定して、データベースに入れます。
サービスはこれを監視し、キュー内のジョブを処理します。
サービスバス、メッセージキュー、サービスのアプローチにかなり満足しています。基本的なアーキテクチャはこれです。
Webサイトはメッセージをキューに送信します
bus.Send(new ProjectApproved()); // returns immediately
Windowsサービスは独自の時間にメッセージを受信して処理します
public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
public void Consume(ProjectApproved Message)
{
// Do something "offline"
}
}
利点は、ユーザーが接続しているフロントエンドサービスにも遅延がないことです。メインサイトを中断することなく、Windowsサービスをシャットダウンしてアップグレードできます。さらに、それは非常に高速です。
メッセージ内にすべてのデータを保存できない場合は、いつでも保存して後で取得できます。 RavenDB または MongoDB などのドキュメントストレージメカニズムを使用することをお勧めします。
Webサイトはメッセージをキューに送信します
// Save your object
store.Save(completeProject);
// Send a message indicating its ready to be processed
bus.Send(new ProjectApproved() { ProjectId = completeProject.Id });
Windowsサービスは独自の時間にメッセージを受信して処理します
public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
public void Consume(ProjectApproved Message)
{
// Retrieve your object back
var completeProject = store.Get(Message.ProjectId);
}
}
物事を簡単にするために、 Rhino ESB および Topshelf を使用します。設定は非常に簡単で、既存のアプリケーションにこれを配置するのに時間がかからないことがわかっています。
なぜこの2つの組み合わせが実行可能な選択肢ではないのか、私は興味があります。現在、ページビューでジョブをトリガーし、不幸な樹液がページが表示されるまで10秒間待機してスタックします。少なくとも、あなたの現在の方法に対する私の理解です。
ただし、これらのジョブはサイトが成長するにつれて実行に時間がかかり、サイトのユーザーエクスペリエンスを損なうことは望ましくありません。一日中、数人(または多くの場合)の不運なユーザーもいないため、バックグラウンドでジョブをスケジュールすることを考えています。
定期的に実行されるバックグラウンドジョブが訪問者を模倣できない理由はわかりません。現在、私はWindowsプログラマではありませんが、Linuxの世界では、一定の間隔で実行されるcronジョブをセットアップし、2行のコードを記述します。
#!/bin/bash
wget -O /dev/null http://stackoverflow.com/specially_crafted_url
両方のシステムの長所を組み合わせています。バックグラウンドで行われます。ユーザーには影響しません。それでも、ページビューを使用してジョブを開始します。このアプローチが以前に使用されたのを見たことがあります。それは、古い単純な方法と、より複雑な方法の道の中間にある傾向があります。
更新
Webサーバー自体でジョブランナーを実行することで、負荷分散の問題を回避できると思います。ジョブランナーは、ジョブキューからURLを取り出し、次のように実行します。
wget -O /dev/null http://localhost/specially_crafted_url
ジョブ/メッセージングキューの性質により、ジョブはジョブランナー間で均等に分散されます。つまり、specially_crafted_urlは最終的にWebサーバー間で分散されます。
純粋なサービスアプローチの欠点は、コードがサービスに分散していて、コアアプリから離れていることです。
以下は、時間に依存しない大きなバックグラウンドジョブで行ったコードです。これにより、コードがまとめられ、サービスが簡素化されます。
さらに簡単なのは、コンソールアプリで呼び出しを行い、タスクスケジューラまたはVisualCronを使用してそれを「サービス」に変えるだけです。
Resque はいいです。または Kthxbye 完了時に結果の値を通知する必要がある場合。
Redis/Rubyベースの両方。
正直なところ、あなたがサービスベースのアプローチをしているなら、それはあなたの現在のプラットフォームと本当に超統合する必要はありません。私はそれが(ある種のモニタリングで)実行され、ジョブを完了する、一生忘れられないシステムになることを願っています。データベース情報を更新/変更するだけなので、同じプラットフォームで実行する必要があるかどうかはわかりません。
特にスレッド化の問題に対処しているように思われるので、このようなワークアウトを別のエンティティにファーム化すると、はるかに多くの時間を節約できます。 Resque と Kthxbye の両方は、OSが並行性を処理できるように、処理を別のプロセスに移動します。
TopShelfが気に入りました。シンプルさを保ちながら、Windowsサービスとして実行する適切な方法を実行します。基本的にコンソールアプリを作成し、約15〜20行のコードを追加してから、サービスとしてインストールします。
Webサーバー上で実行され、さまざまなタスクを実行するメンテナンスURLに定期的にアクセスする非常に単純なWindowsサービスを用意するのはどうでしょう。特定のリクエストで実行する作業量を調整します。
ここでは明らかな傾向を打ち破り、IIS内モデルに進むことをお勧めします。私はそれを自分で使用しましたが、それは本当にうまくいきます。まともなスレッドプールクラスを実装するのはそれほど難しくありません(長年にわたって、スレッドプールクラスを拡張して、スレッドの動的な作成と破棄、ジョブの再試行などをサポートしてきました)。利点は次のとおりです。
私の意見では、IIS内ソリューションは、ランダムなページビューに作業を便乗させることからの「次のステップアップ」にすぎません。
タスクの概念
App Engineバックグラウンド処理では、タスクは小さな作業単位の完全な説明です。この説明は2つの部分で構成されています。
オフラインWebフックとしてのタスク
幸いなことに、インターネットはすでにそのようなソリューションをHTTP要求とその応答という形で提供しています。データペイロードは、Webフォーム変数、XML、JSON、エンコードされたバイナリデータなどのHTTPリクエストのコンテンツです。コード参照はURL自体です。実際のコードは、サーバーが応答の準備で実行するロジックです。
MSMQキューをリッスンするWASがホストするWCFサービスを使用します。
プロの
Webアプリからの一方向のメッセージを起動して忘れる
MSMQ/WCFスロットリングと再試行
配達保証; D
デッドレター管理
分散処理
WAS/MSMQアクティベーション
コンの
WCFのMSMQ機能により、MSMQの使用は非常に便利です。はい、あなたは構成に出血しますが、利点は犠牲を上回ります。
Webアプリケーションを開発するときに、これに何度か遭遇しました。私たちは、タスクを実行するWindowsコンソールアプリケーションを作成し、実際にタスクを実行するために頻繁に実行されるスケジュールされたタスクを作成することで、問題を解決してきました。
Rxと次のようなものを使用して、作業をバックグラウンドスレッド(または多くのバックグラウンドスレッド)にシャントできます。
var scheduler = new EventLoopScheduler( SchedulerThreadName );
_workToDo = new Subject<Action>();
var queueSubscription = _workToDo.ObserveOn( scheduler ).Subscribe( work => work() );
_cleanup = new CompositeDisposable( queueSubscription, scheduler );
使用するには:
var work = () => { ... };
_workToDo.OnNext( work ); // Can also put on error / on complete in here
これまでに1つしか存在しないクラス内ですべてをホストします(シングルトンとも呼ばれますが、適切に実行してください-IoCコンテナーを使用してライフスタイルを決定します)。
単一のスレッドを実行するEventLoopSchedulerを使用する代わりにカスタムスケジューラを作成することで、スレッドプールのサイズなどを制御できます。
両方する
ユーザーリクエストで現在便乗している作業を行う質問パスにオプションのパラメーターを追加します。
各サーバーで実行し、IISログ共有バイナリを開き、それをファイルの現在の末尾まで読み取ります。コンソールファイルを監視するためにファイルシステムウォッチャーまたは時間間隔を使用して更新を収集します。 IISログをフラッシュしました。
この情報を使用して、現在表示されているページを判別します。
解析されたログからのページURLを使用して、Webクライアントオブジェクトでlocalhost上の「余分な」バージョンのURLを呼び出します。
いくつかのコードを追加して、各ログ期間の終わりにファイルを切り替えるか、ログ期間ごとにプロセスを再起動します。
私はこのタイプのものを数回実装しました。 Windowsでは、pythonコマンドラインプログラムをセットアップして、さまざまなタイミングで何かを実行します。このプログラムは、ポートでxmlrpcインターフェースも公開します。その後、スケジュールされたタスクジョブが毎分実行され、 xmlrpcインターフェースを照会します。インターフェースが起動していない場合は起動を試み、起動できない場合はメールで通知します。
利点は、実行されるジョブがcronやスケジュールに拘束されないことです。 1秒ごとに実行されるプロセスジョブがありますが、実行する作業があったかどうかに応じて、新しいジョブを開始するまでの待機時間が長くなります。また、結果に基づいてインテリジェントに行動するために使用できます。 500エラーが発生しましたか?本当に長い遅延がありましたか?他のことをしてください。別のサービスに通知します。等。
また、同じシステムがunixでも機能し、若干の変更が加えられています。
自分には答えはありませんが、問題がベルを鳴らします-ランダムな人を覚えています ポッドキャストで一度話し合った 。
スポルスキー:ブログで質問した質問の1つに、メンテナンスの定期的なタスク全般をどのように処理するべきかということに気付きました。
アトウッド:はい。
スポルスキー:それは公平な特徴ですか?どのWebサイトにも、Webページのロード時に実行したくないタスクがいくつかありますが、ある種の繰り返しで実行する必要があります。
Atwood:そうですね、バックグラウンドタスクのようなものです。
スポルスキー:ええ、あなたは何を考えましたか?
Atwood:ええと、私はもともとTwitterで質問しました。軽量なものが欲しかったからです。私は本当にWindowsサービスを書きたくありませんでした。それは帯域外コードのように感じました。さらに、実際には作業を行うコードは実際にはWebページです。これは、私にとってWebサイトの作業の論理単位はWebページだからです。だから、本当にウェブサイトにコールバックしているようなものです。それはウェブサイトの別のリクエストのようなものなので、インラインのままにすべきであり、Twitterで私に勧められた小さなアプローチを考えました本質的には、固定の有効期限でアプリケーションキャッシュに何かを追加することでした。コールバックがあり、有効期限が切れたときに、機能する特定の関数を呼び出してから、同じ有効期限でキャッシュに追加します。だから、それは少しです、多分「ゲットー」は正しい言葉です。