Skip to content

Commit 1cb5551

Browse files
committed
direct reply to restart
Signed-off-by: Gabriele Santomaggio <[email protected]>
1 parent 7378ea1 commit 1cb5551

File tree

8 files changed

+116
-67
lines changed

8 files changed

+116
-67
lines changed

RabbitMQ.AMQP.Client/IRequester.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,14 @@ public interface IRequester : ILifeCycle
9797
/// <param name="cancellationToken">Cancellation token</param>
9898
/// <returns></returns>
9999
Task<IMessage> PublishAsync(IMessage message, CancellationToken cancellationToken = default);
100+
101+
/// <summary>
102+
/// The ReplyTo queue address can be created by:
103+
/// - the client providing a specific queue name
104+
/// - the client creating a temporary queue
105+
/// - The server uses this address to send the reply message. with direct-reply-to
106+
/// </summary>
107+
/// <returns></returns>
108+
public string ReplyToQueueAddress();
100109
}
101110
}

RabbitMQ.AMQP.Client/IResponder.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
namespace RabbitMQ.AMQP.Client
99
{
10-
1110
/// <summary>
1211
/// IRpcServerBuilder is the interface for creating an RPC server.
1312
/// The RPC server consumes requests from a queue and sends replies to a reply queue.
@@ -22,6 +21,7 @@ public interface IResponderBuilder
2221
/// <param name="requestQueue"></param>
2322
/// <returns></returns>
2423
IResponderBuilder RequestQueue(string requestQueue);
24+
2525
IResponderBuilder RequestQueue(IQueueSpecification requestQueue);
2626

2727
/// <summary>
@@ -32,7 +32,6 @@ public interface IResponderBuilder
3232
/// </summary>
3333
/// <param name="correlationIdExtractor"></param>
3434
/// <returns></returns>
35-
3635
IResponderBuilder CorrelationIdExtractor(Func<IMessage, object>? correlationIdExtractor);
3736

3837
/// <summary>
@@ -67,15 +66,14 @@ public interface IResponderBuilder
6766
public delegate Task<IMessage> RpcHandler(IResponder.IContext context, IMessage request);
6867

6968
/// <summary>
70-
/// IRpcServer interface for creating an RPC server.
69+
/// IResponder interface for creating an RPC server.
7170
/// The RPC is simulated by sending a request message and receiving a reply message.
7271
/// Where the client sends the queue where wants to receive the reply.
7372
/// RPC client ---> request queue ---> RPC server ---> reply queue ---> RPC client
7473
/// See also <seealso cref="IRequester"/>
7574
/// </summary>
7675
public interface IResponder : ILifeCycle
7776
{
78-
7977
public interface IContext
8078
{
8179
IMessage Message(byte[] body);

RabbitMQ.AMQP.Client/Impl/AmqpConsumer.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ private async Task ProcessMessages()
167167
stopwatch?.Restart();
168168

169169
// TODO the timeout waiting for messages should be configurable
170-
TimeSpan timeout = TimeSpan.FromSeconds(60);
170+
TimeSpan timeout = TimeSpan.FromSeconds(ConsumerDefaults.MessageReceiveTimeoutSeconds);
171171
Message? nativeMessage = await _receiverLink.ReceiveAsync(timeout)
172172
.ConfigureAwait(false);
173173

@@ -302,7 +302,7 @@ public override async Task CloseAsync()
302302
try
303303
{
304304
// TODO global timeout for closing, other async actions?
305-
await _receiverLink.CloseAsync(TimeSpan.FromSeconds(5))
305+
await _receiverLink.CloseAsync(TimeSpan.FromSeconds(ConsumerDefaults.CloseTimeoutSeconds))
306306
.ConfigureAwait(false);
307307
}
308308
catch (Exception ex)
@@ -318,8 +318,7 @@ await _receiverLink.CloseAsync(TimeSpan.FromSeconds(5))
318318

319319
public override string ToString()
320320
{
321-
string address = AddressBuilderHelper.AddressBuilder().Queue(_configuration.Queue).Address();
322-
return $"Consumer{{Address='{address}', " +
321+
return $"Consumer{{Address='{QueueAddress}', " +
323322
$"id={_id}, " +
324323
$"Connection='{_amqpConnection}', " +
325324
$"State='{State}'}}";

RabbitMQ.AMQP.Client/Impl/AmqpRequester.cs

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@
99

1010
namespace RabbitMQ.AMQP.Client.Impl
1111
{
12-
public class RpcClientConfiguration
12+
public class RequesterConfiguration
1313
{
1414
public AmqpConnection Connection { get; set; } = null!;
1515
public string ReplyToQueue { get; set; } = "";
1616

17-
internal string ReplyToQueueAddress { get; set; } = "";
18-
1917
public string RequestAddress { get; set; } = "";
2018
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(10);
2119

@@ -30,7 +28,7 @@ public class AmqpRequesterBuilder : IRequesterBuilder
3028
{
3129
private readonly RequesterAddressBuilder _addressBuilder;
3230
private readonly AmqpConnection _connection;
33-
private readonly RpcClientConfiguration _configuration = new();
31+
private readonly RequesterConfiguration _configuration = new();
3432

3533
public AmqpRequesterBuilder(AmqpConnection connection)
3634
{
@@ -101,7 +99,7 @@ public async Task<IRequester> BuildAsync()
10199
/// </summary>
102100
public class AmqpRequester : AbstractLifeCycle, IRequester
103101
{
104-
private readonly RpcClientConfiguration _configuration;
102+
private readonly RequesterConfiguration _configuration;
105103
private IConsumer? _consumer = null;
106104
private IPublisher? _publisher = null;
107105
private readonly ConcurrentDictionary<object, TaskCompletionSource<IMessage>> _pendingRequests = new();
@@ -137,19 +135,32 @@ private IMessage RequestPostProcess(IMessage request, object correlationId)
137135
return _configuration.RequestPostProcessor(request, correlationId);
138136
}
139137

140-
return request.ReplyTo(_configuration.ReplyToQueueAddress)
138+
string s = ReplyToQueueAddress();
139+
return request.ReplyTo(s)
141140
.MessageId(correlationId);
142141
}
143142

144-
public AmqpRequester(RpcClientConfiguration configuration)
143+
public AmqpRequester(RequesterConfiguration configuration)
145144
{
146145
_configuration = configuration;
147146
}
148147

148+
/// <summary>
149+
/// OpenAsync initializes the Requester by creating the necessary publisher and consumer.
150+
/// The DirectReplyTo feature is applied if supported by the server and no explicit reply-to queue is set.
151+
/// The AmqpRequester is an opinionated wrapper to simulate RPC over AMQP 1.0.
152+
/// Even when the DirectReplyTo is supported the wrapper can decide to don't use it when
153+
/// the user has explicitly set a reply-to queue.
154+
/// </summary>
155+
/// <returns></returns>
149156
public override async Task OpenAsync()
150157
{
151158
bool isDirectReplyToSupported = _configuration.Connection._featureFlags.IsDirectReplyToSupported;
159+
152160
string queueReplyTo = _configuration.ReplyToQueue;
161+
// if the queue is not set it means we need to create a temporary queue as reply-to
162+
// only if direct-reply-to is not supported. In case of isDirectReplyToSupported the server
163+
// will create the server side temporary queue.
153164
if (string.IsNullOrEmpty(_configuration.ReplyToQueue) && !isDirectReplyToSupported)
154165
{
155166
IQueueInfo queueInfo = await _configuration.Connection.Management().Queue().AutoDelete(true)
@@ -158,10 +169,19 @@ public override async Task OpenAsync()
158169
queueReplyTo = queueInfo.Name();
159170
}
160171

172+
// we can apply DirectReplyTo only if the _configuration.ReplyToQueue is not set
173+
// and the server support DirectReplyTo feature
174+
// AmqpRequester is a wrapper to simulate RPC over AMQP 1.0
175+
// canApplyDirectReplyTo is an opinionated optimization to avoid creating temporary queues
176+
// unless _configuration.ReplyToQueue is explicitly set by the user.
177+
// The user is always free to create custom Requester and Responder
178+
bool canApplyDirectReplyTo = isDirectReplyToSupported &&
179+
string.IsNullOrEmpty(_configuration.ReplyToQueue);
180+
161181
_publisher = await _configuration.Connection.PublisherBuilder().BuildAsync().ConfigureAwait(false);
162182
_consumer = await _configuration.Connection.ConsumerBuilder()
163183
.Queue(queueReplyTo)
164-
.DirectReplyTo(isDirectReplyToSupported)
184+
.DirectReplyTo(canApplyDirectReplyTo)
165185
.MessageHandler((context, message) =>
166186
{
167187
// TODO MessageHandler funcs should catch all exceptions
@@ -175,10 +195,6 @@ public override async Task OpenAsync()
175195
return Task.CompletedTask;
176196
}).BuildAndStartAsync().ConfigureAwait(false);
177197

178-
if (_consumer.QueueAddress is not null)
179-
{
180-
_configuration.ReplyToQueueAddress = _consumer.QueueAddress;
181-
}
182198
await base.OpenAsync().ConfigureAwait(false);
183199
}
184200

@@ -233,5 +249,16 @@ await _pendingRequests[correlationId].Task.WaitAsync(_configuration.Timeout)
233249
_semaphore.Release();
234250
}
235251
}
252+
253+
public string ReplyToQueueAddress()
254+
{
255+
if (_consumer == null)
256+
{
257+
throw new InvalidOperationException("Requester is not opened");
258+
}
259+
260+
return _consumer.QueueAddress ??
261+
throw new InvalidOperationException("ReplyToQueueAddress is not available");
262+
}
236263
}
237264
}

RabbitMQ.AMQP.Client/Impl/AmqpResponder.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace RabbitMQ.AMQP.Client.Impl
1010
{
11-
public class RpcConfiguration
11+
public class ResponderConfiguration
1212
{
1313
public AmqpConnection Connection { get; set; } = null!;
1414
public RpcHandler? Handler { get; set; }
@@ -22,7 +22,7 @@ public class RpcConfiguration
2222
/// </summary>
2323
public class AmqpResponderBuilder : IResponderBuilder
2424
{
25-
readonly RpcConfiguration _configuration = new();
25+
readonly ResponderConfiguration _configuration = new();
2626

2727
public AmqpResponderBuilder(AmqpConnection connection)
2828
{
@@ -73,7 +73,7 @@ public async Task<IResponder> BuildAsync()
7373
/// </summary>
7474
public class AmqpResponder : AbstractLifeCycle, IResponder
7575
{
76-
private readonly RpcConfiguration _configuration;
76+
private readonly ResponderConfiguration _configuration;
7777
private IPublisher? _publisher = null;
7878
private IConsumer? _consumer = null;
7979

@@ -107,7 +107,7 @@ private IMessage ReplyPostProcessor(IMessage reply, object correlationId)
107107
: reply.CorrelationId(correlationId);
108108
}
109109

110-
public AmqpResponder(RpcConfiguration configuration)
110+
public AmqpResponder(ResponderConfiguration configuration)
111111
{
112112
_configuration = configuration;
113113
}

RabbitMQ.AMQP.Client/PublicAPI.Unshipped.txt

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,9 @@ RabbitMQ.AMQP.Client.Impl.AmqpQuorumSpecification.Queue() -> RabbitMQ.AMQP.Clien
497497
RabbitMQ.AMQP.Client.Impl.AmqpQuorumSpecification.QuorumInitialGroupSize(int size) -> RabbitMQ.AMQP.Client.IQuorumQueueSpecification!
498498
RabbitMQ.AMQP.Client.Impl.AmqpQuorumSpecification.QuorumTargetGroupSize(int size) -> RabbitMQ.AMQP.Client.IQuorumQueueSpecification!
499499
RabbitMQ.AMQP.Client.Impl.AmqpRequester
500-
RabbitMQ.AMQP.Client.Impl.AmqpRequester.AmqpRequester(RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration! configuration) -> void
500+
RabbitMQ.AMQP.Client.Impl.AmqpRequester.AmqpRequester(RabbitMQ.AMQP.Client.Impl.RequesterConfiguration! configuration) -> void
501501
RabbitMQ.AMQP.Client.Impl.AmqpRequester.PublishAsync(RabbitMQ.AMQP.Client.IMessage! message, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<RabbitMQ.AMQP.Client.IMessage!>!
502+
RabbitMQ.AMQP.Client.Impl.AmqpRequester.ReplyToQueueAddress() -> string!
502503
RabbitMQ.AMQP.Client.Impl.AmqpRequesterBuilder
503504
RabbitMQ.AMQP.Client.Impl.AmqpRequesterBuilder.AmqpRequesterBuilder(RabbitMQ.AMQP.Client.Impl.AmqpConnection! connection) -> void
504505
RabbitMQ.AMQP.Client.Impl.AmqpRequesterBuilder.BuildAsync() -> System.Threading.Tasks.Task<RabbitMQ.AMQP.Client.IRequester!>!
@@ -510,7 +511,7 @@ RabbitMQ.AMQP.Client.Impl.AmqpRequesterBuilder.RequestAddress() -> RabbitMQ.AMQP
510511
RabbitMQ.AMQP.Client.Impl.AmqpRequesterBuilder.RequestPostProcessor(System.Func<RabbitMQ.AMQP.Client.IMessage!, object!, RabbitMQ.AMQP.Client.IMessage!>? requestPostProcessor) -> RabbitMQ.AMQP.Client.IRequesterBuilder!
511512
RabbitMQ.AMQP.Client.Impl.AmqpRequesterBuilder.Timeout(System.TimeSpan timeout) -> RabbitMQ.AMQP.Client.IRequesterBuilder!
512513
RabbitMQ.AMQP.Client.Impl.AmqpResponder
513-
RabbitMQ.AMQP.Client.Impl.AmqpResponder.AmqpResponder(RabbitMQ.AMQP.Client.Impl.RpcConfiguration! configuration) -> void
514+
RabbitMQ.AMQP.Client.Impl.AmqpResponder.AmqpResponder(RabbitMQ.AMQP.Client.Impl.ResponderConfiguration! configuration) -> void
514515
RabbitMQ.AMQP.Client.Impl.AmqpResponderBuilder
515516
RabbitMQ.AMQP.Client.Impl.AmqpResponderBuilder.AmqpResponderBuilder(RabbitMQ.AMQP.Client.Impl.AmqpConnection! connection) -> void
516517
RabbitMQ.AMQP.Client.Impl.AmqpResponderBuilder.BuildAsync() -> System.Threading.Tasks.Task<RabbitMQ.AMQP.Client.IResponder!>!
@@ -576,34 +577,34 @@ RabbitMQ.AMQP.Client.Impl.MessageAddressBuilder.MessageAddressBuilder(RabbitMQ.A
576577
RabbitMQ.AMQP.Client.Impl.RequesterAddressBuilder
577578
RabbitMQ.AMQP.Client.Impl.RequesterAddressBuilder.Requester() -> RabbitMQ.AMQP.Client.IRequesterBuilder!
578579
RabbitMQ.AMQP.Client.Impl.RequesterAddressBuilder.RequesterAddressBuilder(RabbitMQ.AMQP.Client.Impl.AmqpRequesterBuilder! builder) -> void
579-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration
580-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.Connection.get -> RabbitMQ.AMQP.Client.Impl.AmqpConnection!
581-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.Connection.set -> void
582-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.CorrelationIdExtractor.get -> System.Func<RabbitMQ.AMQP.Client.IMessage!, object!>?
583-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.CorrelationIdExtractor.set -> void
584-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.CorrelationIdSupplier.get -> System.Func<object!>?
585-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.CorrelationIdSupplier.set -> void
586-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.ReplyToQueue.get -> string!
587-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.ReplyToQueue.set -> void
588-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.RequestAddress.get -> string!
589-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.RequestAddress.set -> void
590-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.RequestPostProcessor.get -> System.Func<RabbitMQ.AMQP.Client.IMessage!, object!, RabbitMQ.AMQP.Client.IMessage!>?
591-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.RequestPostProcessor.set -> void
592-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.RpcClientConfiguration() -> void
593-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.Timeout.get -> System.TimeSpan
594-
RabbitMQ.AMQP.Client.Impl.RpcClientConfiguration.Timeout.set -> void
595-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration
596-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.Connection.get -> RabbitMQ.AMQP.Client.Impl.AmqpConnection!
597-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.Connection.set -> void
598-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.CorrelationIdExtractor.get -> System.Func<RabbitMQ.AMQP.Client.IMessage!, object!>?
599-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.CorrelationIdExtractor.set -> void
600-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.Handler.get -> RabbitMQ.AMQP.Client.RpcHandler?
601-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.Handler.set -> void
602-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.ReplyPostProcessor.get -> System.Func<RabbitMQ.AMQP.Client.IMessage!, object!, RabbitMQ.AMQP.Client.IMessage!>?
603-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.ReplyPostProcessor.set -> void
604-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.RequestQueue.get -> string!
605-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.RequestQueue.set -> void
606-
RabbitMQ.AMQP.Client.Impl.RpcConfiguration.RpcConfiguration() -> void
580+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration
581+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.Connection.get -> RabbitMQ.AMQP.Client.Impl.AmqpConnection!
582+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.Connection.set -> void
583+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.CorrelationIdExtractor.get -> System.Func<RabbitMQ.AMQP.Client.IMessage!, object!>?
584+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.CorrelationIdExtractor.set -> void
585+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.CorrelationIdSupplier.get -> System.Func<object!>?
586+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.CorrelationIdSupplier.set -> void
587+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.ReplyToQueue.get -> string!
588+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.ReplyToQueue.set -> void
589+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.RequestAddress.get -> string!
590+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.RequestAddress.set -> void
591+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.RequesterConfiguration() -> void
592+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.RequestPostProcessor.get -> System.Func<RabbitMQ.AMQP.Client.IMessage!, object!, RabbitMQ.AMQP.Client.IMessage!>?
593+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.RequestPostProcessor.set -> void
594+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.Timeout.get -> System.TimeSpan
595+
RabbitMQ.AMQP.Client.Impl.RequesterConfiguration.Timeout.set -> void
596+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration
597+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.Connection.get -> RabbitMQ.AMQP.Client.Impl.AmqpConnection!
598+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.Connection.set -> void
599+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.CorrelationIdExtractor.get -> System.Func<RabbitMQ.AMQP.Client.IMessage!, object!>?
600+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.CorrelationIdExtractor.set -> void
601+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.Handler.get -> RabbitMQ.AMQP.Client.RpcHandler?
602+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.Handler.set -> void
603+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.ReplyPostProcessor.get -> System.Func<RabbitMQ.AMQP.Client.IMessage!, object!, RabbitMQ.AMQP.Client.IMessage!>?
604+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.ReplyPostProcessor.set -> void
605+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.RequestQueue.get -> string!
606+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.RequestQueue.set -> void
607+
RabbitMQ.AMQP.Client.Impl.ResponderConfiguration.ResponderConfiguration() -> void
607608
RabbitMQ.AMQP.Client.Impl.StreamFilterOptions
608609
RabbitMQ.AMQP.Client.Impl.StreamFilterOptions.AbsoluteExpiryTime(System.DateTime absoluteExpiryTime) -> RabbitMQ.AMQP.Client.IConsumerBuilder.IStreamFilterOptions!
609610
RabbitMQ.AMQP.Client.Impl.StreamFilterOptions.ContentEncoding(string! contentEncoding) -> RabbitMQ.AMQP.Client.IConsumerBuilder.IStreamFilterOptions!
@@ -694,6 +695,7 @@ RabbitMQ.AMQP.Client.IRecoveryConfiguration.IsTopologyActive() -> bool
694695
RabbitMQ.AMQP.Client.IRecoveryConfiguration.Topology(bool activated) -> RabbitMQ.AMQP.Client.IRecoveryConfiguration!
695696
RabbitMQ.AMQP.Client.IRequester
696697
RabbitMQ.AMQP.Client.IRequester.PublishAsync(RabbitMQ.AMQP.Client.IMessage! message, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<RabbitMQ.AMQP.Client.IMessage!>!
698+
RabbitMQ.AMQP.Client.IRequester.ReplyToQueueAddress() -> string!
697699
RabbitMQ.AMQP.Client.IRequesterAddressBuilder
698700
RabbitMQ.AMQP.Client.IRequesterAddressBuilder.Requester() -> RabbitMQ.AMQP.Client.IRequesterBuilder!
699701
RabbitMQ.AMQP.Client.IRequesterBuilder

0 commit comments

Comments
 (0)