public void TestMultipleProducerCreateAndSend( [Values(MsgDeliveryMode.NonPersistent, MsgDeliveryMode.Persistent)] MsgDeliveryMode mode ) { const int MSG_TTL_MILLIS = 8500; // 8.5 secs const int NUM_MSGS = 200; const int NUM_PRODUCERS = 5; const string MSG_ID_KEY = "MsgIndex"; const string PRODUCER_ID_KEY = "ProducerIndex"; const string PRODUCER_INDEXED_ID_KEY = "ProducerIndexedMsgId"; bool persistent = mode.Equals(MsgDeliveryMode.Persistent); bool useMsgId = !persistent; int msgIdWindow = 0; string failureErr = null; IMessageProducer producer = null; IList <IMessageProducer> producers = null; IList <int> lastProducerIndexedIds = null; try { using (IConnection connection = this.GetConnection("c1")) using (ISession session = this.GetSession("s1")) using (IDestination destination = this.GetDestination("t1")) using (IMessageConsumer drain = this.GetConsumer("drain")) { lastProducerIndexedIds = new List <int>(); MessageListener ackCallback = CreateListener(NUM_MSGS); drain.Listener += (message) => { if (failureErr == null) { ackCallback(message); int id = message.Properties.GetInt(PRODUCER_INDEXED_ID_KEY); int prodIndex = message.Properties.GetInt(PRODUCER_ID_KEY); int lastId = lastProducerIndexedIds[prodIndex]; int advancedMsgs = id - lastId; if (id < lastId) { failureErr = string.Format( "Received message out of order." + " Received, sent from producer {0} msg id {1} where last msg id {2}", prodIndex, id, lastId ); this.waiter.Set(); } else if (persistent && advancedMsgs > 1) { failureErr = string.Format( "Persistent Messages where drop." + " Received, sent from producer {0} msg id {1} where last msg id {2}", prodIndex, id, lastId ); this.waiter.Set(); } else { lastProducerIndexedIds[prodIndex] = id; if (advancedMsgs > 1 && (Logger.IsInfoEnabled || Logger.IsDebugEnabled)) { Logger.Info(string.Format( "{0} Messages dropped for producer {1} from message id {2}", advancedMsgs, prodIndex, lastId )); } msgIdWindow += advancedMsgs; if (!persistent && msgIdWindow == NUM_MSGS) { this.waiter.Set(); } } } }; connection.ExceptionListener += DefaultExceptionListener; producers = new List <IMessageProducer>(); for (int i = 0; i < NUM_PRODUCERS; i++) { try { producer = session.CreateProducer(destination); } catch (Exception ex) { this.PrintTestFailureAndAssert(this.GetMethodName(), "Failed to Created Producer " + i, ex); } producer.DeliveryMode = mode; producer.DisableMessageID = !useMsgId; producer.TimeToLive = TimeSpan.FromMilliseconds(MSG_TTL_MILLIS); producers.Add(producer); lastProducerIndexedIds.Add(-1); } connection.Start(); Assert.AreEqual(NUM_PRODUCERS, producers.Count, "Did not create all producers."); Assert.IsNull(asyncEx, "Exception Listener Called While creating producers. With exception {0}.", asyncEx); ITextMessage msg = session.CreateTextMessage(); int producerIndex = -1; for (int i = 0; i < NUM_MSGS; i++) { msg.Text = "Index:" + i; msg.Properties[MSG_ID_KEY] = i; msg.Properties[PRODUCER_INDEXED_ID_KEY] = i / NUM_PRODUCERS; producerIndex = i % NUM_PRODUCERS; msg.Properties[PRODUCER_ID_KEY] = producerIndex; producers[producerIndex].Send(msg); } Assert.IsNull(asyncEx, "Exception Listener Called While sending messages. With exception {0}.", asyncEx); Assert.IsTrue(waiter.WaitOne(TIMEOUT), "Failed to received all messages in {0}ms. Received {1} of {2} messages", TIMEOUT, msgCount, NUM_MSGS); Assert.IsNull(failureErr, "Received assertion failure from IMessageConsumer message Listener. Failure : {0}", failureErr ?? ""); if (persistent) { Assert.AreEqual(NUM_MSGS, msgCount, "Receive unexpected from messages sent. Message Window {0}", msgIdWindow); } else { int missedMsgs = (msgIdWindow - msgCount); Assert.AreEqual(NUM_MSGS, msgIdWindow, "Failed to receive all messages." + " Received {0} of {1} messages, with missed messages {2}, in {3}ms", msgCount, NUM_MSGS, missedMsgs, TIMEOUT ); if (missedMsgs > 0) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); const string SEPARATOR = ", "; for (int i = 0; i < NUM_PRODUCERS; i++) { sb.AppendFormat("Last received Producer {0} message id {1}{2}", i, lastProducerIndexedIds[i], SEPARATOR); } sb.Length = sb.Length - SEPARATOR.Length; Logger.Warn(string.Format("Did not receive all Non Persistent messages. Received {0} of {1} messages. Where last received message ids = [{2}]", msgCount, NUM_MSGS, sb.ToString())); } } Assert.IsNull(asyncEx, "Exception Listener Called While receiveing messages. With exception {0}.", asyncEx); // // Some brokers are sticklers for detail and actually honor the // batchable flag that AMQPnetLite sets on all published messages. As // a result all messages can be long received before the published // messages are acknowledged. So to avoid a hand full of // amqp:message:released outcomes, just pause a few seconds before // closing the producer System.Threading.Thread.Sleep(3000); } } catch (Exception e) { this.PrintTestFailureAndAssert(this.GetMethodName(), "Unexpected Exception.", e); } finally { if (producers != null) { foreach (IMessageProducer p in producers) { p?.Close(); p?.Dispose(); } producers.Clear(); } } }
[ProducerSetup("default", "testdest4", "pro4")]//*/ public void TestMultipleConsumerMessageListenerReceive() { const int NUM_MSGS = 100000; string[] conIds = new string[] { "con1", "con2", "con3", "con4" }; string[] proIds = new string[] { "pro1", "pro2", "pro3", "pro4" }; IList <IMessageConsumer> consumers = this.GetConsumers(conIds); IList <IMessageProducer> producers = this.GetProducers(proIds); int ProducerCount = producers.Count; int ConsumerCount = consumers.Count; long[] lastMsgId = new long[ConsumerCount]; long[] MissingMsgCount = new long[ConsumerCount]; long lostMsgCount = 0; int TotalMsgs = NUM_MSGS * ProducerCount; long undeliveredMsgCount = TotalMsgs; int timeout = Math.Max(TotalMsgs / 1000, 1) * 1100; MsgDeliveryMode mode = MsgDeliveryMode.NonPersistent; using (IConnection connection = this.GetConnection("default")) { connection.ExceptionListener += DefaultExceptionListener; for (int i = 0; i < ConsumerCount; i++) { lastMsgId[i] = -1; MissingMsgCount[i] = 0; } try { MessageListener callback = CreateListener(TotalMsgs); int index = 0; foreach (IMessageConsumer consumer in consumers) { int num = index; MessageListener countingCallback = (m) => { long lastId = lastMsgId[num]; long msgId = ExtractMsgId(m.NMSMessageId); if (msgId > lastId) { lastMsgId[num] = msgId; if (lastId != -1) { MissingMsgCount[num] += (msgId - (lastId + 1)); lostMsgCount += (msgId - (lastId + 1)); } } callback(m); // signal envent waiter when the last expected msg is delivered on all consumers if (lostMsgCount + msgCount == TotalMsgs) { // signal if detected lost msgs from id gap and delivered msgs make the total msgs. waiter?.Set(); } else if (lastMsgId[num] == NUM_MSGS - 1) { // signal if final msg id on every consumer is detected. undeliveredMsgCount -= NUM_MSGS; if (undeliveredMsgCount <= 0) { waiter?.Set(); } } }; consumer.Listener += countingCallback; index++; } connection.Start(); // send messages to Destinations ITextMessage sendMsg = producers[0].CreateTextMessage(); for (int i = 0; i < NUM_MSGS; i++) { int link = 0; foreach (IMessageProducer producer in producers) { sendMsg.Text = "Link: " + link + ", num:" + i; link++; producer.Send(sendMsg, mode, MsgPriority.Normal, TimeSpan.Zero); } } if (!waiter.WaitOne(timeout)) { if (mode.Equals(MsgDeliveryMode.NonPersistent)) { if (msgCount != TotalMsgs) { Logger.Warn(string.Format("Only received {0} of {1} messages in {2}ms.", msgCount, TotalMsgs, timeout)); LogSummary(MissingMsgCount, lastMsgId, NUM_MSGS); } Assert.IsNull(asyncEx, "Received unexpected asynchronous exception. Message : {0}", asyncEx?.Message); } else { Assert.Fail("Only received {0} of {1} messages in {2}ms.", msgCount, TotalMsgs, timeout); } } else { if (msgCount != TotalMsgs) { Logger.Warn(string.Format("Only received {0} of {1} messages in {2}ms.", msgCount, TotalMsgs, timeout)); LogSummary(MissingMsgCount, lastMsgId, NUM_MSGS); } Assert.IsNull(asyncEx, "Received unexpected asynchronous exception. Message : {0}", asyncEx?.Message); long ActualMsgs = mode.Equals(MsgDeliveryMode.NonPersistent) ? msgCount + lostMsgCount : msgCount; Assert.AreEqual(TotalMsgs, ActualMsgs, "Only received {0} (delivered = {1}, lost = {2}) of {3} messages in {4}ms.", ActualMsgs, msgCount, lostMsgCount, TotalMsgs, timeout); } } catch (Exception ex) { this.PrintTestFailureAndAssert(GetTestMethodName(), "Unexpected Exception.", ex); } finally { // sleep for 2 seconds to allow for pending procuder acknowledgements to be received from the broker. System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2)); } } }
protected void DoSend(IDestination destination, IMessage message, MsgDeliveryMode deliveryMode, MsgPriority priority, TimeSpan timeToLive) { this.Attach(); bool sendSync = deliveryMode.Equals(MsgDeliveryMode.Persistent); if (destination.IsTemporary && (destination as TemporaryDestination).IsDeleted) { throw new InvalidDestinationException("Can not send message on deleted temporary topic."); } message.NMSDestination = destination; message.NMSDeliveryMode = deliveryMode; message.NMSPriority = priority; // If there is timeToLive, set it before setting NMSTimestamp as timeToLive // is required to calculate absolute expiry time. // TBD: If the messageProducer has a non-default timeToLive and the message // already has a timeToLive set by application, which should take precedence, this // code overwrites the message TimeToLive in this case but not if the producer TimeToLive // is the default ... if (timeToLive != NMSConstants.defaultTimeToLive) { message.NMSTimeToLive = timeToLive; } if (!DisableMessageTimestamp) { message.NMSTimestamp = DateTime.UtcNow; } if (!DisableMessageID) { message.NMSMessageId = MessageIdGenerator.GenerateId().ToString(); } Amqp.Message amqpmsg = null; if (message is Message.Message) { Message.Message copy = (message as Message.Message).Copy(); copy.NMSDestination = DestinationTransformation.Transform(Session.Connection, destination); PrepareMessageForSend(copy); IMessageCloak cloak = copy.GetMessageCloak(); if (cloak is AMQPMessageCloak) { amqpmsg = (cloak as AMQPMessageCloak).AMQPMessage; } } else { Message.Message nmsmsg = this.Session.Connection.TransformFactory.TransformMessage <Message.Message>(message); PrepareMessageForSend(nmsmsg); IMessageCloak cloak = nmsmsg.GetMessageCloak().Copy(); if (cloak is AMQPMessageCloak) { amqpmsg = (cloak as AMQPMessageCloak).AMQPMessage; } } if (amqpmsg != null) { if (Tracer.IsDebugEnabled) { Tracer.DebugFormat("Sending message : {0}", message.ToString()); } if (sendSync) { DoAMQPSendSync(amqpmsg, this.RequestTimeout); } else { DoAMQPSendAsync(amqpmsg, HandleAsyncAMQPMessageOutcome); } } }