Stackbook Logo
messagingestablished · medium operational burden

Request-Reply over Queues

Also known as: asynchronous request-reply, RPC over messaging

Intent

Get a response to a request using asynchronous queues, with a correlation id linking the reply to the original request.

Problem

A caller needs a result from a service that is slow, scaled independently or reachable only via messaging. Holding an HTTP connection or blocking a thread wastes resources and fails on timeouts.

Forces

  • •Responses must be matched to requests
  • •Callers cannot block indefinitely
  • •Replies may arrive late or never
  • •Reply channels need routing and cleanup

Solution

Overview

Request-reply layers a conversation on one-way messaging. Each side needs only the broker, not the other side address.

The correlation id is the essential piece. Without it a requester with many in-flight calls cannot tell which reply is which.

Decide how the caller experiences the wait. A synchronous wrapper blocks until the reply or timeout, which is convenient but ties up a thread. An asynchronous style returns a ticket at once and delivers the result by callback, polling endpoint or notification, which scales better for slow work.

How it works

The requester registers a pending entry under a fresh correlation id, publishes the request, and a reply listener completes the entry when a matching reply arrives.

req  = {id: "c-91", replyTo: "replies.svcA", body: ...}
send("pricing.requests", req)

# responder
send(req.replyTo, {id: req.id, result: ...})

# requester listener
pending.pop("c-91").complete(result)

On the responder side, check the expiry before starting, since a request that waited in a long queue may already be useless. For idempotency, key the response by correlation id so a redelivered request returns the same answer instead of redoing the work. Include an error reply shape so failures arrive as messages rather than as silent timeouts.

Pitfalls

  • Unbounded waits instead of timeouts with a clear failure path.
  • Creating a new temporary queue per request, which is expensive at volume; use one per instance.
  • Ignoring late replies instead of discarding them by correlation id.
  • Not setting message TTL, so dead requesters leave work behind.

Key takeaways

  • Correlation id plus reply-to address define the pattern.
  • Always set a timeout and a message expiry.
  • Prefer one reply queue per instance.
  • Handle late and duplicate replies gracefully.

✓ When to Use

  • •Work is slow and callers should not hold connections
  • •Services are decoupled behind a broker
  • •Multiple responders can share the load

✗ When Not to Use

  • •Low-latency synchronous calls are the norm
  • •Fire-and-forget events suffice
  • •Reply semantics need strong transactional guarantees

Pros

  • +Responders scale and fail independently
  • +Backpressure through queues
  • +Caller threads are not held

Cons

  • −More moving parts than a direct call
  • −Timeouts and late replies complicate logic
  • −Reply queue lifecycle management

Cost Profile

Infrastructure

Medium - broker and reply queues

Operational

Medium - queue cleanup and timeouts

Cognitive

Medium - correlation and timeout handling

Failure Modes

  • Late reply arrives after the requester gave up

  • Reply queues leak when requesters die

  • Responder processes an expired request wastefully

Real-World Examples

Alternatives

    Related Patterns

    • competing-consumers
    • dead-letter-queue

    Competency Domains

    distribution communicationreliability ops