Skip to content

Commit 9d831f4

Browse files
Merge pull request #2172 from rabbitmq/direct-reply-to
Revise Direct Reply-To
2 parents 4eff274 + e990ff5 commit 9d831f4

File tree

11 files changed

+104
-59
lines changed

11 files changed

+104
-59
lines changed

docs/direct-reply-to.md

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,19 @@ limitations under the License.
2323
## Overview {#overview}
2424

2525
Direct reply-to is a feature that allows RPC (request/reply) clients with a design
26-
similar to that demonstrated in [tutorial 6](/tutorials) to avoid
27-
declaring a response queue per request.
26+
similar to that demonstrated in [tutorial 6](/tutorials) without requiring the creation
27+
of a reply queue.
2828

29-
### Motivation {#motivation}
29+
:::important
30+
31+
Request-reply implementations where clients use explicitly declared queues, both
32+
long-lived client named and connection-specific exclusive queues, are
33+
just as valid as Direct Reply-to, and have their benefits, in particular
34+
for workloads with long-running tasks
35+
36+
:::
37+
38+
## Motivation {#motivation}
3039

3140
RPC (request/reply) is a popular pattern to implement with a messaging broker
3241
like RabbitMQ. [Tutorial 6](/tutorials) demonstrates its implementation
@@ -38,22 +47,26 @@ header.
3847

3948
Where does the client's queue come from? The client can
4049
declare a single-use queue for each request-response pair. But
41-
this is inefficient; even a transient unreplicated queue can be
50+
this is inefficient; even an unreplicated queue can be
4251
expensive to create and then delete (compared with the cost of
4352
sending a message). This is especially true in a cluster as all
4453
cluster nodes need to agree that the queue has been created,
4554
agree on its type, replication parameters, and other metadata.
4655

47-
So alternatively the client can create a long-lived queue for
48-
its replies. But this can be fiddly to manage, especially if the
49-
client itself is not long-lived.
56+
Therefore, the client should create a single reply queue for multiple RPC requests.
57+
58+
The [properties](queues#properties) of this reply queue depend on the use case:
5059

51-
The direct reply-to feature allows RPC clients to receive
52-
replies directly from their RPC server, without going through a
53-
reply queue. "Directly" here still means going through the same connection
54-
and a RabbitMQ node; there is no direct network connection
55-
between RPC client and RPC server processes.
60+
* **[Exclusive](queues#exclusive-queues) queues** are commonly used when replies are consumed by a single client and deleted upon disconnection
61+
* **Non-exclusive long-lived queues** are better suited for long-running tasks, ensuring replies persist even if the client disconnects temporarily
5662

63+
Direct reply-to eliminates the need for a reply queue. This benefits the request-reply
64+
implementations with short-lived queues and transient responses at the cost
65+
of giving up all control over how the responses are stored.
66+
67+
With Direct Reply-to, RPC clients will receive replies directly from their RPC server,
68+
without going through a reply queue. "Directly" here still means going through the same channel
69+
and a RabbitMQ node; there is no direct network connection between RPC client and RPC server processes.
5770

5871
## How to Use Direct Reply-to {#usage}
5972

@@ -123,3 +136,16 @@ or fail.
123136
was set.
124137
</li>
125138
</ul>
139+
140+
## When to Use Direct Reply-to
141+
142+
While clients should use long lived connections, direct reply-to is ideal for workloads with
143+
[high connection churn](connections#high-connection-churn), where clients establish a connection
144+
for a single RPC and disconnect immediately after.
145+
By avoiding the creation of queue metadata in the [metadata store](metadata-store), direct
146+
reply-to can reduce overhead and latency.
147+
148+
For workloads with long-lived connections where clients perform multiple RPCs, the performance
149+
benefits of direct reply-to are not significant compared to [classic queues](classic-queues).
150+
Modern RabbitMQ versions have optimized classic queues for low latency and minimal resource usage,
151+
making them similarly efficient in such scenarios.

tutorials/tutorial-six-dotnet.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,8 @@ https://github.com/rabbitmq/rabbitmq-tutorials/blob/main/dotnet/RPCClient/RPCCli
103103

104104
### Correlation Id
105105

106-
In the method presented above we suggest creating a callback queue for
107-
every RPC request. That's pretty inefficient, but fortunately there is
108-
a better way - let's create a single callback queue per client.
106+
Creating a callback queue for every RPC request is inefficient.
107+
A better way is creating a single callback queue per client.
109108

110109
That raises a new issue, having received a response in that queue it's
111110
not clear to which request the response belongs. That's when the
@@ -131,7 +130,7 @@ gracefully, and the RPC should ideally be idempotent.
131130

132131
Our RPC will work like this:
133132

134-
* When the Client starts up, it creates an anonymous exclusive
133+
* When the Client starts up, it creates an exclusive
135134
callback queue.
136135
* For an RPC request, the Client sends a message with two properties:
137136
`ReplyTo`, which is set to the callback queue and `CorrelationId`,

tutorials/tutorial-six-elixir.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,8 @@ AMQP.Basic.publish(channel,
113113

114114
### Correlation id
115115

116-
In the method presented above we suggest creating a callback queue for
117-
every RPC request. That's pretty inefficient, but fortunately there is
118-
a better way - let's create a single callback queue per client.
116+
Creating a callback queue for every RPC request is inefficient.
117+
A better way is creating a single callback queue per client.
119118

120119
That raises a new issue, having received a response in that queue it's
121120
not clear to which request the response belongs. That's when the
@@ -141,7 +140,7 @@ gracefully, and the RPC should ideally be idempotent.
141140

142141
Our RPC will work like this:
143142

144-
* When the Client starts up, it creates an anonymous exclusive
143+
* When the Client starts up, it creates an exclusive
145144
callback queue.
146145
* For an RPC request, the Client sends a message with two properties:
147146
`reply_to`, which is set to the callback queue and `correlation_id`,

tutorials/tutorial-six-go.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,8 @@ err = ch.PublishWithContext(ctx,
114114
115115
### Correlation Id
116116

117-
In the method presented above we suggest creating a callback queue for
118-
every RPC request. That's pretty inefficient, but fortunately there is
119-
a better way - let's create a single callback queue per client.
117+
Creating a callback queue for every RPC request is inefficient.
118+
A better way is creating a single callback queue per client.
120119

121120
That raises a new issue, having received a response in that queue it's
122121
not clear to which request the response belongs. That's when the
@@ -142,7 +141,7 @@ gracefully, and the RPC should ideally be idempotent.
142141

143142
Our RPC will work like this:
144143

145-
* When the Client starts up, it creates an anonymous exclusive
144+
* When the Client starts up, it creates an exclusive
146145
callback queue.
147146
* For an RPC request, the Client sends a message with two properties:
148147
`reply_to`, which is set to the callback queue and `correlation_id`,

tutorials/tutorial-six-java.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,8 @@ import com.rabbitmq.client.AMQP.BasicProperties;
119119
120120
### Correlation Id
121121

122-
In the method presented above we suggest creating a callback queue for
123-
every RPC request. That's pretty inefficient, but fortunately there is
124-
a better way - let's create a single callback queue per client.
122+
Creating a callback queue for every RPC request is inefficient.
123+
A better way is creating a single callback queue per client.
125124

126125
That raises a new issue, having received a response in that queue it's
127126
not clear to which request the response belongs. That's when the
@@ -147,9 +146,10 @@ gracefully, and the RPC should ideally be idempotent.
147146

148147
Our RPC will work like this:
149148

149+
* When the Client starts up, it creates an exclusive
150+
callback queue.
150151
* For an RPC request, the Client sends a message with two properties:
151-
`replyTo`, which is set to an anonymous exclusive queue created
152-
just for the request, and `correlationId`,
152+
`reply_to`, which is set to the callback queue and `correlation_id`,
153153
which is set to a unique value for every request.
154154
* The request is sent to an `rpc_queue` queue.
155155
* The RPC worker (aka: server) is waiting for requests on that queue.

tutorials/tutorial-six-javascript.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ service that returns Fibonacci numbers.
6767
In general doing RPC over RabbitMQ is easy. A client sends a request
6868
message and a server replies with a response message. In order to
6969
receive a response we need to send a 'callback' queue address with the
70-
request. We can use the default exchange.
70+
request.
7171
Let's try it:
7272

7373
```javascript
@@ -99,9 +99,8 @@ channel.sendToQueue('rpc_queue', Buffer.from('10'), {
9999
100100
### Correlation Id
101101

102-
In the method presented above we suggest creating a callback queue for
103-
every RPC request. That's pretty inefficient, but fortunately there is
104-
a better way - let's create a single callback queue per client.
102+
Creating a callback queue for every RPC request is inefficient.
103+
A better way is creating a single callback queue per client.
105104

106105
That raises a new issue, having received a response in that queue it's
107106
not clear to which request the response belongs. That's when the
@@ -127,7 +126,7 @@ gracefully, and the RPC should ideally be idempotent.
127126

128127
Our RPC will work like this:
129128

130-
* When the Client starts up, it creates an anonymous exclusive
129+
* When the Client starts up, it creates an exclusive
131130
callback queue.
132131
* For an RPC request, the Client sends a message with two properties:
133132
`reply_to`, which is set to the callback queue and `correlation_id`,

tutorials/tutorial-six-php.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ $channel->basic_publish($msg, '', 'rpc_queue');
112112
113113
### Correlation Id
114114

115-
In the method presented above we suggest creating a callback queue for
116-
every RPC request. That's pretty inefficient, but fortunately there is
117-
a better way - let's create a single callback queue per client.
115+
Creating a callback queue for every RPC request is inefficient.
116+
A better way is creating a single callback queue per client.
118117

119118
That raises a new issue, having received a response in that queue it's
120119
not clear to which request the response belongs. That's when the
@@ -140,7 +139,7 @@ gracefully, and the RPC should ideally be idempotent.
140139

141140
Our RPC will work like this:
142141

143-
* When the Client starts up, it creates an anonymous exclusive
142+
* When the Client starts up, it creates an exclusive
144143
callback queue.
145144
* For an RPC request, the Client sends a message with two properties:
146145
`reply_to`, which is set to the callback queue and `correlation_id`,

tutorials/tutorial-six-python.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,8 @@ channel.basic_publish(exchange='',
120120

121121
### Correlation id
122122

123-
In the method presented above we suggest creating a callback queue for
124-
every RPC request. That's pretty inefficient, but fortunately there is
125-
a better way - let's create a single callback queue per client.
123+
Creating a callback queue for every RPC request is inefficient.
124+
A better way is creating a single callback queue per client.
126125

127126
That raises a new issue, having received a response in that queue it's
128127
not clear to which request the response belongs. That's when the
@@ -148,7 +147,7 @@ gracefully, and the RPC should ideally be idempotent.
148147

149148
Our RPC will work like this:
150149

151-
* When the Client starts up, it creates an anonymous exclusive
150+
* When the Client starts up, it creates an exclusive
152151
callback queue.
153152
* For an RPC request, the Client sends a message with two properties:
154153
`reply_to`, which is set to the callback queue and `correlation_id`,

tutorials/tutorial-six-ruby.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ exchange.publish(message, routing_key: 'rpc_queue', reply_to: queue.name)
112112
113113
### Correlation Id
114114

115-
In the method presented above we suggest creating a callback queue for
116-
every RPC request. That's pretty inefficient, but fortunately there is
117-
a better way - let's create a single callback queue per client.
115+
Creating a callback queue for every RPC request is inefficient.
116+
A better way is creating a single callback queue per client.
118117

119118
That raises a new issue, having received a response in that queue it's
120119
not clear to which request the response belongs. That's when the
@@ -140,7 +139,7 @@ gracefully, and the RPC should ideally be idempotent.
140139

141140
Our RPC will work like this:
142141

143-
* When the Client starts up, it creates an anonymous exclusive
142+
* When the Client starts up, it creates an exclusive
144143
callback queue.
145144
* For an RPC request, the Client sends a message with two properties:
146145
`:reply_to`, which is set to the callback queue and `:correlation_id`,

tutorials/tutorial-six-spring-amqp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ Spring AMQP allows you to focus on the message style you're working
107107
with and hide the details of message plumbing required to support
108108
this style. For example, typically the native client would
109109
create a callback queue for every RPC request. That's pretty
110-
inefficient so an alternative is to create a single callback
110+
inefficient, so an alternative is to create a single callback
111111
queue per client.
112112

113113
That raises a new issue, having received a response in that queue it's

0 commit comments

Comments
 (0)