.netiorabbitmq

RabbitMQ IO threads


I was reading .net documentation(https://www.rabbitmq.com/tutorials/tutorial-seven-dotnet) for the RabbitMq.Client library and reached this part

var channel = connection.CreateModel();
channel.ConfirmSelect();
channel.BasicAcks += (sender, ea) =>
{
  // code when message is confirmed
};
channel.BasicNacks += (sender, ea) =>
{
  //code when message is nack-ed
};

Quote: It can be tempting to re-publish a nack-ed message from the corresponding callback but this should be avoided, as confirm callbacks are dispatched in an I/O thread where channels are not supposed to do operations. A better solution consists in enqueuing the message in an in-memory queue which is polled by a publishing thread. A class like ConcurrentQueue would be a good candidate to transmit messages between the confirm callbacks and a publishing thread.

I read about OS-level IO threads, but i'm pretty sure it's not about them. How can a thread be IO? Why they're tellings me i should't be doing this

channel.BasicNacks += async (sender, ea) =>
{
   var someIoReuslt = await channel.BasicPublishAsync(queue,...);
};

I tried to look at the internal implementation of the Channel, how they're invoking all the callbacks that're subscribed on this event(nack received)

protected void HandleBasicAck(IncomingCommand cmd)
{
    var ack = new BasicAck(cmd.MethodSpan);
    if (!_basicAcksWrapper.IsEmpty)
    {
        var args = new BasicAckEventArgs(ack._deliveryTag, ack._multiple);
        _basicAcksWrapper.Invoke(this, args);
    }

    HandleAckNack(ack._deliveryTag, ack._multiple, false);
}

apart from return type void i see no problem here in terms of performance!


Solution

  • The I/O operations in rabbitmq are mainly for communication between rabbitmq client and rabbit mq broker.

    The callbacks ( BasicAcks, BasicNacks ) will use the I/O thread, so you may wonder why this can be a problem. Rabbitmq is designed for fast message transfer and non-blocking message transfer. When you perform heavy operations in the callback, these will happen on the I/O thread that rabbitmq uses, which will affect the performance and may even cause deadlocks ( if you perform heavy tasks in the I/O thread, communication might be interrupted ). Thus, the rabbitmq tutorial suggests using re-queuing strategy like ConcurrentQueue because it provides thread-safe communication between threads. It can hold nacked messages until they are re-published

    For handling this re-publishing, you can create a queue service and consume within this service,

    Microsoft official docs: https://learn.microsoft.com/en-us/dotnet/core/extensions/queue-service

    Hope it helps,