簡體   English   中英

等待RabbitMQ線程在Windows Service OnStop()中完成

[英]Wait for RabbitMQ Threads to finish in Windows Service OnStop()

我正在使用C#(.NET 4.5,VS2012)編寫的Windows服務,該服務使用RabbitMQ(通過訂閱接收消息)。 有一個從DefaultBasicConsumer派生的類,該類中有兩個實際使用者(因此有兩個通道)。 因為有兩個通道,所以兩個線程處理傳入消息(來自兩個不同的隊列/路由鍵),並且都調用相同的HandleBasicDeliver(...)函數。

現在,當調用Windows服務OnStop()時(當某人停止該服務時),我要讓這兩個線程都完成對消息的處理(如果他們當前正在處理消息),將ack發送到服務器,然后停止服務(中止線程等)。

我考慮過多種解決方案,但是似乎都不是一個好方案。 這是我嘗試過的:

  • 使用一個互斥鎖; 每個線程在進入HandleBasicDeliver時都會嘗試使用它,然后再釋放它。 調用OnStop() ,主線程嘗試獲取相同的互斥量,從而有效地阻止RabbitMQ線程實際處理更多消息。 缺點是一次只能有一個使用者線程可以處理一條消息。
  • 使用兩個互斥鎖:每個RabbitMQ線程都有一個不同的互斥鎖,因此它們在HandleBasicDeliver()中不會互相阻塞-我可以根據路由鍵區分哪個線程實際上正在處理當前消息。 就像是:

     HandleBasicDeliver(...) { if(routingKey == firstConsumerRoutingKey) { // Try to grab the mutex of the first consumer } else { // Try to grab the mutex of the second consumer } } 

    調用OnStop() ,主線程將嘗試獲取兩個互斥鎖; 一旦兩個互斥鎖都在主線程“手中”,它就可以繼續停止服務。 問題是:如果將另一個使用者添加到此類中,則需要更改很多代碼。

  • 使用計數器或CountdownEvent 計數器從0開始計數,每次輸入HandleBasicDeliver() ,都可以使用Interlocked類安全地遞增計數器。 處理完消息后,計數器遞減。 調用OnStop() ,主線程將檢查計數器是否為0。如果滿足此條件,它將繼續。 但是,在檢查計數器是否為0之后,某些RabbitMQ線程可能開始處理消息。
  • 調用OnStop() ,關閉與RabbitMQ的連接(以確保沒有新消息到達),然后等待幾秒鍾(以防萬一正在處理任何消息,以完成處理),然后關閉應用程序。 問題是,我不知道在關閉連接之前應該等待的確切秒數,因此這不是一個優雅的解決方案。

我意識到設計不符合“單一責任原則”,這可能會導致缺乏解決方案。 但是,是否有一個很好的解決方案,而不必重新設計項目?

我們在應用程序中執行此操作,主要思想是使用CancellationTokenSource

在Windows服務上添加以下內容:

private static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();

然后在您的兔子消費者中執行以下操作:1.從使用Dequeue更改為DequeueNoWait 2.讓您的兔子消費者檢查取消令牌

這是我們的代碼:

        public async Task StartConsuming(IMessageBusConsumer consumer, MessageBusConsumerName fullConsumerName, CancellationToken cancellationToken)
        {
            var queueName = GetQueueName(consumer.MessageBusConsumerEnum);

            using (var model = _rabbitConnection.CreateModel())
            {
                // Configure the Quality of service for the model. Below is how what each setting means.
                // BasicQos(0="Don't send me a new message until I’ve finished",  _fetchSize = "Send me N messages at a time", false ="Apply to this Model only")
                model.BasicQos(0, consumer.FetchCount.Value, false);
                var queueingConsumer = new QueueingBasicConsumer(model);

                model.BasicConsume(queueName, false, fullConsumerName, queueingConsumer);


                var queueEmpty = new BasicDeliverEventArgs(); //This is what gets returned if nothing in the queue is found.

                while (!cancellationToken.IsCancellationRequested)
                {
                    var deliverEventArgs = queueingConsumer.Queue.DequeueNoWait(queueEmpty);
                    if (deliverEventArgs == queueEmpty)
                    {
                        // This 100ms wait allows the processor to go do other work.
                        // No sense in going back to an empty queue immediately. 
                        // CancellationToken intentionally not used!
                        // ReSharper disable once MethodSupportsCancellation
                        await Task.Delay(100);  
                        continue;
                    }
                    //DO YOUR WORK HERE!
                  }
}

通常,我們如何確保Windows服務在處理完成之前不會停止,是使用以下代碼。 希望對您有所幫助。

    protected override void OnStart(string[] args)
    {
        // start the worker thread
        _workerThread = new Thread(WorkMethod)
        {
            // !!!set to foreground to block windows service be stopped
            // until thread is exited when all pending tasks complete
            IsBackground = false
        };
        _workerThread.Start();
    }

    protected override void OnStop()
    {
        // notify the worker thread to stop accepting new migration requests
        // and exit when all tasks are completed
        // some code to notify worker thread to stop accepting new tasks internally

        // wait for worker thread to stop
        _workerThread.Join();
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM