[英]Wait for RabbitMQ Threads to finish in Windows Service OnStop()
我正在使用C#(.NET 4.5,VS2012)編寫的Windows服務,該服務使用RabbitMQ(通過訂閱接收消息)。 有一個從DefaultBasicConsumer
派生的類,該類中有兩個實際使用者(因此有兩個通道)。 因為有兩個通道,所以兩個線程處理傳入消息(來自兩個不同的隊列/路由鍵),並且都調用相同的HandleBasicDeliver(...)
函數。
現在,當調用Windows服務OnStop()
時(當某人停止該服務時),我要讓這兩個線程都完成對消息的處理(如果他們當前正在處理消息),將ack發送到服務器,然后停止服務(中止線程等)。
我考慮過多種解決方案,但是似乎都不是一個好方案。 這是我嘗試過的:
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.