Skip to content

RPC over RabbitMQ

Messaging is fire-and-forget by nature — but sometimes you want a reply. RabbitMQ supports a request/reply (RPC) pattern using two message properties you met earlier:

  • reply_to — the client creates a callback queue and puts its name here, telling the server “send the answer to this queue.”
  • correlation_id — a unique id the client attaches to each request and expects echoed back, so it can match a reply to the request that produced it (replies may arrive out of order).
sequenceDiagram
  participant C as Client
  participant RQ as rpc_queue
  participant S as Server
  participant CB as callback queue
  C->>RQ: request (reply_to=cb, correlation_id=abc)
  RQ->>S: deliver
  S->>S: compute result
  S->>CB: reply (correlation_id=abc)
  CB->>C: deliver
  Note over C: match abc to the pending request
An RPC round-trip matched by correlation_id

The server consumes from a well-known request queue, does the work, and publishes the result to the request’s reply_to queue, echoing back the correlation_id. The client publishes a request with a fresh correlation_id and a reply_to, then waits on its callback queue for a reply carrying that same id.

// Server
await channel.assertQueue('rpc_queue', { durable: false });
await channel.prefetch(1);
channel.consume('rpc_queue', (msg) => {
const n = parseInt(msg.content.toString(), 10);
const result = Buffer.from(String(fib(n)));
channel.sendToQueue(msg.properties.replyTo, result, {
correlationId: msg.properties.correlationId,
});
channel.ack(msg);
});
// Client (sketch)
const { queue: cbQueue } = await channel.assertQueue('', { exclusive: true });
const correlationId = randomUUID();
channel.consume(cbQueue, (msg) => {
if (msg.properties.correlationId === correlationId) {
console.log('result:', msg.content.toString());
}
}, { noAck: true });
channel.sendToQueue('rpc_queue', Buffer.from('30'), {
correlationId, replyTo: cbQueue,
});

Be honest with yourself before reaching for RPC-over-RabbitMQ. You are rebuilding a synchronous call on top of asynchronous infrastructure — with a callback queue, correlation bookkeeping, and your own timeout handling — to get something a plain HTTP or gRPC call gives you for free. For most request/reply needs, a direct call is simpler, easier to debug, and lower latency.

RPC-over-messaging earns its keep when you specifically want what the broker adds: load-balancing requests across a worker pool (many RPC servers competing on rpc_queue), location transparency (the client doesn’t know or care which worker answers), or buffering requests when workers are momentarily busy. If you don’t need those, don’t build it.

What is the role of correlation_id in RPC over RabbitMQ?
What does the reply_to property tell the server?
When is RPC over RabbitMQ genuinely worth it instead of a direct HTTP/gRPC call?