2017-02-16 63 views
0

我想爲RabbitMQ C#客戶端的BasicPublish方法編寫一個超時函數。出於很多原因,有時隊列被阻塞,或者兔子掉下來或者其他什麼。但是我想知道發佈何時失敗。我不想因任何原因阻止該網站。如何寫短暫和重複動作的超時?

我很擔心在任務或線程中添加了一個超時,爲簡單的發佈添加開銷,我們在生產中做了數百萬次。

有沒有人有想法如何寫一個快速阻塞方法的快速超時爲BasicPublish?

說明:另外我在.Net 4中工作,我沒有異步。

+0

您可以在.NET4中使用'async' /'await'。0使用[Microsoft.BCL.Async](https://www.nuget.org/packages/Microsoft.Bcl.Async/)包。 –

回答

1

波利有TimeoutPolicy瞄準的正是這種情況。

波莉的TimeoutStrategy.Optimistic接近@ ThiagoCustodio的答案,但它也正確地配置CancellationTokenSource。然而,RabbitMQ的C#客戶端不會(在撰寫本文時)提供BasicPublish()過載,因此需要使用CancellationToken,因此此方法不相關。

波莉TimeoutStrategy.Pessimistic旨在場景,如BasicPublish(),要強加給委託其CancellationToken支持超時。

波莉TimeoutStrategy.Pessimistic

[1]允許調用線程超時上(從行走等待外)的執行,即使執行委託不支持取消。

[2]以一個額外的任務/線程爲代價(在同步執行中),併爲您進行管理。

[3]也captures the timed-out Task(你已經離開的任務)。這對於日誌記錄是有價值的,並且是必需的以避免UnobservedTaskExceptions - 特別是在.NET4.0中,其中UnobservedTaskException can bring down your entire process

簡單的例子:

Policy.Timeout(TimeSpan.FromSeconds(10), TimeoutStrategy.Pessimistic).Execute(() => BasicPublish(...)); 

完整的示例適當避免UnobservedTaskException S:

Policy timeoutPolicy = Policy.Timeout(TimeSpan.FromSeconds(10), TimeoutStrategy.Pessimistic, (context, timespan, task) => 
{ 
    task.ContinueWith(t => { // ContinueWith important!: the abandoned task may very well still be executing, when the caller times out on waiting for it! 
     if (t.IsFaulted) 
     { 
      logger.Error($"{context.PolicyKey} at {context.ExecutionKey}: execution timed out after {timespan.TotalSeconds} seconds, eventually terminated with: {t.Exception}."); 
     } 
     else 
     { 
      // extra logic (if desired) for tasks which complete, despite the caller having 'walked away' earlier due to timeout. 
     } 
    }); 
}); 

timeoutPolicy.Execute(() => BasicPublish(...)); 

爲了避免在RabbitMQ的不可用的情況下建立太多的併發未決任務/線程,您可以使用Bulkhead Isolation policy來限制並行和/或CircuitBreaker,以防止撥打電話一段時間,你會發現一定程度的故障。這些可以與使用PolicyWrap的TimeoutPolicy相結合。

0

我會說最簡單的方法是使用任務/取消令牌。你認爲這是一個開銷?

public static async Task WithTimeoutAfterStart(
    Func<CancellationToken, Task> operation, TimeSpan timeout) 
{ 
    var source = new CancellationTokenSource(); 
    var task = operation(source.Token); 
    source.CancelAfter(timeout); 
    await task; 
} 

用法:

await WithTimeoutAfterStart(
    ct => SomeOperationAsync(ct), TimeSpan.FromMilliseconds(n)); 
+1

你也可以使用Polly包裝你的代碼:https://github.com/App-vNext/Polly#retry和https://github.com/App-vNext/Polly#retry –

+0

我沒有關於if任務真的創建一個線程或不創建一個線程,當它創建一個新的線程。但是,我認爲爲每個發佈創建一個線程是一個發佈操作的開銷,當一切工作正常時。圖書館Polly也有,我會調查它 – elranu

+0

很酷。最後它只是一個封裝的重試模式。如果它們都不符合您的需求,則可以隨時編寫自己的:https://msdn.microsoft.com/en-us/library/dn589788.aspx和http://stackoverflow.com/questions/1563191/cleanest-way寫入重試邏輯 –