2017-10-02 2 views
2

Ich habe einen Windows-Dienst (mit Topshelf), der auf der Third-Party-Bibliothek EasyRedisMQ beruht. Leider habe ich gerade entdeckt, den folgenden Code in einen der Bibliothek Methoden:Wie wird die Ausnahme abgefangen, die von einer Aufgabe ausgelöst wird, die in einer Bibliothek eines Drittanbieters nicht erwartet wird?

public async Task InitializeAsync() 
    { 
     if (SubscriberInfo == null) throw new NullReferenceException("SubscriberInfo is required."); 
     if (string.IsNullOrWhiteSpace(SubscriberInfo.SubscriberId)) throw new NullReferenceException("SubscriberId is required"); 
     if (string.IsNullOrWhiteSpace(SubscriberInfo.ExchangeName)) throw new NullReferenceException("ExchangeName is required"); 
     if (string.IsNullOrWhiteSpace(SubscriberInfo.QueueName)) throw new NullReferenceException("QueueName is required"); 
     if (OnMessageAsync == null) throw new NullReferenceException("OnMessageAsync is required"); 

     await _cacheClient.SubscribeAsync<string>(SubscriberInfo.ExchangeName, DoWorkAsync); 

     DoWorkAsync("").FireAndForget(); 
    } 

Hier DoWorkAsync gibt eine Aufgabe, aber als fire-and-forget zeigt, ist dies leider nicht erwartet. Tatsächlich ist FireAndForget eine Methode mit einem leeren Körper (der einzige Zweck ist es explizit zu machen, dass die Aufgabe nicht erwartet wird). Siehe den vollständigen Quellcode here.

Das Problem ist, dass eine Ausnahme gelegentlich in DoWorkAsync geworfen wird, die den Dienst zu Absturz verursacht:

{ 
    "Depth": 0, 
    "ClassName": "StackExchange.Redis.RedisConnectionException", 
    "Message": "SocketFailure on RPOP", 
    "Source": "mscorlib", 
    "StackTraceString": " at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at StackExchange.Redis.Extensions.Core.StackExchangeRedisCacheClient.<ListGetFromRightAsync>d__67`1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at EasyRedisMQ.Models.Subscriber`1.<GetNextMessageAsync>d__12.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at EasyRedisMQ.Models.Subscriber`1.<DoWorkAsync>d__13.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at StackExchange.Redis.Extensions.Core.StackExchangeRedisCacheClient.<>c__DisplayClass59_0`1.<<SubscribeAsync>b__0>d.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__6_1(Object state)\r\n at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)\r\n at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)\r\n at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)\r\n at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()\r\n at System.Threading.ThreadPoolWorkQueue.Dispatch()\r\n at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()", 
... 
} 

ich sehr viel möchte die Ausnahme fangen (wie es eigentlich harmlos und einfach wiederholen Sie den Vorgang) . Nach dem Lesen this StackOverflow question und Experimentieren mit AppDomain.CurrentDomain.FirstChanceException habe ich keine großen Hoffnungen. Das Problem mit FirstChanceException ist, dass es außerhalb Tophelf des

HostFactory.Run(hostConfigurator => { ... } 

Verfahren, bei dem tritt ich den Verweis auf den Dienst haben (wo die Business-Logik befindet):

hostConfigurator.Service<IConsumer>(serviceConfigurator => 
{ 
    serviceConfigurator.ConstructUsing(() => IocContainer.IocContainer.Instance.Resolve<IConsumer>()); 
    serviceConfigurator.WhenStarted(consumer => { /* Here I have control */ }); 
    ... 
} 

Hat jemand irgendwelche Ideen, wie um mit dieser Situation umzugehen?

+2

an diesem Code der Suche, etwas namens mit 'FireAndForget' sollte nie werfen. Das könnte also ein potenzieller Fehler sein. Vielleicht sollten Sie ein Problem einreichen. – Iqon

+0

Leider wurde die Client-Bibliothek seit 2 Jahren nicht aktualisiert, daher bin ich nicht sicher, ob das Einreichen eines Fehlers helfen wird. Aber ich stimme vollkommen zu, dass dies idealerweise in der Bibliothek selbst behandelt werden sollte. – SabrinaMH

+2

Ich habe keine Erfahrung mit 'EasyRedisMQ', aber in der von Ihnen angegebenen Quelle gibt es einen Aufruf an' PushMessageToSubscriberAsync', also könnte es eine Möglichkeit geben, diese Nachricht zu abonnieren. Es sollte versendet werden, auch wenn es eine Ausnahme gab, die es verarbeitet. – Iqon

Antwort

1

-event von Taskscheduler abonnieren:

TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
//... 

private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
{ 
    e.SetObserved(); 
} 
+0

Danke für den Vorschlag. Ich habe auch nach TaskScheduler.UnobservedTaskException gesucht, aber das Problem ist, dass ich Code hinzufügen muss, um den Vorgang zu wiederholen, und in TaskScheduler_UnobservedTaskException kann ich keine Methode für das Consumer-Objekt (in serviceConfiguration.WhenStarted (consumer = > {...}); – SabrinaMH

Verwandte Themen