Dear All,
I have 2 micro services. One of the Microservice (Application A) has an Api endpoint that recieve messages from a partner Payment service. So my design is that once I recieve message from a payment partner. I would initiate a Queuing and pass message to that my second Micro services Application B. would be the Consumer would get the message and process it for some other background functions.
I decided to go with this design because I want 100 % gauranttee.
I am using RabbitMQ queuing system. In this system you would have to create a Producer and a consumer.
Based on my design Application A is gong to be the Producer and Application B is going to be the Consumer.
My question goes thus: Is it right for me to create the Queueing producer code from within my endpoint. Endpoint in Application A. Or is the Queueing system suppose to be a background service. I would paste what my Controller end point looks like for Application A.
public class Controller : ApiController { [HttpPost] public JsonResponse Recieve(Request model){ //I process the request sent from payment partners and save to db. //Below I now go and create a Queue which I would call QueueProducer queue= new QueueProducer(model); queue.ProcessQueue();//This message is going to process the queue. } } .........Main Code snippet from QueueProducer below. That is what sends the Producer message to the Queue. .... public void ProcessQueue() { using (var connection = CreateConnection()) using (var channel = connection.CreateModel()) { bool isopen = connection.IsOpen; channel.ConfirmSelect(); Console.WriteLine("Rabbit Queue connection is open "+isopen); logger.Log(LogLevel.Information, "Rabbit Queue connection is open " + isopen); QueueName = channel.QueueDeclare().QueueName; logger.Log(LogLevel.Information, "Rabbit Queue Name " + QueueName); Console.WriteLine("Rabbit Queue Name " + QueueName); //channel.QueueDeclare(queue: QueueName); /*channel.QueueDeclare(queue: QueueName, durable: false, exclusive: true, autoDelete: true, arguments: null);*/ var outstandingConfirms = new ConcurrentDictionary<ulong, string>(); outstandingConfirms.TryAdd(channel.NextPublishSeqNo, settlementRequest.ToString()); void cleanOutstandingConfirms(ulong sequenceNumber, bool multiple) { logger.Log(LogLevel.Information,"This callback is called when maessages are delivered and confirmed for clean up"); logger.Log(LogLevel.Information, "QueueProducer \n Cleans out outstanding confirmations in the queue based on delivery"); if (multiple) { var confirmed = outstandingConfirms.Where(k => k.Key <= sequenceNumber); foreach (var entry in confirmed) outstandingConfirms.TryRemove(entry.Key, out _); } else outstandingConfirms.TryRemove(sequenceNumber, out _); } channel.BasicAcks += (sender, ea) => cleanOutstandingConfirms(ea.DeliveryTag, ea.Multiple); channel.BasicNacks += (sender, ea) => { outstandingConfirms.TryGetValue(ea.DeliveryTag, out string body); Console.WriteLine($"Message with body {body} has been nack-ed. Sequence number: {ea.DeliveryTag}, multiple: {ea.Multiple}"); cleanOutstandingConfirms(ea.DeliveryTag, ea.Multiple); }; //Convert the message to bytes var bytes = ByteUtility.ObjectToByteArray(settlementRequest); var body1 = bytes; var timer = new Stopwatch(); timer.Start(); channel.BasicPublish(exchange: "", routingKey: QueueName, basicProperties: null, body: body1); channel.WaitForConfirmsOrDie(new TimeSpan(0, 0, 5)); timer.Stop(); if (!WaitUntil(60, () => outstandingConfirms.IsEmpty)) { logger.Log(LogLevel.Information, "QueueProducer \n All messages could not be confirmed in 60 seconds"); throw new Exception("All messages could not be confirmed in 60 seconds"); } //{MESSAGE_COUNT:N0} logger.Log(LogLevel.Information, $"QueueProducer \n Published messages and handled confirm asynchronously {timer.ElapsedMilliseconds:N0} ms"); //Console.WriteLine($"Published {MESSAGE_COUNT:N0} messages individually in {timer.ElapsedMilliseconds:N0} ms"); } }
I asked this question becos while I was readding RabbitMQ documentation. I read that this block of code
channel.ConfirmSelect();
is meant to be called once, it meant for initiating a Publisher confirmation for guarantteed delivery.
So with the approach that I am taking. My producer code is called to action every time a request hits my end point. So that means that I would always be making subsequest connection to the Rabbitmq server and calling the channel.ConfirmSelect(); method.
I dont have any other design flow in my head. So am just confused here. is there a better way to implement this.