Skip to content

Commit a17d66d

Browse files
committed
Revise Direct Reply-To
* Identify benefits of this feature * Improve `Motivation` section * Fix tutorial descriptions
1 parent 4eff274 commit a17d66d

10 files changed

+53
-48
lines changed

docs/direct-reply-to.md

+25-13
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ 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+
## Motivation {#motivation}
3030

3131
RPC (request/reply) is a popular pattern to implement with a messaging broker
3232
like RabbitMQ. [Tutorial 6](/tutorials) demonstrates its implementation
@@ -38,22 +38,21 @@ header.
3838

3939
Where does the client's queue come from? The client can
4040
declare a single-use queue for each request-response pair. But
41-
this is inefficient; even a transient unreplicated queue can be
41+
this is inefficient; even an unreplicated queue can be
4242
expensive to create and then delete (compared with the cost of
4343
sending a message). This is especially true in a cluster as all
4444
cluster nodes need to agree that the queue has been created,
4545
agree on its type, replication parameters, and other metadata.
46+
Therefore, the client should create a single reply queue for multiple RPC requests.
4647

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.
50-
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.
48+
The [properties](queues#properties) of this reply queue depend on the use case. For example:
49+
* **[Exclusive](queues#exclusive-queues) queues** are commonly used when replies are consumed by a single client and deleted upon disconnection.
50+
* **Non-exclusive queues** might be better suited for long-running tasks, ensuring replies persist even if the client disconnects temporarily.
5651

52+
Direct reply-to eliminates the need for a reply queue entirely.
53+
It allows RPC clients to receive replies directly from their RPC server,
54+
without going through a reply queue. "Directly" here still means going through the same connection
55+
and a RabbitMQ node; there is no direct network connection between RPC client and RPC server processes.
5756

5857
## How to Use Direct Reply-to {#usage}
5958

@@ -123,3 +122,16 @@ or fail.
123122
was set.
124123
</li>
125124
</ul>
125+
126+
## When to Use Direct Reply-to
127+
128+
While clients should use long lived connections, direct reply-to is ideal for workloads with
129+
[high connection churn](connections#high-connection-churn), where clients establish a connection
130+
for a single RPC and disconnect immediately after.
131+
By avoiding the creation of queue metadata in the [metadata store](metadata-store), direct
132+
reply-to can reduce overhead and latency.
133+
134+
For workloads with long-lived connections where clients perform multiple RPCs, the performance
135+
benefits of direct reply-to are not significant compared to [classic queues](classic-queues).
136+
Modern RabbitMQ versions have optimized classic queues for low latency and minimal resource usage,
137+
making them similarly efficient in such scenarios.

tutorials/tutorial-six-dotnet.md

+3-4
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

+3-4
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

+3-4
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

+5-5
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

+4-5
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

+3-4
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

+3-4
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

+3-4
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

+1-1
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)