private void WireUpConsumer(IRawConsumer consumer, IConsumerConfiguration cfg) { consumer.OnMessageAsync = (o, args) => { ResponseCompletionSource responseTcs = null; return(_errorStrategy.ExecuteAsync(() => { if (_responseDictionary.TryRemove(args.BasicProperties.CorrelationId, out responseTcs)) { _logger.LogDebug($"Recived response with correlationId {args.BasicProperties.CorrelationId}."); responseTcs.RequestTimer.Dispose(); _errorStrategy.OnResponseRecievedAsync(args, responseTcs); if (responseTcs.Task.IsFaulted) { return _completed; } var response = _serializer.Deserialize(args); responseTcs.TrySetResult(response); return _completed; } _logger.LogWarning($"Unable to find callback for {args.BasicProperties.CorrelationId}."); return _completed; }, exception => _errorStrategy.OnResponseRecievedException(consumer, cfg, args, responseTcs, exception))); }; }
private Task <TResponse> SendRequestAsync <TRequest, TResponse>(TRequest message, Guid globalMessageId, RequestConfiguration cfg, IRawConsumer consumer) { var correlationId = Guid.NewGuid().ToString(); var responseSource = new ResponseCompletionSource { RequestTimer = new Timer(state => { ResponseCompletionSource rcs; if (!_responseDictionary.TryRemove(correlationId, out rcs)) { _logger.LogWarning($"Unable to find request timer for {correlationId}."); return; } rcs.RequestTimer?.Dispose(); rcs.TrySetException( new TimeoutException($"The request '{correlationId}' timed out after {_config.RequestTimeout.ToString("g")}.")); }, null, _config.RequestTimeout, new TimeSpan(-1)) }; _responseDictionary.TryAdd(correlationId, responseSource); consumer.Model.BasicPublish( exchange: cfg.Exchange.ExchangeName, routingKey: _config.RouteWithGlobalId ? $"{cfg.RoutingKey}.{globalMessageId}" : cfg.RoutingKey, basicProperties: _propertiesProvider.GetProperties <TResponse>(p => { p.ReplyTo = cfg.ReplyQueue.QueueName; p.CorrelationId = correlationId; p.Expiration = _config.RequestTimeout.TotalMilliseconds.ToString(); p.Headers.Add(PropertyHeaders.Context, _contextProvider.GetMessageContext(globalMessageId)); }), body: _serializer.Serialize(message) ); return(responseSource.Task.ContinueWith(tResponse => { if (tResponse.IsFaulted) { throw tResponse.Exception?.InnerException ?? new Exception("Failed to recieve response"); } return (TResponse)tResponse.Result; })); }
internal Task <TResponse> SendReceiveFrameAsync <TRequest, TResponse>( RpcFrameType frameType, string operation, RpcRequestContext?context, TRequest request, LightweightSerializers <TRequest, TResponse> serializers, int timeout) where TRequest : class { if (TraceEnabled) { // TODO: Logger.Trace("Begin SendReceiveFrameAsync {Operation}.", operation); } var tcs = new ResponseCompletionSource <TResponse>(serializers.Serializer, serializers.ResponseSerializer); int messageId = this.AddAwaitingResponse(tcs); try { RpcOperationFlags flags = 0; var cancellationToken = context != null ? context.CancellationToken : default; if (cancellationToken.CanBeCanceled) { cancellationToken.Register(() => this.CancelCall(messageId, operation)); flags |= RpcOperationFlags.CanCancel; } var frame = new LightweightRpcFrame(frameType, messageId, operation, flags, (uint)timeout, context?.Headers); var writeState = this.BeginWrite(frame); var writeTask = this.WriteRequestAsync(request, serializers.RequestSerializer, writeState); if (writeTask.IsCompletedSuccessfully) { if (timeout == 0 || RpcProxyOptions.RoundTripCancellationsAndTimeouts) { return(tcs.Task); } else { return(AwaitTimeoutResponse(tcs.Task, operation, timeout)); } } else { return(AwaitWrite(writeTask, tcs.Task)); } } catch (Exception ex) { this.HandleCallError(messageId, ex); throw; } async Task <TResponse> AwaitTimeoutResponse(Task <TResponse> responseTask, string operation, int timeout) { var completedTask = await Task.WhenAny(responseTask, Task.Delay(timeout)).ContextFree(); if (completedTask == responseTask) { return(responseTask.AwaiterResult()); } else { throw new TimeoutException($"Operation '{operation}' didn't complete within the timeout ({timeout} ms)."); } } async Task <TResponse> AwaitWrite(ValueTask writeTask, Task <TResponse> responseTask) { await writeTask.ContextFree(); if (timeout == 0 || RpcProxyOptions.RoundTripCancellationsAndTimeouts) { return(await responseTask.ContextFree()); } else { return(await AwaitTimeoutResponse(responseTask, operation, timeout).ContextFree()); } } }