in src/Microsoft.ServiceFabric.Services.Remoting/V2/Runtime/ServiceRemotingMessageDispatcher.cs [176:258]
private Task<IServiceRemotingResponseMessage> OnDispatch(
IServiceRemotingRequestMessageHeader requestMessageHeaders,
IServiceRemotingRequestMessageBody requestBody,
CancellationToken cancellationToken)
{
if (!this.methodDispatcherMap.TryGetValue(requestMessageHeaders.InterfaceId, out var methodDispatcher))
{
throw new NotImplementedException(string.Format(
CultureInfo.CurrentCulture,
SR.ErrorInterfaceNotImplemented,
requestMessageHeaders.InterfaceId,
this.serviceImplementation));
}
Task<IServiceRemotingResponseMessageBody> dispatchTask = null;
var stopwatch = Stopwatch.StartNew();
var requestMessage = new ServiceRemotingRequestMessage(requestMessageHeaders, requestBody);
ServiceRemotingServiceEvents.RaiseReceiveRequest(requestMessage, methodDispatcher.GetMethodName(requestMessageHeaders.MethodId));
try
{
dispatchTask = methodDispatcher.DispatchAsync(
this.serviceImplementation,
requestMessageHeaders.MethodId,
requestBody,
this.GetRemotingMessageBodyFactory(),
cancellationToken);
}
catch (Exception e)
{
// Suggestion:
// In future, we should consider consolodating how service remoting handles exceptions (failed requests) and normal responses (successful requests)
// My contention is that a request that fails also generates a response (albeit a special kind) that encapsulates an exception.
// If an IServiceRemotingResponseMessage can encapsulate a response in either case - this allows us to use response headers in either case for communication (think http 4XX / 5XX responses have standard headers)
// The proxy on the caller side can always deserialize the exception and throw it, so user experience won't have to change due to this suggested architectural change.
ServiceRemotingServiceEvents.RaiseExceptionResponse(e, requestMessage);
var info = ExceptionDispatchInfo.Capture(e);
this.servicePerformanceCounterProvider.OnServiceMethodFinish(
requestMessageHeaders.InterfaceId,
requestMessageHeaders.MethodId,
stopwatch.Elapsed,
e);
info.Throw();
}
return dispatchTask.ContinueWith(
t =>
{
object responseBody = null;
try
{
responseBody = t.GetAwaiter().GetResult();
}
catch (Exception e)
{
ServiceRemotingServiceEvents.RaiseExceptionResponse(e, requestMessage);
var info = ExceptionDispatchInfo.Capture(e);
this.servicePerformanceCounterProvider.OnServiceMethodFinish(
requestMessageHeaders.InterfaceId,
requestMessageHeaders.MethodId,
stopwatch.Elapsed,
e);
info.Throw();
}
this.servicePerformanceCounterProvider.OnServiceMethodFinish(
requestMessageHeaders.InterfaceId,
requestMessageHeaders.MethodId,
stopwatch.Elapsed);
// We are creating empty response headers so that ServiceRemotingServiceEvents can add headers if they needed.
// This wont impact serialization cost since we check if its Empty , then dont serialize.
var response = new ServiceRemotingResponseMessage(new ServiceRemotingResponseMessageHeader(), (IServiceRemotingResponseMessageBody)responseBody);
ServiceRemotingServiceEvents.RaiseSendResponse(response, requestMessage);
return (IServiceRemotingResponseMessage)response;
},
TaskContinuationOptions.ExecuteSynchronously);
}