6

必要:

  • DB のジョブ キューからジョブを実行する Windows サービス

募集:

  • このタイプのアプリケーションのサンプル コード、ガイダンス、またはベスト プラクティス

バックグラウンド:

  • A user will click on an ashx link that will insert a row into the DB.
  • I need my windows service to periodically poll for rows in this table, and it should execute a unit of work for each row.

Emphasis:

  • This isn't completely new terrain for me.
    • EDIT: You can assume that I know how to create a Windows Service and basic data access.
  • But I need to write this service from scratch.
  • And I'd just like to know upfront what I need to consider.
  • EDIT: I'm most worried about jobs that fail, contention for jobs, and keeping the service running.
4

4 に答える 4

8

データベース キューを扱っていることを考えると、データベースのトランザクションの性質により、ジョブのかなりの部分が既に完了しています。典型的なキュー駆動型アプリケーションには、次のようなループがあります。

while(1) {
 Start transction;
 Dequeue item from queue;
 process item;
 save new state of item;
 commit;
}

処理が途中でクラッシュした場合、トランザクションはロールバックされ、アイテムは次のサービス起動時に処理されます。

しかし、実際には、データベースにキューを書き込むのは、あなたが思っているよりもずっとトリッキーです。素朴なアプローチを展開すると、エンキューとデキューが互いにブロックされ、ashx ページが応答しなくなることがわかります。次に、デキューとデキューがデッドロックになっており、ループが常にエラー 1205 に達していることがわかります。この記事Using Tables as Queuesを読むことを強くお勧めします。

あなたの次の課題は、プーリング率を「ちょうどいい」ものにすることです。積極的すぎると、プーリング リクエストでデータベースが燃え尽きてしまいます。緩すぎると、ラッシュアワーにキューが増え、排水が遅くなりすぎます. まったく異なるアプローチを使用することを検討する必要があります。SQL Server の組み込みオブジェクトを使用し、セマンティクスQUEUEの魔法に依存します。WAITFOR(RECEIVE)これにより、完全にポーリングのない自己負荷調整サービスの動作が可能になります。実際には、他にもあります。最初からサービスは必要ありません。私が話していることの説明については、非同期プロシージャの実行を参照してください。完全に信頼できる方法で、Web サービス呼び出しから SQL Server で非同期に処理を開始します。そして最後に、これにより、T-SQL プロシージャではなく、スタンドアロン プロセスで処理をホストできます。

于 2010-07-16T15:59:28.363 に答える
4

まず、検討する必要があります

  1. ポーリングする頻度
  2. あなたのサービスは停止して開始するだけですか、それとも一時停止して続行することをサポートしていますか?
  3. 同時実行。サービスは、問題が発生する可能性を高める可能性があります

実装

  1. Threading.Timer ではなく System.Timers.Timer を使用する
  2. Timer.AutoReset を false に設定してください。これにより、リエントラントの問題が停止します。
  3. 必ず実行時間を含めてください

これが、これらすべてのアイデアの基本的なフレームワークです。これをデバッグする方法が含まれていますが、これは面倒です

        public partial class Service : ServiceBase{

        System.Timers.Timer timer;


        public Service()
        {

        timer = new System.Timers.Timer();
        //When autoreset is True there are reentrancy problme 
        timer.AutoReset = false;


        timer.Elapsed += new System.Timers.ElapsedEventHandler(DoStuff);
    }


     private void DoStuff(object sender, System.Timers.ElapsedEventArgs e)
     {

        Collection stuff = GetData();
        LastChecked = DateTime.Now;

        foreach (Object item in stuff)
        {
            try
                    {
                        item.Dosomthing()
                    }
                    catch (System.Exception ex)
            {
                this.EventLog.Source = "SomeService";
                this.EventLog.WriteEntry(ex.ToString());
                this.Stop();
        }


        TimeSpan ts = DateTime.Now.Subtract(LastChecked);
        TimeSpan MaxWaitTime = TimeSpan.FromMinutes(5);


        if (MaxWaitTime.Subtract(ts).CompareTo(TimeSpan.Zero) > -1)
            timer.Interval = MaxWaitTime.Subtract(ts).TotalMilliseconds;
        else
            timer.Interval = 1;

        timer.Start();





     }

        protected override void OnPause()
     {

         base.OnPause();
         this.timer.Stop();
     }

     protected override void OnContinue()
     {
         base.OnContinue();
         this.timer.Interval = 1;
         this.timer.Start();
     }

     protected override void OnStop()
     {

         base.OnStop();
         this.timer.Stop();
     }


     protected override void OnStart(string[] args)
     {
        foreach (string arg in args)
        {
            if (arg == "DEBUG_SERVICE")
                    DebugMode();

        }

         #if DEBUG
             DebugMode();
         #endif

         timer.Interval = 1;
         timer.Start();

        }

    private static void DebugMode()
    {

        Debugger.Break();
    }



 }

編集Start() の固定ループ

EDIT Milliseconds は TotalMilliseconds と同じではないことが判明しました

于 2010-07-16T15:53:31.043 に答える
2

ジョブのスケジュールを管理するには、Quartz.Netを確認することをお勧めします。それがあなたの特定の状況に合うかどうかはわかりませんが、一見の価値があります。

于 2010-07-16T16:18:14.403 に答える
1

あなたの編集に基づいて、私が考えることができるいくつかのこと:

Re: 仕事の失敗:

  • ジョブを再試行できるかどうかを判断し、次のいずれかを実行します。
    • 後でロギング/レポートするために行を「エラー」テーブルに移動する OR
    • ジョブ サービスによって再処理されるように、行をキューに残します。
    • WaitUntil などの列を追加して、失敗後にジョブの再試行を遅らせることができます

Re: 競合:

  • ジョブがいつ開始されたかを追跡するために、「JobStarted」や「Locked」などのタイムスタンプ列を追加します。これにより、他のスレッド (サービスがマルチスレッド化されている場合) がジョブを同時に実行しようとするのを防ぐことができます。
  • 再処理のために古いジョブをクリアしてクリアするクリーンアップ プロセスが必要です (ジョブ サービスが失敗し、ロックが解除されない場合)。

Re: サービスを継続する

  • 失敗した場合にサービスを再起動するように Windows に指示できます。
  • サービスの実行中にある種のファイルを開いたままにし、シャットダウンが成功したときにそれを削除することにより、起動時に以前の失敗を検出できます。サービスが起動し、そのファイルが既に存在する場合は、サービスが以前に失敗したことがわかり、オペレーターに警告したり、必要なクリーンアップ操作を実行したりできます。

私は本当にここで暗闇の中でぶらぶらしているだけです。サービスのプロトタイピングを行い、その機能について具体的な質問があれば返信することを強くお勧めします。

于 2010-07-16T15:45:23.900 に答える