8

私の Web サイトのユーザーが実行する特定のアクションにより、電子メールが送信されます。メールを送信するコードはしばらくの間ブロックされる可能性があるため、HTTP 要求ハンドラーのスレッドからこれを実行したいと考えています。

現在、私は次のようなものを使用しています:

ThreadPool.QueueUserWorkItem(o => {
    try
    {
        email.Send();
    }
    catch (Exception ex)
    {
        _log.Error("Error sending email", ex);
    }
});

ほとんどの場合、これでうまくいきます。ただし、Web サイトは、アプリケーション プールをリサイクルできるホスト環境で実行されます。

ときどき、送信されるべき電子メールが届かないことがあります。スレッドプールのキューにあるこの作業項目が、アプリケーション プールのリサイクル中にドロップされているのではないかと思います。

このような非同期操作を実行し、そのような場合に完了することを保証するにはどうすればよいですか?

4

2 に答える 2

8

アプリケーションが統合モードで実行されている場合、ホスト環境内でメール ディスパッチャ サービスを登録できます。ホストは、リサイクルが完了する前にサービスに通知します。ホストは実装をIRegisteredObject.Stop正確に 2 回呼び出します。最初の電話で、ホストはあなたに仕事を終わらせる機会を与えます。タイムアウトに達し、サービスがホストから削除されていない場合、別の呼び出しが行われますが、今回はサービスの同意の有無にかかわらずリサイクルが行われることを通知するためだけです。

これは、Stop() メソッドを実装する方法の例 (テストされていません) です。

public class MailDispatchService : IRegisteredObject
{
    private AutoResetEvent _processQueueEvt = new AutoResetEvent();
    private ConcurrentQueue<MailMessage> _queue = new ConcurrentQueue<MailMessage>();
    private Thread _dispatcherThread;
    private volatile bool _enabled = true;

    #region Implementation of IRegisteredObject

    public void Stop(bool immediate)
    {
        if (_dispatcherThread != null && _dispatcherThread.IsAlive)
        {
            // it's not an immediate stop, we can wait for the queue to empty
            if (!immediate)
            {
                // stop accepting new items in the send queue...
                _enabled = false;
                // awake dispatcher thread, so it can quit if the queue is empty
                _processQueueEvt.Set();
                // and wait for a while but not forever.
                _dispatcherThread.Join(TimeSpan.FromSeconds(30));
            }
            else
            {
                // host env will recycle now, nothing to do...
                _dispatcherThread.Abort();
            }
        }
        // remove the service from host
        HostingEnvironment.UnregisterObject(this);
    }

    #endregion

    public void Start()
    {
        _dispatcherThread = new Thread(ProcessQueue);
        _dispatcherThread.Start();
    }

    private void ProcessQueue()
    {
        while (_enabled)
        {
            _processQueueEvt.WaitOne();
            MailMessage message;
            while (_queue.TryDequeue(out message)) { /* send mail ...*/}
        }
    }

    public void DispatchEmail(MailMessage message)
    {
        if (!_enabled) throw new Exception("....");
        _queue.Enqueue(message);
        _processQueueEvt.Set();
    }
}

サービスを開始し、ホストに登録します。

var mailService = new MailDispatchService();
System.Web.Hosting.HostingEnvironment.RegisterObject(mailService);
mailService.Start();

var message = new MailMessage();
mailService.DispatchEmail(message);   
于 2012-08-07T14:14:21.670 に答える
2

ここでの最良のオプションは、永続的なメッセージ キューまたはサービス バスを使用することです。アプリはメール リクエストをキューに書き込み、キュー ハンドラー (Web アプリの場合もあります) がキューを読み取り、メッセージを処理します。何かが死んだ場合、永続化の角度が始まります。メッセージは、処理できるようになるまでぶらぶらします。

于 2012-08-07T14:27:10.220 に答える