internal void ReturnModel(AmqpModelContainer modelContainer) { // NOTE: do not call AmqpModelContainer.Close() here. // That method calls this method. #if !DISABLE_CHANNELPOOLING //Do not return channel to pool if either: //1. Pooler is disposed //2. Channel is consumer related //3. Channel has expired if (_disposed || modelContainer.Discard || HasModelExpired(Environment.TickCount, modelContainer)) { DestroyModel(modelContainer); return; } //Insert model in pool ConcurrentQueue <AmqpModelContainer> queue = _pool[(int)modelContainer.Flags]; queue.Enqueue(modelContainer); //It's possible for the disposed flag to be set (and the pool flushed) right after the first _disposed check //but before the modelContainer was added, so check again. if (_disposed) { Flush(); } #else DisposeModel(modelContainer); #endif }
private static void DisposeModel(AmqpModelContainer modelContainer) { if (modelContainer != null && modelContainer.Channel != null) { try { modelContainer.Channel.Dispose(); } catch { } } }
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; }
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 AmqpModelContainer GetModel(ChannelFlags flags) { #if ENABLE_CHANNELPOOLING //Search pool for a model: AmqpModelContainer model = null; //First get the queue ConcurrentQueue<AmqpModelContainer> queue; if (_pool.TryGetValue(flags, out queue)) { bool retry; int tick = Environment.TickCount; //Dequeue queue until an unexpired model is found do { retry = false; model = null; if (queue.TryDequeue(out model)) { if (HasModelExpired(tick, model)) { DisposeModel(model); // dispose model retry = true; } } } while (retry ); } if (model == null) { //Wasn't found, so create a new one model = new AmqpModelContainer(conn.CreateModel(), flags, this); } return model; #else return new AmqpModelContainer( conn.CreateModel(), flags, this); #endif }
internal void ReturnModel(AmqpModelContainer modelContainer) { // NOTE: do not call AmqpModelContainer.Close() here. // That method calls this method. #if ENABLE_CHANNELPOOLING if (_disposed.IsTrue || HasModelExpired(Environment.TickCount, modelContainer)) { DisposeModel(modelContainer); return; } //Insert model in pool ConcurrentQueue<AmqpModelContainer> queue; if (_pool.TryGetValue(modelContainer.Flags, out queue)) { //Found the queue so just enqueue the model queue.Enqueue(modelContainer); //TODO: AddOrUpdate below performs this lookup so this code here is redundant. //Consider removing this code and using an Add factory to eliminate the new queue allocation below } else { //Attempt to add a new queue, if a queue doesn't exist and if it does, then add model to queue queue = new ConcurrentQueue<AmqpModelContainer>(); queue.Enqueue(modelContainer); _pool.AddOrUpdate(modelContainer.Flags, queue, (f, q) => { q.Enqueue(modelContainer); return q; }); } //It's possible for the disposed flag to be set (and the pool flushed) right after the first _disposed check //but before the modelContainer was added, so check again. if (_disposed.IsTrue) { Flush(); } #else DisposeModel(modelContainer); #endif }
internal AmqpModelContainer GetModel(ChannelFlags flags) { #if !DISABLE_CHANNELPOOLING if (flags == ChannelFlags.Consumer) { //Do not pool consumer related channels return(CreateModel(flags)); } //Search pool for a model: AmqpModelContainer model = null; //First get the queue ConcurrentQueue <AmqpModelContainer> queue = _pool[(int)flags]; bool retry; int tick = Environment.TickCount; //Dequeue queue until an unexpired model is found do { retry = false; model = null; if (queue.TryDequeue(out model)) { if (HasModelExpired(tick, model)) { DestroyModel(model); // dispose model for good retry = true; } } }while (retry); if (model == null) { //Wasn't found, so create a new one model = CreateModel(flags); } return(model); #else return(CreateModel(flags)); #endif }
internal void ReturnModel(AmqpModelContainer modelContainer) { // NOTE: do not call AmqpModelContainer.Close() here. // That method calls this method. #if ENABLE_CHANNELPOOLING if (_disposed.IsTrue || HasModelExpired(Environment.TickCount, modelContainer)) { DisposeModel(modelContainer); return; } //Insert model in pool ConcurrentQueue <AmqpModelContainer> queue; if (_pool.TryGetValue(modelContainer.Flags, out queue)) { //Found the queue so just enqueue the model queue.Enqueue(modelContainer); //TODO: AddOrUpdate below performs this lookup so this code here is redundant. //Consider removing this code and using an Add factory to eliminate the new queue allocation below } else { //Attempt to add a new queue, if a queue doesn't exist and if it does, then add model to queue queue = new ConcurrentQueue <AmqpModelContainer>(); queue.Enqueue(modelContainer); _pool.AddOrUpdate(modelContainer.Flags, queue, (f, q) => { q.Enqueue(modelContainer); return(q); }); } //It's possible for the disposed flag to be set (and the pool flushed) right after the first _disposed check //but before the modelContainer was added, so check again. if (_disposed.IsTrue) { Flush(); } #else DisposeModel(modelContainer); #endif }
internal AmqpModelContainer GetModel(ChannelFlags flags) { #if ENABLE_CHANNELPOOLING //Search pool for a model: AmqpModelContainer model = null; //First get the queue ConcurrentQueue <AmqpModelContainer> queue; if (_pool.TryGetValue(flags, out queue)) { bool retry; int tick = Environment.TickCount; //Dequeue queue until an unexpired model is found do { retry = false; model = null; if (queue.TryDequeue(out model)) { if (HasModelExpired(tick, model)) { DisposeModel(model); // dispose model retry = true; } } }while (retry); } if (model == null) { //Wasn't found, so create a new one model = new AmqpModelContainer(conn.CreateModel(), flags, this); } return(model); #else return(new AmqpModelContainer(conn.CreateModel(), flags, this)); #endif }
public void Restart() { hasStarted.Set(true); //CLose connections and channels if (subscriberChannel != null) { if (subscriberConsumer != null) { try { subscriberChannel.Channel.BasicCancel(subscriberConsumer.ConsumerTag); } catch { } } try { subscriberChannel.Close(); } catch { } } if (workChannel != null) { if (workConsumer != null) { try { workChannel.Channel.BasicCancel(workConsumer.ConsumerTag); } catch { } } try { workChannel.Close(); } catch { } } if (_subscriberPool != null) { _subscriberPool.Dispose(); } //NOTE: CreateConnection() can throw BrokerUnreachableException //That's okay because the exception needs to propagate to Reconnect() or Start() var conn = connectionFactory.CreateConnection(); if (connectionBroken != null) connectionBroken.Dispose(); connectionBroken = new CancellationTokenSource(); if (stopWaitingOnQueue != null) stopWaitingOnQueue.Dispose(); stopWaitingOnQueue = CancellationTokenSource.CreateLinkedTokenSource(disposedCancellationSource.Token, connectionBroken.Token); var pool = new AmqpChannelPooler(conn); _subscriberPool = pool; //Use pool reference henceforth. //Create work channel and declare exchanges and queues workChannel = pool.GetModel(ChannelFlags.Consumer); //Redeclare exchanges and queues AmqpUtils.DeclareExchangeAndQueues(workChannel.Channel, messageMapper, messagingConfig, serviceName, exchangeDeclareSync, Id); //Listen on work queue workConsumer = new ConcurrentQueueingConsumer(workChannel.Channel, requestQueued); string workQueueName = AmqpUtils.GetWorkQueueName(messagingConfig, serviceName); workChannel.Channel.BasicQos(0, (ushort)Settings.PrefetchCount, false); workChannel.Channel.BasicConsume(workQueueName, Settings.AckBehavior == SubscriberAckBehavior.Automatic, workConsumer); //Listen on subscriber queue subscriberChannel = pool.GetModel(ChannelFlags.Consumer); subscriberConsumer = new ConcurrentQueueingConsumer(subscriberChannel.Channel, requestQueued); string subscriberWorkQueueName = AmqpUtils.GetSubscriberQueueName(serviceName, Id); subscriberChannel.Channel.BasicQos(0, (ushort)Settings.PrefetchCount, false); subscriberChannel.Channel.BasicConsume(subscriberWorkQueueName, Settings.AckBehavior == SubscriberAckBehavior.Automatic, subscriberConsumer); //Cancel connectionBroken on connection/consumer problems pool.Connection.ConnectionShutdown += (s, e) => { connectionBroken.Cancel(); }; workConsumer.ConsumerCancelled += (s, e) => { connectionBroken.Cancel(); }; subscriberConsumer.ConsumerCancelled += (s, e) => { connectionBroken.Cancel(); }; }
private void RedeclareExchangesAndQueues(AmqpModelContainer model, string serviceName) { //Redeclare exchanges and queues every minute if exchanges and queues are transient, or the first time client is sending a message TimeSpan elapsedSinceLastDeclareExchange = TimeSpan.FromMilliseconds(Environment.TickCount - lastExchangeDeclareTickCount); //TODO: Partition elapsedSinceLastDeclareExchange by serviceName and connection so that redeclares take place on new servicenames and connections. //Discovering firstDeclare by comparing lastExchangeDeclareTickCount to zero is not perfect //because tickcount can wrap back to zero (through the negative number range), if client is running long enough. //However, redeclaring exchanges and queues are a safe operation, so this is okay if it occurs more than once in persistent queues. bool firstDeclare = lastExchangeDeclareTickCount == 0; if (firstDeclare || (!messagingConfig.PersistentWorkQueuesAndExchanges && (elapsedSinceLastDeclareExchange.TotalMilliseconds < 0 || elapsedSinceLastDeclareExchange.TotalSeconds > 60))) { if (!firstDeclare) { //All threads must attempt to declare exchanges and queues if it hasn't been previously declared //(for instance, all threads were started at once) //So do not swap out this value on first declare lastExchangeDeclareTickCount = Environment.TickCount; } AmqpUtils.DeclareExchangeAndQueues(model.Channel, messageMapper, messagingConfig, serviceName, exchangeDeclareSync, null); if (firstDeclare) { //Swap out this value after declaring on firstdeclare lastExchangeDeclareTickCount = Environment.TickCount; } } }
private static void DisposeModel(AmqpModelContainer modelContainer) { if (modelContainer != null && modelContainer.Channel != null) { try { modelContainer.Channel.Dispose(); } catch { } } }
private static bool HasModelExpired(int currentTickCount, AmqpModelContainer modelContainer) { //TickCount wrapped around (so timespan can't be trusted) or model has expired return currentTickCount < modelContainer.Created || modelContainer.Created < (currentTickCount - MODEL_EXPIRY_TIMESPAN); }
private void CloseAmqpModel(AmqpModelContainer model) { if (model != null) { model.Close(); } }
private static bool HasModelExpired(int currentTickCount, AmqpModelContainer modelContainer) { //TickCount wrapped around (so timespan can't be trusted) or model has expired return(currentTickCount < modelContainer.Created || modelContainer.Created < (currentTickCount - MODEL_EXPIRY_TIMESPAN)); }
internal void ReturnModel(AmqpModelContainer modelContainer) { // NOTE: do not call AmqpModelContainer.Close() here. // That method calls this method. #if !DISABLE_CHANNELPOOLING //Do not return channel to pool if either: //1. Pooler is disposed //2. Channel is consumer related //3. Channel has expired if (_disposed || modelContainer.Discard || HasModelExpired(Environment.TickCount, modelContainer)) { DestroyModel(modelContainer); return; } //Insert model in pool ConcurrentQueue<AmqpModelContainer> queue = _pool[(int)modelContainer.Flags]; queue.Enqueue(modelContainer); //It's possible for the disposed flag to be set (and the pool flushed) right after the first _disposed check //but before the modelContainer was added, so check again. if (_disposed) { Flush(); } #else DisposeModel(modelContainer); #endif }
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 }