public TaskSlim<MessageDelivery> Call(string exchange, string routing, BasicProperties properties, ArraySegment<byte> buffer) { _semaphoreSlim.Wait(); var task = _taskResultPool.GetObject(); uint correlationId; if (!SecureSpotAndUniqueCorrelationId(task, out correlationId)) { _semaphoreSlim.Release(); Console.WriteLine("max calls reached!"); task.SetException(new Exception("reached max calls")); return task; } task.Id = correlationId; // so we can confirm we have the right instance later task.Started = DateTime.Now.Ticks; try { var prop = properties ?? _channel.RentBasicProperties(); prop.CorrelationId = correlationId.ToString(); prop.ReplyTo = _replyQueueName.Name; // TODO: confirm this doesnt cause more overhead to rabbitmq prop.Expiration = _timeoutInMs.ToString(); _channel.BasicPublishFast(exchange, routing, true, false, properties, buffer); } catch (Exception ex) { // release spot Interlocked.Exchange(ref _pendingCalls[correlationId % _maxConcurrentCalls], null); _semaphoreSlim.Release(); task.SetException(ex); } return task; }
internal void DispatchDeliveredMessage( string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, int bodySize, BasicProperties properties, BaseLightStream lightStream) { BasicConsumerSubscriptionInfo consumer; if (!_consumerSubscriptions.TryGetValue(consumerTag, out consumer)) { // received msg but nobody was subscribed to get it (?) LogAdapter.LogWarn(LogSource, "Received message without a matching subscription. Discarding. " + "Exchange: " + exchange + " routing: " + routingKey + " consumer tag: " + consumerTag + " and channel " + this.ChannelNumber); // Ensure moved cursor ahead var marker = new RingBufferPositionMarker(lightStream); marker.EnsureConsumed(bodySize); return; } var delivery = new MessageDelivery { bodySize = bodySize, properties = properties, routingKey = routingKey, deliveryTag = deliveryTag + this._deliveryTagOffset, // adds tag offset redelivered = redelivered, }; var mode = consumer.Mode; var cb = consumer.Callback; var consumerInstance = consumer._consumer; if (mode == ConsumeMode.SingleThreaded) { // run with scissors, we're letting // the user code mess with the ring buffer in the name of performance delivery.stream = bodySize == 0 ? EmptyStream : lightStream; // upon return it's assumed the user has consumed from the stream and is done with it var marker = new RingBufferPositionMarker(lightStream); try { if (cb != null) { cb(delivery).GetAwaiter().GetResult(); } else { consumerInstance.Consume(delivery).GetAwaiter().GetResult(); } } finally { if (!delivery.TakenOver) { delivery.Dispose(); } // fingers crossed the user cloned the buffer if she needs it later marker.EnsureConsumed(bodySize); } } else { // parallel mode. it cannot hold the frame handler, so we copy the buffer (yuck) and move forward if (mode == ConsumeMode.ParallelWithBufferCopy || mode == ConsumeMode.SerializedWithBufferCopy) { delivery.stream = delivery.bodySize == 0 ? EmptyStream : lightStream.CloneStream(bodySize); } if (mode == ConsumeMode.SerializedWithBufferCopy) // Posts to a thread { if (consumer._consumerThread == null) { // Initialization. safe since this delivery call always happen from the same thread consumer._receivedMessages = new ConcurrentQueue <MessageDelivery>(); consumer._messageAvailableEv = new AutoResetEvent(false); consumer._consumerThread = ThreadFactory.BackgroundThread(SerializedDelivery, "Delivery_" + consumer.ConsumerTag, consumer); } consumer._receivedMessages.Enqueue(delivery); consumer._messageAvailableEv.Set(); } else if (mode == ConsumeMode.ParallelWithBufferCopy) // Posts to TaskScheduler { new Task(async state => { var tuple = (Tuple <MessageDelivery, Func <MessageDelivery, Task>, IQueueConsumer, Channel>)state; var delivery1 = tuple.Item1; var cb1 = tuple.Item2; var conInstance = tuple.Item3; try { if (cb1 != null) { await cb1(delivery1).ConfigureAwait(false); } else { await conInstance.Consume(delivery1).ConfigureAwait(false); } } catch (Exception e) { if (LogAdapter.IsErrorEnabled) { LogAdapter.LogError(LogSource, "Error processing message (user code)", e); } } finally { if (!delivery1.TakenOver) { delivery1.Dispose(); } } }, Tuple.Create(delivery, cb, consumerInstance, this)) // tuple avoids the closure capture // Start task in the given scheduler .Start(_schedulerToDeliverMessages); } } }
public static TaskSlim BasicPublish(this Channel source, string exchange, string routingKey, bool mandatory, bool immediate, BasicProperties properties, byte[] buffer) { return(source.BasicPublish(exchange, routingKey, mandatory, immediate, properties, new ArraySegment <byte>(buffer))); }
public static void BasicPublishFast(this Channel source, string exchange, string routingKey, bool mandatory, BasicProperties properties, byte[] buffer) { source.BasicPublishFast(exchange, routingKey, mandatory, false, properties, new ArraySegment <byte>(buffer)); }
// private static async Task StartRpc() // { // Connection conn1 = null; // Connection conn2 = null; // try // { // conn1 = await new ConnectionFactory().Connect(TargetHost, vhost: VHost, username: _username, password: _password); // conn2 = await new ConnectionFactory().Connect(TargetHost, vhost: VHost, username: _username, password: _password); // // Console.WriteLine("[Connected]"); // // var newChannel = await conn1.CreateChannel(); // Console.WriteLine("[channel created] " + newChannel.ChannelNumber); // await newChannel.BasicQos(0, Prefetch, false); // // await newChannel.ExchangeDeclare("test_ex", "direct", true, false, null, true); // // var qInfo = await newChannel.QueueDeclare("rpc1", false, true, false, false, null, true); // // Console.WriteLine("[qInfo] " + qInfo); // // await newChannel.QueueBind("rpc1", "test_ex", "rpc1", null, true); // // var temp = new byte[1000]; // // Console.WriteLine("Starting Rpc channel consumer..."); // await newChannel.BasicConsume(ConsumeMode.SingleThreaded, delivery => // { // if (delivery.stream != null) // delivery.stream.Read(temp, 0, (int) delivery.bodySize); //// else //// temp = delivery.Body; // // var replyProp = new BasicProperties() // { // CorrelationId = delivery.properties.CorrelationId // }; // // newChannel.BasicPublishFast("", // delivery.properties.ReplyTo, false, false, // replyProp, new ArraySegment<byte>(temp, 0, 4)); // // return Task.CompletedTask; // // }, "rpc1", "", true, false, null, waitConfirmation: true); // // Console.WriteLine("Starting Rpc calls..."); // // var newChannel2 = await conn2.CreateChannel(); // Console.WriteLine("[channel created] " + newChannel2.ChannelNumber); // await newChannel2.BasicQos(0, Prefetch, false); // // var rpcHelper = await newChannel2.CreateRpcHelper(ConsumeMode.SingleThreaded); // // var watch = new Stopwatch(); // watch.Start(); // // var totalReceived = 0; // var req = new byte[100]; // var reply = new byte[100]; // for (int i = 0; i < TotalPublish; i++) // { // var prop2 = new BasicProperties(); // req[3] = (byte)((i & 0xFF000000) >> 24); // req[2] = (byte)((i & 0x00FF0000) >> 16); // req[1] = (byte)((i & 0x0000FF00) >> 8); // req[0] = (byte)((i & 0x000000FF)); // // var rpcCallResult = await rpcHelper.Call("test_ex", "rpc1", prop2, new ArraySegment<byte>(req, 0, 4)); // if (rpcCallResult.stream != null) // { // rpcCallResult.stream.Read(reply, 0, rpcCallResult.bodySize); // var x = BitConverter.ToInt32(reply, 0); // if (x != i) throw new Exception("Invalid result for call"); // } //// else if (rpcCallResult.Body != null) //// { //// var x = BitConverter.ToInt32(rpcCallResult.Body, 0); //// if (x != i) throw new Exception("Invalid result for call"); //// } // // // var rpcCallResult2 = await rpcHelper.Call("test_ex", "rpc1", prop2, new ArraySegment<byte>(req, 0, 4)); // // var val = Interlocked.Increment(ref totalReceived); // // if (val == TotalPublish) // { // watch.Stop(); // Console.WriteLine("Rpc stress. Took " + // watch.Elapsed.TotalMilliseconds + // "ms - rate of " + (TotalPublish / watch.Elapsed.TotalSeconds) + " message per second"); // totalReceived = 0; // } // } // //// watch = Stopwatch.StartNew(); //// int totalReceived = 0; // //// Console.WriteLine("[subscribed to queue] " + sub); // // await Task.Delay(TimeSpan.FromMinutes(5)); // // await newChannel.Close(); // await newChannel2.Close(); // } // catch (AggregateException ex) // { // Console.WriteLine("[Captured error] " + ex.Flatten().Message); // } // catch (Exception ex) // { // Console.WriteLine("[Captured error 2] " + ex.Message); // } // // if (conn1 != null) // await conn1.Close(); // if (conn2 != null) // await conn2.Close(); // } private static async Task Start() { Connection conn = null; try { conn = await new ConnectionFactory().Connect(TargetHost, vhost: VHost, username: _username, password: _password); Console.WriteLine("[Connected]"); var newChannel = await conn.CreateChannel(); // var newChannel = await conn.CreateChannelWithPublishConfirmation(); Console.WriteLine("[channel created] " + newChannel.ChannelNumber); await newChannel.BasicQos(0, Prefetch, false); await newChannel.ExchangeDeclare("test_ex", "direct", true, false, null, true); var qInfo = await newChannel.QueueDeclare("queue1", false, true, false, false, null, true); Console.WriteLine("[qInfo] " + qInfo); await newChannel.QueueBind("queue1", "test_ex", "routing1", null, true); var prop = new BasicProperties() { // DeliveryMode = 2, Type = "type1", Headers = new Dictionary<string, object> {{"serialization", 0}} }; newChannel.MessageUndeliveredHandler = (undelivered) => { Console.WriteLine("\t(Ops, message not routed: " + undelivered.replyCode + " " + undelivered.replyText + " " + undelivered.routingKey + ")"); return Task.CompletedTask; }; Console.WriteLine("Started Publishing..."); var watch = Stopwatch.StartNew(); for (int i = 0; i < TotalPublish; i++) { prop.Headers["serialization"] = i; // var buffer = Encoding.ASCII.GetBytes("The " + i + " " + Message); await newChannel.BasicPublish("test_ex", "routing1", true, false, prop, new ArraySegment<byte>(MessageContent)); // await Task.Delay(TimeSpan.FromMilliseconds(100)); } watch.Stop(); Console.WriteLine(DateTime.Now.TimeOfDay.TotalSeconds + " BasicPublish stress. Took " + watch.Elapsed.TotalMilliseconds + "ms - rate of " + (TotalPublish / watch.Elapsed.TotalSeconds) + " message per second"); await Task.Delay(TimeSpan.FromMilliseconds(200)); var newChannel2 = await conn.CreateChannel(); Console.WriteLine("[channel created] " + newChannel2.ChannelNumber); await newChannel2.BasicQos(0, Prefetch, false); var temp = new byte[1000000]; watch = Stopwatch.StartNew(); int totalReceived = 0; Console.WriteLine("[subscribing to queue] "); var sub = await newChannel2.BasicConsume(ConsumeMode.SingleThreaded, async (delivery) => { // var len = await delivery.stream.ReadAsync(temp, 0, (int) delivery.bodySize); // var str = Encoding.UTF8.GetString(temp, 0, len); // Console.WriteLine("Received : " + str.Length); if (WithAcks) { if (totalReceived % 2 == 0) newChannel2.BasicAck(delivery.deliveryTag, false); else newChannel2.BasicNAck(delivery.deliveryTag, false, false); } var val = Interlocked.Increment(ref totalReceived); if (val == TotalPublish) { watch.Stop(); Console.WriteLine("Consume stress. Took " + watch.Elapsed.TotalMilliseconds + "ms - rate of " + (TotalPublish / watch.Elapsed.TotalSeconds) + " message per second"); totalReceived = 0; } // return Task.CompletedTask; }, "queue1", "tag123", !WithAcks, false, null, true); Console.WriteLine("[subscribed to queue] " + sub); await Task.Delay(TimeSpan.FromMinutes(5)); await newChannel.Close(); await newChannel2.Close(); } catch (AggregateException ex) { Console.WriteLine("[Captured error] " + ex.Flatten().Message); } catch (Exception ex) { Console.WriteLine("[Captured error 2] " + ex.Message); } if (conn != null) conn.Dispose(); }
// private static async Task StartConcurrentRpc() // { // Connection conn2 = null; // try // { // conn2 = await new ConnectionFactory().Connect(TargetHost, // vhost: VHost, username: _username, password: _password); // // Console.WriteLine("[Connected]"); // // Console.WriteLine("Starting Rpc Parallel calls..."); // // var newChannel2 = await conn2.CreateChannel(); // Console.WriteLine("[channel created] " + newChannel2.ChannelNumber); // await newChannel2.BasicQos(0, Prefetch, false); // // var rpcHelper = await newChannel2.CreateRpcHelper(ConsumeMode.ParallelWithBufferCopy); // // var watch = new Stopwatch(); // watch.Start(); // // var totalReceived = 0; // // // var tasks = new Task[ConcurrentCalls]; // // for (int i = 0; i < TotalPublish; i += ConcurrentCalls) // { // for (int j = 0; j < ConcurrentCalls; j++) // { // var t = MakeCall(rpcHelper, i + j); // tasks[j] = t; // } // // Task.WaitAll(tasks); // // totalReceived += ConcurrentCalls; // //// Console.WriteLine("calls " + totalReceived); // // if (totalReceived >= TotalPublish) // { // watch.Stop(); // Console.WriteLine("Rpc stress done. Took " + // watch.Elapsed.TotalMilliseconds + // "ms - rate of " + (TotalPublish / watch.Elapsed.TotalSeconds) + " messages per second"); // totalReceived = 0; // } // } // // await Task.Delay(TimeSpan.FromMinutes(5)); // // await newChannel2.Close(); // } // catch (AggregateException ex) // { // Console.WriteLine("[Captured error] " + ex.Message); // } // catch (Exception ex) // { // Console.WriteLine("[Captured error 2] " + ex.Message); // } // // if (conn2 != null) // conn2.Dispose(); // } private static async Task<int> MakeCall(RpcHelper rpcHelper, int y) { var prop2 = new BasicProperties(); var req = new byte[4]; req[3] = (byte)((y & 0xFF000000) >> 24); req[2] = (byte)((y & 0x00FF0000) >> 16); req[1] = (byte)((y & 0x0000FF00) >> 8); req[0] = (byte)((y & 0x000000FF)); var rpcCallResult = await rpcHelper.Call("test_ex", "rpc1", prop2, new ArraySegment<byte>(req, 0, 4)); if (rpcCallResult.stream != null) { var reply = new byte[4]; rpcCallResult.stream.Read(reply, 0, rpcCallResult.bodySize); var x = BitConverter.ToInt32(reply, 0); if (x != y) throw new Exception("Invalid result for call"); } // else if (rpcCallResult.Body != null) // { // var x = BitConverter.ToInt32(rpcCallResult.Body, 0); // if (x != y) throw new Exception("Invalid result for call"); // } // Console.WriteLine("Call " + y + " completed"); return y; }
public static Task <MessageDelivery> Call(this RpcHelper source, string exchange, string routing, BasicProperties properties, byte[] buffer) { return(source.Call(exchange, routing, properties, new ArraySegment <byte>(buffer))); }
object ICloneable.Clone() { var cloned = new BasicProperties(false, false) { _presenceSWord = this._presenceSWord, _timestamp = this._timestamp, _deliveryMode = this._deliveryMode, _priority = this._priority, _contentType = this._contentType, _contentEncoding = this._contentEncoding, _correlationId = this._correlationId, _replyTo = this._replyTo, _expiration = this._expiration, _messageId = this._messageId, _type = this._type, _userId = this._userId, _appId = this._appId, _clusterId = this._clusterId, }; if (this._headers != null && this._headers.Count != 0) { cloned._headers = new Dictionary<string, object>(this._headers); } return cloned; }
/// <summary> /// The request message is expected to have multiple receivers, and multiple replies. /// The replies will be aggregated and returned, respecting up to a minimum set by minExpectedReplies, and /// if unsuccessful a timeout error will be thrown. /// </summary> public Task <IEnumerable <MessageDelivery> > CallAggregate(string exchange, string routing, BasicProperties properties, ArraySegment <byte> buffer, int minExpectedReplies, bool runContinuationsAsynchronously = true) { if (!_operational) { throw new Exception("Can't make RPC call when connection in recovery"); } _semaphoreSlim.Wait(); uint correlationId; long pos; var tcs = SecureSpotAndUniqueCorrelationId(runContinuationsAsynchronously, exchange, routing, out pos, out correlationId); if (tcs == null) { _semaphoreSlim.Release(); // NOTE: If our use of semaphore is correct, this should never happen: LogAdapter.LogError(LogSource, "Maxed calls: " + _maxConcurrentCalls); throw new Exception("reached max calls"); } var cookie = tcs.Task.Id; var state = _pendingAggregationState[pos]; lock (state) // bad, but chances of s*** happening are too high { Interlocked.Exchange(ref state.waitingCount, minExpectedReplies); Interlocked.Exchange(ref state.cookie, cookie); state.received.Clear(); } try { var prop = (properties == null || properties == BasicProperties.Empty) ? _channel.RentBasicProperties() : properties; prop.CorrelationId = BuildFullCorrelation(cookie, correlationId); // correlationId + SeparatorStr + cookie; prop.ReplyTo = _replyQueueName.Name; // TODO: confirm this doesnt cause more overhead to rabbitmq if (_timeoutInMs.HasValue) { // prop.Expiration = _timeoutInMs.ToString(); } _channel.BasicPublishFast(exchange, routing, true, prop, buffer); } catch (Exception ex) { if (ReleaseSpot(pos, cookie)) { _semaphoreSlim.Release(); } tcs.TrySetException(ex); } return(tcs.Task); }
/// <summary> /// The request message is expected to have multiple receivers, and multiple replies. /// The replies will be aggregated and returned, respecting up to a minimum set by minExpectedReplies, and /// if unsuccessful a timeout error will be thrown. /// </summary> public TaskSlim <IEnumerable <MessageDelivery> > CallAggregate(string exchange, string routing, BasicProperties properties, ArraySegment <byte> buffer, int minExpectedReplies) { _semaphoreSlim.Wait(); var task = _taskResultPool.GetObject(); uint correlationId; long pos; if (!SecureSpotAndUniqueCorrelationId(task, out pos, out correlationId)) { _semaphoreSlim.Release(); // NOTE: If our use of semaphore is correct, this should never happen: LogAdapter.LogError("RpcAggregateHelper", "Maxed calls: " + _maxConcurrentCalls); task.SetException(new Exception("reached max calls")); return(task); } var cookie = Rnd.Next(); var state = _pendingAggregationState[pos]; lock (state) // bad, but chances of s*** happening are too high { Interlocked.Exchange(ref state.waitingCount, minExpectedReplies); Interlocked.Exchange(ref state.cookie, cookie); state.received.Clear(); } task.Id = correlationId; // so we can confirm we have the right instance later task.Started = DateTime.Now.Ticks; try { var prop = properties ?? _channel.RentBasicProperties(); prop.CorrelationId = correlationId + Separator[0].ToString() + cookie; prop.ReplyTo = _replyQueueName.Name; // TODO: confirm this doesnt cause more overhead to rabbitmq if (_timeoutInMs.HasValue) { // prop.Expiration = _timeoutInMs.ToString(); } _channel.BasicPublishFast(exchange, routing, true, prop, buffer); } catch (Exception ex) { // release spot Interlocked.Exchange(ref _pendingCalls[pos], null); _semaphoreSlim.Release(); task.SetException(ex); } return(task); }
internal async Task DispatchDeliveredMessage(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, int bodySize, BasicProperties properties, Stream stream) { BasicConsumerSubscriptionInfo consumer; if (_consumerSubscriptions.TryGetValue(consumerTag, out consumer)) { var delivery = new MessageDelivery() { bodySize = bodySize, properties = properties, routingKey = routingKey, deliveryTag = deliveryTag, redelivered = redelivered }; var mode = consumer.Mode; var cb = consumer.Callback; var consumerInstance = consumer._consumer; if (mode == ConsumeMode.SingleThreaded) { // run with scissors, we're letting // the user code mess with the ring buffer in the name of performance delivery.stream = stream; // upon return it's assumed the user has consumed from the stream and is done with it try { if (cb != null) { await cb(delivery); } else { await consumerInstance.Consume(delivery); } } finally { // fingers crossed the user cloned the buffer if she needs it later this.Return(properties); } } else { // parallel mode. it cannot hold the frame handler, so we copy the buffer (yuck) and more forward // since we dont have any control on how the user // will deal with the buffer we cant even re-use/use a pool, etc :-( // Idea: split Ringbuffer consumers, create reader barrier. once they are all done, // move the read pos forward. Shouldnt be too hard to implement and // avoids the new buffer + GC and keeps the api Stream based consistently if (mode == ConsumeMode.ParallelWithBufferCopy) { var bufferCopy = BufferUtil.Copy(stream as RingBufferStreamAdapter, (int)bodySize); var memStream = new MemoryStream(bufferCopy, writable: false); delivery.stream = memStream; } // else if (mode == ConsumeMode.ParallelWithReadBarrier) // { // var readBarrier = new RingBufferStreamReadBarrier(stream as RingBufferStreamAdapter, delivery.bodySize); // delivery.stream = readBarrier; // } #pragma warning disable 4014 Task.Factory.StartNew(async() => // ThreadPool.UnsafeQueueUserWorkItem((param) => #pragma warning restore 4014 { try { using (delivery.stream) { if (cb != null) { await cb(delivery); } else { await consumerInstance.Consume(delivery); } } } catch (Exception e) { Console.WriteLine("From threadpool " + e); } finally { this.Return(properties); } }, TaskCreationOptions.PreferFairness); } } else { // received msg but nobody was subscribed to get it (?) TODO: log it at least } }
internal void __BasicPublish(string exchange, string routingKey, bool mandatory, bool immediate, BasicProperties properties, ArraySegment<byte> buffer) { if (properties == null) { properties = BasicProperties.Empty; } var args = _basicPubArgsPool.GetObject(); args.exchange = exchange; args.immediate = immediate; args.routingKey = routingKey; args.mandatory = mandatory; args.properties = properties; args.buffer = buffer; _connectionIo.SendCommand(_channelNum, 60, 40, null, // writer reply: (channel, classMethodId, error) => { if (properties.IsReusable) { _channel.Return(properties); // the tcs is left for the confirmation keeper } return Task.CompletedTask; }, expectsReply: false, optArg: args); }
internal TaskSlim __BasicPublishConfirm(string exchange, string routingKey, bool mandatory, bool immediate, BasicProperties properties, ArraySegment<byte> buffer) { if (properties == null) { properties = BasicProperties.Empty; } var confirmationKeeper = _channel._confirmationKeeper; TaskSlim tcs = _taskLightPool.GetObject(); confirmationKeeper.WaitForSemaphore(); // make sure we're not over the limit var args = _basicPubArgsPool.GetObject(); args.exchange = exchange; args.immediate = immediate; args.routingKey = routingKey; args.mandatory = mandatory; args.properties = properties; args.buffer = buffer; _connectionIo.SendCommand(_channelNum, 60, 40, null, // AmqpChannelLevelFrameWriter.InternalBasicPublish, reply: (channel, classMethodId, error) => { if (properties.IsReusable) { _channel.Return(properties); // the tcs is left for the confirmation keeper } return Task.CompletedTask; }, expectsReply: false, tcsL: null, optArg: args, prepare: () => _channel._confirmationKeeper.Add(tcs)); return tcs; }
public static TaskSlim BasicPublish(this Channel source, string exchange, string routingKey, bool mandatory, bool immediate, BasicProperties properties, byte[] buffer) { return source.BasicPublish(exchange, routingKey, mandatory, immediate, properties, new ArraySegment<byte>(buffer)); }
public static void BasicPublishFast(this Channel source, string exchange, string routingKey, bool mandatory, BasicProperties properties, byte[] buffer) { source.BasicPublishFast(exchange, routingKey, mandatory, false, properties, new ArraySegment<byte>(buffer)); }