0

すでにいくつかのアイデアがありますが、可能であれば、異なる意見や代替案を皆さんから聞きたいです。

Exchange Web サービスを使用して Exchange に接続し、電子メール メッセージをダウンロードする Windows コンソール アプリを使用しています。目標は、個々のメッセージ オブジェクトの取得、メタデータの抽出、添付ファイルの解析などです。アプリは 60 秒ごとに受信トレイをチェックしています。受信トレイへの接続とメッセージ オブジェクトの取得に問題はありません。これはすべて良いです。

ここで皆さんからの入力を受け付けます。メッセージ オブジェクトを取得したら、すぐにメッセージを処理し、上記で説明した忙しい作業をすべて実行したいと思います。私はこれに対していくつかの異なるアプローチを検討していました:

  • 電子メール オブジェクトをテーブルのキューに入れ、1 つずつ処理します。
  • 電子メール オブジェクトをローカルの Windows サービスに渡して忙しい作業を行う。

複数の電子メール オブジェクトを処理する必要がある場合があるため、db キューイングは適切なアプローチではないと思います。5 個の添付ファイルを含む優先度の高い電子メールが処理される前に、30 個の添付ファイルを含む優先度の低い電子メールが処理されるのは公平ではありません。言い換えれば、スタックの下位にある電子メールは、処理のために列に並んで待つ必要はありません。それは、目の前のボーンヘッドが 100 個のアイテムをスキャンするために、1 つのレジで店で並んで待っているようなものです。それは公正ではありません。私の電子メール オブジェクトについても同じ概念です。

Windows サービスのアプローチについては、やや確信が持てません。しかし、私はインストール済みのサービスをリッスンして、オンデマンドで新しい電子メールを処理する命令を待つことができると確信しています。5 つの個別の電子メール オブジェクトがある場合、Windows サービスに対して 5 つの個別の呼び出しを行い、競合することなく処理できますか?

私は提案や代替アプローチを受け入れます。ただし、ソリューションは .NET テクノロジ スタックを使用して提示する必要があります。

4

2 に答える 2

1

1 つのオプションは、コンソール アプリケーションで処理を行うことです。あなたが持っているものは、1 つのプロデューサー (電子メールを取得するスレッド) と複数のコンシューマーによる標準的なプロデューサーとコンシューマーの問題のように見えます。これはBlockingCollectionで簡単に処理できます。

メッセージの種類 (メール サーバーから取得するもの) はMailMessage.

したがってBlockingCollection<MailMessage>、クラススコープで作成します。また、メッセージを収集してキューに入れるために 60 秒ごとに作動するタイマーがあると仮定します。

private BlockingCollection<MailMessage> MailMessageQueue =
    new BlockingCollection<MailMessage>();

// Timer is created as a one-shot and re-initialized at each tick.
// This prevents the timer proc from being re-entered if it takes
// longer than 60 seconds to run.
System.Threading.Timer ProducerTimer = new System.Threading.Timer(
    TimerProc, null, TimeSpan.FromSeconds(60), TimeSpan.FromMilliseconds(-1));


void TimerProc(object state)
{
    var newMessages = GetMessagesFromServer();
    foreach (var msg in newMessages)
    {
        MailMessageQueue.Add(msg);
    }
    ProducerTimer.Change(TimeSpan.FromSeconds(60), TimeSpan.FromMilliseconds(-1));
}

コンシューマー スレッドはキューを読み取るだけです。

void MessageProcessor()
{
    foreach (var msg in MailMessageQueue.GetConsumingEnumerable())
    {
        ProcessMessage();
    }
}

タイマーにより、プロデューサーは 1 分に 1 回実行されます。コンシューマーを開始するには (2 つ欲しいとします):

var t1 = Task.Factory.StartNew(MessageProcessor, TaskCreationOptions.LongRunning);
var t2 = Task.Factory.StartNew(MessageProcessor, TaskCreationOptions.LongRunning);

したがって、2 つのスレッドでメッセージを処理することになります。

使用可能な CPU コアより多くの処理スレッドを使用しても意味がありません。プロデューサー スレッドはおそらく多くの CPU リソースを必要としないため、専用のスレッドを作成する必要はありません。処理を行っているときはいつでも、メッセージの処理が一時的に遅くなるだけです。

上記の説明の詳細、特にスレッドのキャンセルについては省略しました。プログラムを停止したいが、コンシューマにメッセージの処理を終了させたい場合は、プロデューサ タイマーを強制終了し、追加のためにキューを完了として設定します。

MailMessageQueue.CompleteAdding();

コンシューマーはキューを空にして終了します。もちろん、タスクが完了するまで待ちます (「 」を参照Task.Wait)。

キューを空にすることなくコンシューマーを強制終了する機能が必要な場合は、 Cancellationを調べる必要があります。

のデフォルトのバッキング ストアBlockingCollectionConcurrentQueue、厳密な FIFO です。物事に優先順位を付けたい場合は、IProducerConsumerCollectionインターフェイスを実装する同時優先キューを考え出す必要があります。.NET にはそのようなもの (または優先度キュー クラス) はありませんが、同時アクセスを防ぐためにロックを使用する単純なバイナリ ヒープで十分です。あなたはこれを非常に強く打つことについて話しているのではありません。

もちろん、メッセージに優先順位を付ける何らかの方法が必要です。おそらく、添付ファイルのないメッセージがより迅速に処理されるように、添付ファイルの数で並べ替えます。もう 1 つのオプションは、2 つの別個のキューを用意することです。1 つは添付ファイルが 0 または 1 個のメッセージ用で、もう 1 つは多数の添付ファイルがあるメッセージ用です。簡単なメッセージが常に最初に処理される可能性が高くなるように、コンシューマーの 1 つを 0 または 1 キュー専用にすることができます。他のコンシューマーは、空でない限り 0 または 1 キューから受け取り、次に他のキューから受け取ります。 . 消費者はもう少し複雑になりますが、それほど複雑ではありません。

メッセージ処理を別のプログラムに移動することを選択した場合は、プロデューサーからコンシューマーにデータを永続化する何らかの方法が必要になります。それを行うには多くの可能な方法がありますが、私はそれの利点がわかりません。

于 2013-11-06T21:37:28.930 に答える
0

私はここではやや初心者ですが、最初のアプローチは別の優先度の高いキューを持つことのようです。ワーカーが新しいメッセージを取得できるようになるたびに、次のようなことができます。

If DateTime.Now - lowPriorityQueue.Peek.AddedTime < maxWaitTime Then
    ProcessMessage(lowPriorityQueue.Dequeue())
Else If highPriorityQueue.Count > 0 Then
    ProcessMessage(highPriorityQueue.Dequeue())
Else
    ProcessMessage(lowPriorityQueue.Dequeue())
End If

シングル スレッドでは、1 つのメッセージが他のメッセージをブロックしている可能性がありますが、優先度の高いメッセージはより早く処理される可能性があります。

ほとんどのメッセージが処理される速度に応じて、キューが大きくなりすぎたり古すぎたりすると、アプリケーションは新しいスレッドで新しいワーカーを作成する可能性があります。

私がここで完全にオフベースであるかどうか教えてください。

于 2013-11-06T20:08:53.570 に答える