Exemple #1
0
 public void CleanupMessagingResources(string correlationId, ExpectedResponse expectedResponse)
 {
     if (expectedResponse != null)
     {
         expectedResponse.Dispose();
     }
 }
        public ExpectedResponse PrepareForResponse(string correlationId, BasicProperties basicProperties, AmqpModelContainer model, HttpRequestMessage request, TimeSpan requestTimeout, CancellationToken cancellationToken, TaskCompletionSource<HttpResponseMessage> taskSource)
        {
            //Set Reply to queue
            basicProperties.ReplyTo = RPCStrategyHelpers.DIRECT_REPLY_TO_QUEUENAME_ARG;
            var rpcModel = (RPCModelContainer)model;
            var arrival = new ExpectedResponse(rpcModel.ReceivedResponseEvent);
            rpcModel.ExpectResponse(correlationId, arrival);

            RPCStrategyHelpers.WaitForResponse(request, arrival, requestTimeout, model, true, cancellationToken, taskSource, () => CleanupMessagingResources(correlationId, arrival));
            return arrival;
        }
Exemple #3
0
        public ExpectedResponse PrepareForResponse(string correlationId, BasicProperties basicProperties, AmqpModelContainer model, HttpRequestMessage request, TimeSpan requestTimeout, CancellationToken cancellationToken, TaskCompletionSource <HttpResponseMessage> taskSource)
        {
            //Set Reply to queue
            basicProperties.ReplyTo = RPCStrategyHelpers.DIRECT_REPLY_TO_QUEUENAME_ARG;
            var rpcModel = (RPCModelContainer)model;
            var arrival  = new ExpectedResponse(rpcModel.ReceivedResponseEvent);

            rpcModel.ExpectResponse(correlationId, arrival);

            RPCStrategyHelpers.WaitForResponse(request, arrival, requestTimeout, model, true, cancellationToken, taskSource, () => CleanupMessagingResources(correlationId, arrival));
            return(arrival);
        }
Exemple #4
0
        public void CleanupMessagingResources(string correlationId, ExpectedResponse expectedResponse)
        {
            if (!String.IsNullOrEmpty(correlationId))
            {
                ExpectedResponse unused;
                expectedResponses.TryRemove(correlationId, out unused);
            }

            if (expectedResponse != null)
            {
                expectedResponse.Dispose();
            }
        }
        public ExpectedResponse PrepareForResponse(string correlationId, BasicProperties basicProperties, AmqpModelContainer model, HttpRequestMessage request, TimeSpan requestTimeout, CancellationToken cancellationToken, TaskCompletionSource<HttpResponseMessage>  taskSource)
        {
            //Set Reply to queue
            basicProperties.ReplyTo = callbackQueueName;

            //Initialize response arrival object and add to expected responses dictionary
            var arrival = new ExpectedResponse();
            expectedResponses[correlationId] = arrival;

            RPCStrategyHelpers.WaitForResponse(request, arrival, requestTimeout, model, false, cancellationToken, taskSource, () => CleanupMessagingResources(correlationId, arrival));

            return arrival;

        }
Exemple #6
0
        public ExpectedResponse PrepareForResponse(string correlationId, BasicProperties basicProperties, AmqpModelContainer model, HttpRequestMessage request, TimeSpan requestTimeout, CancellationToken cancellationToken, TaskCompletionSource <HttpResponseMessage> taskSource)
        {
            //Set Reply to queue
            basicProperties.ReplyTo = callbackQueueName;

            //Initialize response arrival object and add to expected responses dictionary
            var arrival = new ExpectedResponse();

            expectedResponses[correlationId] = arrival;

            RPCStrategyHelpers.WaitForResponse(request, arrival, requestTimeout, model, false, cancellationToken, taskSource, () => CleanupMessagingResources(correlationId, arrival));

            return(arrival);
        }
        internal static void ReadAndSignalDelivery(ExpectedResponse expected, BasicDeliverEventArgs evt)
        {
            try
            {
                expected.Response = HttpResponsePacket.Deserialize(evt.Body);
            }
            catch (Exception ex)
            {
                expected.DeserializationException = ex;
            }

            //NOTE: The ManualResetEventSlim.Set() method can be called after the object has been disposed
            //So no worries about the Timeout disposing the object before the response comes in.
            expected.ReceivedEvent.Set();
        }
        internal static void WaitForResponse(HttpRequestMessage request, ExpectedResponse arrival, TimeSpan requestTimeout, AmqpModelContainer model, bool closeModel, CancellationToken cancellationToken, TaskCompletionSource <HttpResponseMessage> taskSource, Action cleanup)
        {
            //Spawning a new task to wait on the MRESlim is slower than using ThreadPool.RegisterWaitForSingleObject
            //
            //TODO: Test task vs RegisterWaitForSingleObject modes in a super fast network environment with 40, 100, 200 all the way to 1000 threads to see what method has fastest throughput.

#if WAIT_FOR_RESPONSE_IN_TASK_MODE
            //Wait for response arrival event on a new task

            Task.Factory.StartNew(() =>
            {
                bool succeeded = arrival.ReceivedEvent.Wait(
                    requestTimeout.TotalMilliseconds > Int32.MaxValue ? TimeSpan.FromMilliseconds(Int32.MaxValue) : requestTimeout /* Covers InfiniteTimeSpan */,
                    cancellationToken);

                Thread.MemoryBarrier();         //Ensure non-cached versions of arrival are read

                //It failed either because it timed out or was cancelled
                //HttpClient treats both scenarios the same way.

                try
                {
                    if (closeModel)
                    {
                        model.Close();
                    }
                    SetResponseResult(request, !succeeded, arrival, taskSource);
                }
                catch
                {
                    //TODO: Log this:
                    // the code in the try block should be safe so this catch block should never be called,
                    // hoewever, this delegate is called on a separate thread and should be protected.
                }
                finally
                {
                    CleanupMessagingResources(correlationId, arrival);
                }
            }, cancellationToken);
#else
            //Wait for response arrival event on the ThreadPool:

            var localVariableInitLock = new object();

            lock (localVariableInitLock)
            {
                //TODO: Have cancellationToken signal WaitHandle so that threadpool stops waiting.

                RegisteredWaitHandle callbackHandle = null;
                callbackHandle = ThreadPool.RegisterWaitForSingleObject(arrival.ReceivedEvent.WaitHandle,
                                                                        (state, timedOut) =>
                {
                    //TODO: Investigate, this memorybarrier might be unnecessary since the thread is released from the threadpool
                    //after deserializationException and responsePacket is set.
                    Thread.MemoryBarrier();     //Ensure non-cached versions of arrival are read
                    try
                    {
                        //TODO: Check Cancelation Token when it's implemented

                        if (closeModel)
                        {
                            model.Close();
                        }
                        SetResponseResult(request, timedOut, arrival, taskSource);

                        lock (localVariableInitLock)
                        {
                            callbackHandle.Unregister(null);
                        }
                    }
                    catch
                    {
                        //TODO: Log this:
                        // the code in the try block should be safe so this catch block should never be called,
                        // hoewever, this delegate is called on a separate thread and should be protected.
                    }
                    finally
                    {
                        cleanup();
                    }
                },
                                                                        null,
                                                                        requestTimeout == System.Threading.Timeout.InfiniteTimeSpan ? System.Threading.Timeout.Infinite : (long)requestTimeout.TotalMilliseconds,
                                                                        true);
            }
#endif
        }
        private static void SetResponseResult(HttpRequestMessage request, bool timedOut, ExpectedResponse arrival, TaskCompletionSource <HttpResponseMessage> taskSource)
        {
            if (timedOut)
            {
                //NOTE: This really ought to return an "Operation Timed Out" WebException and not a Cancellation as noted in the following posts
                // http://social.msdn.microsoft.com/Forums/en-US/d8d87789-0ac9-4294-84a0-91c9fa27e353/bug-in-httpclientgetasync-should-throw-webexception-not-taskcanceledexception?forum=netfxnetcom&prof=required
                // http://stackoverflow.com/questions/10547895/how-can-i-tell-when-httpclient-has-timed-out
                // and http://stackoverflow.com/questions/12666922/distinguish-timeout-from-user-cancellation
                //
                // However, for compatibility with the HttpClient, it returns a cancellation
                //

                taskSource.SetCanceled();
            }
            else
            {
                if (arrival.DeserializationException == null)
                {
                    if (arrival.Response == null)
                    {
                        //TODO: Log this -- Critical issue (or just assert)
                    }
                }

                HttpResponseMessage msg;
                if (arrival.DeserializationException == null && TryGetHttpResponseMessage(arrival.Response, out msg))
                {
                    msg.RequestMessage = request;
                    taskSource.SetResult(msg);
                }
                else
                {
                    taskSource.SetException(RestBusClient.GetWrappedException("An error occurred while reading the response.", arrival.DeserializationException));
                }
            }
        }
Exemple #10
0
        internal static void WaitForResponse (HttpRequestMessage request, ExpectedResponse arrival, TimeSpan requestTimeout, AmqpModelContainer model, bool closeModel, CancellationToken cancellationToken, TaskCompletionSource<HttpResponseMessage> taskSource, Action cleanup)
        {
            //Spawning a new task to wait on the MRESlim is slower than using ThreadPool.RegisterWaitForSingleObject
            //
            //TODO: Test task vs RegisterWaitForSingleObject modes in a super fast network environment with 40, 100, 200 all the way to 1000 threads to see what method has fastest throughput.

#if WAIT_FOR_RESPONSE_IN_TASK_MODE

                    //Wait for response arrival event on a new task

                    Task.Factory.StartNew(() =>
                    {
                        bool succeeded = arrival.ReceivedEvent.Wait(
                            requestTimeout.TotalMilliseconds > Int32.MaxValue ? TimeSpan.FromMilliseconds(Int32.MaxValue) : requestTimeout /* Covers InfiniteTimeSpan */,
                            cancellationToken);

                        Thread.MemoryBarrier(); //Ensure non-cached versions of arrival are read

                        //It failed either because it timed out or was cancelled
                        //HttpClient treats both scenarios the same way.

                        try
                        {
                            if (closeModel) model.Close();
                            SetResponseResult(request, !succeeded, arrival, taskSource);

                        }
                        catch
                        {
                            //TODO: Log this: 
                            // the code in the try block should be safe so this catch block should never be called, 
                            // hoewever, this delegate is called on a separate thread and should be protected.
                        }
                        finally
                        {
                            CleanupMessagingResources(correlationId, arrival);
                        }

                    }, cancellationToken);

#else

            //Wait for response arrival event on the ThreadPool:

            var localVariableInitLock = new object();

            lock (localVariableInitLock)
            {
                //TODO: Have cancellationToken signal WaitHandle so that threadpool stops waiting.

                RegisteredWaitHandle callbackHandle = null;
                callbackHandle = ThreadPool.RegisterWaitForSingleObject(arrival.ReceivedEvent.WaitHandle,
                    (state, timedOut) =>
                    {
                        //TODO: Investigate, this memorybarrier might be unnecessary since the thread is released from the threadpool
                        //after deserializationException and responsePacket is set.
                        Thread.MemoryBarrier(); //Ensure non-cached versions of arrival are read
                        try
                        {
                            //TODO: Check Cancelation Token when it's implemented

                            if (closeModel) model.Close();
                            SetResponseResult(request, timedOut, arrival, taskSource);

                            lock (localVariableInitLock)
                            {
                                callbackHandle.Unregister(null);
                            }

                        }
                        catch
                        {
                            //TODO: Log this: 
                            // the code in the try block should be safe so this catch block should never be called, 
                            // hoewever, this delegate is called on a separate thread and should be protected.
                        }
                        finally
                        {
                            cleanup();
                        }
                    },
                        null,
                        requestTimeout == System.Threading.Timeout.InfiniteTimeSpan ? System.Threading.Timeout.Infinite : (long)requestTimeout.TotalMilliseconds,
                        true);

            }
#endif
        }
Exemple #11
0
 internal void Reset()
 {
     _correlationId = null;
     _expected = null;
     _receivedResponse.Reset();
 }
Exemple #12
0
        internal static void ReadAndSignalDelivery (ExpectedResponse expected, BasicDeliverEventArgs evt)
        {
            try
            {
                expected.Response = HttpResponsePacket.Deserialize(evt.Body);
            }
            catch (Exception ex)
            {
                expected.DeserializationException = ex;
            }

            //NOTE: The ManualResetEventSlim.Set() method can be called after the object has been disposed
            //So no worries about the Timeout disposing the object before the response comes in.
            expected.ReceivedEvent.Set();
        }
        public void CleanupMessagingResources(string correlationId, ExpectedResponse expectedResponse)
        {
            if (!String.IsNullOrEmpty(correlationId))
            {
                ExpectedResponse unused;
                expectedResponses.TryRemove(correlationId, out unused);
            }

            if (expectedResponse != null)
            {
                expectedResponse.Dispose();
            }
        }
Exemple #14
0
        /// <summary>Send an HTTP request as an asynchronous operation.</summary>
        /// <returns>Returns <see cref="T:System.Threading.Tasks.Task`1" />.The task object representing the asynchronous operation.</returns>
        /// <param name="request">The HTTP request message to send.</param>
        /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="request" /> was null.</exception>
        public override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            if (request.RequestUri == null && BaseAddress == null)
            {
                throw new InvalidOperationException("The request URI must either be set or BaseAddress must be set");
            }

            if (disposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
            hasKickStarted = true;
            PrepareMessage(request);

            //Get Request Options
            RequestOptions requestOptions    = GetRequestOptions(request);
            var            messageProperties = GetMessagingProperties(requestOptions);

            //Determine if message expects a response
            TimeSpan requestTimeout = GetRequestTimeout(requestOptions);
            //TODO: expectingResponse has a slightly different meaning in publisher confirms
            //where timespan may be longer than zero but MessageExpectsReply is false
            //in which case the timeout only applies to how long to wait for the publisher confirmation.
            bool expectingResponse = requestTimeout != TimeSpan.Zero && GetExpectsReply(request);

            //Declare messaging resources
            ExpectedResponse   arrival = null;
            AmqpModelContainer model   = null;
            bool   modelClosed         = false;
            string correlationId       = null;

            //Get channel pool and decide on RPC strategy
            var          pool        = connectionMgr.GetConnectedPool();
            IRPCStrategy rpcStrategy = pool.IsDirectReplyToCapable && !Settings.DisableDirectReplies ? directStrategy : callbackStrategy;

            try
            {
                #region Ensure CallbackQueue is started (If Using CallbackQueue Strategy)

                rpcStrategy.StartStrategy(pool, expectingResponse);

                #endregion

                #region Populate BasicProperties
                //Fill BasicProperties

                BasicProperties basicProperties = new BasicProperties();

                //Set message delivery mode -- Make message persistent if either:
                // 1. Properties.Persistent is true
                // 2. messagingConfig.PersistentMessages is true and Properties.Persistent is null
                // 3. messagingConfig.PersistentMessages is true and Properties.Persistent is true
                if (messageProperties.Persistent == true || (messagingConfig.PersistentMessages && messageProperties.Persistent != false))
                {
                    basicProperties.Persistent = true;
                }

                //Set Exchange Headers
                var exchangeHeaders = messageProperties.Headers ?? messageMapper.GetHeaders(request);
                if (exchangeHeaders != null)
                {
                    basicProperties.Headers = exchangeHeaders;
                }

                if (expectingResponse)
                {
                    //Set CorrelationId
                    correlationId = correlationIdGen.GetNextId();
                    basicProperties.CorrelationId = correlationId;

                    //Set Expiration if messageProperties doesn't override Client.Timeout, RequestOptions and MessageMapper.
                    if (!messageProperties.Expiration.HasValue && requestTimeout != System.Threading.Timeout.InfiniteTimeSpan &&
                        (messagingConfig.MessageExpires == null || messagingConfig.MessageExpires(request)))
                    {
                        if (requestTimeout.TotalMilliseconds > Int32.MaxValue)
                        {
                            basicProperties.Expiration = Int32.MaxValue.ToString();
                        }
                        else
                        {
                            basicProperties.Expiration = ((int)requestTimeout.TotalMilliseconds).ToString();
                        }
                    }
                }
                else if (!messageProperties.Expiration.HasValue && (messagingConfig.MessageExpires == null || messagingConfig.MessageExpires(request)))
                {
                    //Request has a zero timeout and the message mapper indicates it should expire and messageproperties expiration is not set:
                    //Set the expiration to zero which means RabbitMQ will only transmit if there is a consumer ready to receive it.
                    //If there is no ready consumer, RabbitMQ drops the message. See https://www.rabbitmq.com/ttl.html

                    basicProperties.Expiration = "0";
                }

                //Set expiration if set in message properties
                if (messageProperties.Expiration.HasValue)
                {
                    if (messageProperties.Expiration != System.Threading.Timeout.InfiniteTimeSpan)
                    {
                        var expiration = messageProperties.Expiration.Value.Duration();
                        if (expiration.TotalMilliseconds > Int32.MaxValue)
                        {
                            basicProperties.Expiration = Int32.MaxValue.ToString();
                        }
                        else
                        {
                            basicProperties.Expiration = ((int)expiration.TotalMilliseconds).ToString();
                        }
                    }
                    else
                    {
                        //Infinite Timespan indicates that message should never expire
                        basicProperties.ClearExpiration();
                    }
                }

                #endregion

                #region Get Ready to Send Message

                model = rpcStrategy.GetModel(pool, false);

                var serviceName =
                    (requestOptions == null || requestOptions.ServiceName == null)
                    ? (messageMapper.GetServiceName(request) ?? String.Empty).Trim()
                    : requestOptions.ServiceName.Trim();

                RedeclareExchangesAndQueues(model, serviceName);

                //TODO: Check if cancellation token was set before operation even began
                var taskSource = new TaskCompletionSource <HttpResponseMessage>();

                var exchangeKind = ExchangeKind.Direct;
                //TODO: Get ExchangeKind from CLient.Settings.ExchangeKind
                //TODO: Pull exchangeName from a concurrent dictionary that has a key of serviceName, exchangeKind
                //exchangeKind could be an index into arrays that have concurrentDictionaries.
                var exchangeName = AmqpUtils.GetExchangeName(messagingConfig, serviceName, exchangeKind);

                #endregion

                #region Start waiting for response
                //Start waiting for response if a request timeout is set.
                if (expectingResponse)
                {
                    //TODO: Better to just check if cancellationHasbeen requested instead of checking if it's None
                    if (!cancellationToken.Equals(System.Threading.CancellationToken.None))
                    {
                        //TODO: Have cancellationtokens cancel event trigger callbackHandle
                        //In fact turn this whole thing into an extension
                    }

                    arrival = rpcStrategy.PrepareForResponse(correlationId, basicProperties, model, request, requestTimeout, cancellationToken, taskSource);
                }

                #endregion

                #region Send Message
                //TODO: Implement routing to a different exchangeKind via substituting exchangeName
                //Send message
                model.Channel.BasicPublish(exchangeName,
                                           messageProperties.RoutingKey ?? messageMapper.GetRoutingKey(request, exchangeKind) ?? AmqpUtils.GetWorkQueueRoutingKey(),
                                           basicProperties,
                                           request.ToHttpRequestPacket().Serialize());

                //Close channel
                if (!expectingResponse || rpcStrategy.ReturnModelAfterSending)
                {
                    CloseAmqpModel(model);
                    modelClosed = true;
                }

                #endregion

                #region Cleanup if not expecting response
                //Exit with OK result if no request timeout was set.
                if (!expectingResponse)
                {
                    //TODO: Investigate adding a publisher confirm for zero timeout messages so we know that RabbitMQ did pick up the message before replying OK.
                    //Might add extra complexity to this class.

                    //Zero timespan means the client isn't interested in a response
                    taskSource.SetResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)
                    {
                        Content = _emptyByteArrayContent
                    });

                    rpcStrategy.CleanupMessagingResources(correlationId, arrival);
                }

                #endregion

                return(taskSource.Task);
            }
            catch (Exception ex)
            {
                //TODO: Log this

                if (model != null && !modelClosed)
                {
                    if (expectingResponse && model.Flags == ChannelFlags.RPC || model.Flags == ChannelFlags.RPCWithPublisherConfirms)
                    {
                        //Model might still be in use in waiting thread and so unsafe to be recycled
                        model.Discard = true;
                    }

                    CloseAmqpModel(model);
                }

                rpcStrategy.CleanupMessagingResources(correlationId, arrival);

                if (ex is HttpRequestException)
                {
                    throw;
                }
                else
                {
                    throw GetWrappedException("An error occurred while sending the request.", ex);
                }
            }
        }
 public void CleanupMessagingResources(string correlationId, ExpectedResponse expectedResponse)
 {
     if (expectedResponse != null) expectedResponse.Dispose();
 }
Exemple #16
0
        private static void SetResponseResult(HttpRequestMessage request, bool timedOut, ExpectedResponse arrival, TaskCompletionSource<HttpResponseMessage> taskSource)
        {
            if (timedOut)
            {
                //NOTE: This really ought to return an "Operation Timed Out" WebException and not a Cancellation as noted in the following posts
                // http://social.msdn.microsoft.com/Forums/en-US/d8d87789-0ac9-4294-84a0-91c9fa27e353/bug-in-httpclientgetasync-should-throw-webexception-not-taskcanceledexception?forum=netfxnetcom&prof=required
                // http://stackoverflow.com/questions/10547895/how-can-i-tell-when-httpclient-has-timed-out
                // and http://stackoverflow.com/questions/12666922/distinguish-timeout-from-user-cancellation
                //
                // However, for compatibility with the HttpClient, it returns a cancellation
                //

                taskSource.SetCanceled();
            }
            else
            {
                if (arrival.DeserializationException == null)
                {
                    if (arrival.Response == null)
                    {
                        //TODO: Log this -- Critical issue (or just assert)
                    }
                }

                HttpResponseMessage msg;
                if (arrival.DeserializationException == null && TryGetHttpResponseMessage(arrival.Response, out msg))
                {
                    msg.RequestMessage = request;
                    taskSource.SetResult(msg);
                }
                else
                {
                    taskSource.SetException(RestBusClient.GetWrappedException("An error occurred while reading the response.", arrival.DeserializationException));
                }
            }
        }
Exemple #17
0
 internal void ExpectResponse(string correlationId, ExpectedResponse expected)
 {
     _correlationId = correlationId;
     _expected = expected;
 }