public static async Task <IQueue> GetDelayQueueAsync(this IAdvancedBus advancedBus, string queue) { // ReSharper disable once InconsistentlySynchronizedField if (DelayQueues.TryGetValue(queue, out var result)) { return(result); } var lockObject = DelayQueueLocks.GetOrAdd(queue, key => new AsyncLock()); using (await lockObject.AcquireAsync()) { // ReSharper disable once InconsistentlySynchronizedField if (DelayQueues.TryGetValue(queue, out result)) { return(result); } result = await advancedBus.QueueDeclareAsync(GetDelayQueueName(queue), deadLetterExchange : "", deadLetterRoutingKey : queue ); lock (DelayQueues) { DelayQueues.Add(queue, result); } return(result); } }
public Task FuturePublishAsync <T>(TimeSpan messageDelay, T message) where T : class { Preconditions.CheckNotNull(message, "message"); Preconditions.CheckLess(messageDelay, MaxMessageDelay, "messageDelay"); var delay = Round(messageDelay); var delayString = delay.ToString(@"dd\_hh\_mm\_ss"); var exchangeName = conventions.ExchangeNamingConvention(typeof(T)); var futureExchangeName = exchangeName + "_" + delayString; var futureQueueName = conventions.QueueNamingConvention(typeof(T), delayString); return(advancedBus.ExchangeDeclareAsync(futureExchangeName, ExchangeType.Topic) .Then(futureExchange => advancedBus.QueueDeclareAsync(futureQueueName, perQueueTtl: (int)delay.TotalMilliseconds, deadLetterExchange: exchangeName) .Then(futureQueue => advancedBus.BindAsync(futureExchange, futureQueue, "#")) .Then(() => { var easyNetQMessage = new Message <T>(message) { Properties = { DeliveryMode = (byte)(messageDeliveryModeStrategy.IsPersistent(typeof(T)) ? 2 : 1) } }; return advancedBus.PublishAsync(futureExchange, "#", false, false, easyNetQMessage); }))); }
private async Task FuturePublishInternalAsync <T>(TimeSpan messageDelay, T message, string cancellationKey) where T : class { Preconditions.CheckNotNull(message, "message"); Preconditions.CheckLess(messageDelay, MaxMessageDelay, "messageDelay"); Preconditions.CheckNull(cancellationKey, "cancellationKey"); var delay = Round(messageDelay); var delayString = delay.ToString(@"dd\_hh\_mm\_ss"); var exchangeName = conventions.ExchangeNamingConvention(typeof(T)); var futureExchangeName = exchangeName + "_" + delayString; var futureQueueName = conventions.QueueNamingConvention(typeof(T), delayString); var futureExchange = await advancedBus.ExchangeDeclareAsync(futureExchangeName, ExchangeType.Topic).ConfigureAwait(false); var futureQueue = await advancedBus.QueueDeclareAsync(futureQueueName, perQueueMessageTtl : (int)delay.TotalMilliseconds, deadLetterExchange : exchangeName).ConfigureAwait(false); await advancedBus.BindAsync(futureExchange, futureQueue, "#").ConfigureAwait(false); var easyNetQMessage = new Message <T>(message) { Properties = { DeliveryMode = messageDeliveryModeStrategy.GetDeliveryMode(typeof(T)) } }; await advancedBus.PublishAsync(futureExchange, "#", false, false, easyNetQMessage).ConfigureAwait(false); }
public static async Task <IQueue> DeclareMessageBusAsync(this IAdvancedBus advancedBus, MessageBus messageBus) { if (messageBus.DeadLetterMessageBus == null) { return(await advancedBus.QueueDeclareAsync(messageBus.Queue)); } else { await advancedBus.DeclareMessageBusAsync(messageBus.DeadLetterMessageBus); return(await advancedBus.QueueDeclareAsync(messageBus.Queue, deadLetterExchange : "", deadLetterRoutingKey : messageBus.DeadLetterMessageBus.Queue )); } }
/// <summary> /// Creates an exchange on the message broker. If the exchange is the default exchange /// with a defined queue, the queue is created on the RabbitMQ default exchange. /// </summary> /// <param name="bus">Reference to the advanced bus.</param> /// <param name="meta">The metadata describing the exchange.</param> /// <returns>Reference to the created exchange.</returns> public static async Task <IExchange> ExchangeDeclareAsync(this IAdvancedBus bus, ExchangeMeta meta) { if (bus == null) { throw new ArgumentNullException(nameof(bus)); } if (meta == null) { throw new ArgumentNullException(nameof(meta)); } // If the queue meta is specified on the exchange meta, this indicates that // a queue should be created on the default exchange. // https://www.rabbitmq.com/tutorials/amqp-concepts.html#exchange-default if (meta.QueueMeta != null) { await bus.QueueDeclareAsync(meta.QueueMeta.ScopedQueueName, durable : meta.QueueMeta.IsDurable, autoDelete : meta.QueueMeta.IsAutoDelete, exclusive : meta.QueueMeta.IsExclusive, passive : meta.QueueMeta.IsPassive, maxPriority : meta.QueueMeta.MaxPriority, deadLetterExchange : meta.QueueMeta.DeadLetterExchange, deadLetterRoutingKey : meta.QueueMeta.DeadLetterRoutingKey, perQueueMessageTtl : meta.QueueMeta.PerQueueMessageTtl).ConfigureAwait(false); return(Exchange.GetDefault()); } return(await bus.ExchangeDeclareAsync(meta.ExchangeName, meta.ExchangeType, durable : meta.IsDurable, autoDelete : meta.IsAutoDelete, passive : meta.IsPassive, alternateExchange : meta.AlternateExchangeName).ConfigureAwait(false)); }
private async Task FuturePublishInternalAsync <T>(TimeSpan messageDelay, T message, string cancellationKey = null) where T : class { Preconditions.CheckNotNull(message, "message"); Preconditions.CheckLess(messageDelay, MaxMessageDelay, "messageDelay"); Preconditions.CheckNull(cancellationKey, "cancellationKey"); var exchangeName = conventions.ExchangeNamingConvention(typeof(T)); var futureExchangeName = exchangeName + "_delayed"; var queueName = conventions.QueueNamingConvention(typeof(T), null); var futureExchange = await advancedBus.ExchangeDeclareAsync(futureExchangeName, ExchangeType.Direct, delayed : true).ConfigureAwait(false); var exchange = await advancedBus.ExchangeDeclareAsync(exchangeName, ExchangeType.Topic).ConfigureAwait(false); await advancedBus.BindAsync(futureExchange, exchange, "#").ConfigureAwait(false); var queue = await advancedBus.QueueDeclareAsync(queueName).ConfigureAwait(false); await advancedBus.BindAsync(exchange, queue, "#").ConfigureAwait(false); var easyNetQMessage = new Message <T>(message) { Properties = { DeliveryMode = messageDeliveryModeStrategy.GetDeliveryMode(typeof(T)), Headers = new Dictionary <string, object>{ { "x-delay", (int)messageDelay.TotalMilliseconds } } } }; await advancedBus.PublishAsync(futureExchange, "#", false, false, easyNetQMessage).ConfigureAwait(false); }
public async Task <IDisposable> AddSubscription <TEvent>(string sourceBoundedContextName, string publishedLanguageEntity) where TEvent : class { var queue = await _messageBus.QueueDeclareAsync(NamingConventions.QueueNamingConvention(_boundedContextName, sourceBoundedContextName, publishedLanguageEntity, _subscriberId)); await _messageBus.BindAsync( new Exchange( NamingConventions.ExchangeNamingConvention(sourceBoundedContextName, publishedLanguageEntity)), queue, ""); return(_messageBus.Consume(queue, async(bytes, properties, info) => { var msg = Encoding.UTF8.GetString(bytes); Console.WriteLine(msg); var validationResult = await _schemaValidator.IsValid <TEvent>(msg); if (validationResult.IsValid) { var envelope = System.Text.Json.JsonSerializer.Deserialize <Envelope <TEvent> >(msg); var props = new MessageProperties(); properties.CopyTo(props); await _eventDispatcher.HandleEvent(envelope, props); } else { throw new Exception($"Schema is invalid, errors: {string.Join(", ", validationResult.Errors)}"); } })); }
/// <inheritdoc /> public async Task FuturePublishAsync <T>( T message, TimeSpan delay, Action <IFuturePublishConfiguration> configure, CancellationToken cancellationToken = default ) { Preconditions.CheckNotNull(message, "message"); Preconditions.CheckNotNull(configure, "configure"); using var cts = cancellationToken.WithTimeout(configuration.Timeout); var publishConfiguration = new FuturePublishConfiguration(conventions.TopicNamingConvention(typeof(T))); configure(publishConfiguration); var topic = publishConfiguration.Topic; var exchange = await exchangeDeclareStrategy.DeclareExchangeAsync( conventions.ExchangeNamingConvention(typeof(T)), ExchangeType.Topic, cts.Token ).ConfigureAwait(false); var delayString = delay.ToString(@"dd\_hh\_mm\_ss"); var futureExchange = await exchangeDeclareStrategy.DeclareExchangeAsync( $"{conventions.ExchangeNamingConvention(typeof(T))}_{delayString}", ExchangeType.Topic, cts.Token ).ConfigureAwait(false); var futureQueue = await advancedBus.QueueDeclareAsync( conventions.QueueNamingConvention(typeof(T), delayString), c => { c.WithMessageTtl(delay); c.WithDeadLetterExchange(exchange); if (setDeadLetterRoutingKey) { c.WithDeadLetterRoutingKey(topic); } }, cts.Token ).ConfigureAwait(false); await advancedBus.BindAsync(futureExchange, futureQueue, topic, cts.Token).ConfigureAwait(false); var properties = new MessageProperties(); if (publishConfiguration.Priority != null) { properties.Priority = publishConfiguration.Priority.Value; } properties.DeliveryMode = messageDeliveryModeStrategy.GetDeliveryMode(typeof(T)); var advancedMessage = new Message <T>(message, properties); await advancedBus.PublishAsync( futureExchange, topic, configuration.MandatoryPublish, advancedMessage, cts.Token ).ConfigureAwait(false); }
/// <summary> /// Declare a transient server named queue. Note, this queue will only last for duration of the /// connection. If there is a connection outage, EasyNetQ will not attempt to recreate /// consumers. /// </summary> /// <param name="bus">The bus instance</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>The queue</returns> public static IQueue QueueDeclare(this IAdvancedBus bus, CancellationToken cancellationToken = default) { Preconditions.CheckNotNull(bus, "bus"); return(bus.QueueDeclareAsync(cancellationToken) .GetAwaiter() .GetResult()); }
/// <summary> /// Creates a queue and its associated exchange on the message broker. If the associated /// exchange is the RabbitMQ default exchange, only the queue is created. For a non-default /// exchange, the queue is bound to the exchange. If the metadata has route-keys specified, /// the queue is bound the to exchange for each specified key. /// </summary> /// <param name="bus">Reference to the advanced bus.</param> /// <param name="meta">The metadata describing the queue.</param> /// <returns>Reference to the created queue.</returns> public static async Task <IQueue> QueueDeclare(this IAdvancedBus bus, QueueMeta meta) { if (bus == null) { throw new ArgumentNullException(nameof(bus)); } if (meta == null) { throw new ArgumentNullException(nameof(meta)); } IExchange exchange = Exchange.GetDefault(); // Assume default exchange. ExchangeMeta exchangeMeta = meta.Exchange; if (!meta.Exchange.IsDefaultExchange) { exchange = await bus.ExchangeDeclareAsync(exchangeMeta.ExchangeName, exchangeMeta.ExchangeType, durable : exchangeMeta.IsDurable, autoDelete : exchangeMeta.IsAutoDelete, passive : exchangeMeta.IsPassive, alternateExchange : exchangeMeta.AlternateExchangeName); } IQueue queue = await bus.QueueDeclareAsync(meta.ScopedQueueName, durable : meta.IsDurable, autoDelete : meta.IsAutoDelete, exclusive : meta.IsExclusive, passive : meta.IsPassive, maxPriority : meta.MaxPriority, deadLetterExchange : meta.DeadLetterExchange, deadLetterRoutingKey : meta.DeadLetterRoutingKey, perQueueMessageTtl : meta.PerQueueMessageTtl); // Queues defined on the default exchange so don't bind. if (exchangeMeta.IsDefaultExchange) { return(queue); } string[] routeKeys = meta.RouteKeys ?? new string[] { }; if (routeKeys.Length > 0) { // If route-keys specified, bind the queue to the exchange // for each route-key. foreach (string routeKey in meta.RouteKeys ?? new string[] {}) { bus.Bind(exchange, queue, routeKey); } } else { await bus.BindAsync(exchange, queue, string.Empty); } return(queue); }
/// <summary> /// Declare a queue. If the queue already exists this method does nothing /// </summary> /// <param name="bus">The bus instance</param> /// <param name="name">The name of the queue</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns> /// The queue /// </returns> public static Task <IQueue> QueueDeclareAsync( this IAdvancedBus bus, string name, CancellationToken cancellationToken = default ) { Preconditions.CheckNotNull(bus, "bus"); return(bus.QueueDeclareAsync(name, c => { }, cancellationToken)); }
/// <summary> /// Declare a queue. If the queue already exists this method does nothing /// </summary> /// <param name="bus">The bus instance</param> /// <param name="name">The name of the queue</param> /// <param name="configure">Delegate to configure the queue</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns> /// The queue /// </returns> public static IQueue QueueDeclare( this IAdvancedBus bus, string name, Action <IQueueDeclareConfiguration> configure, CancellationToken cancellationToken = default ) { Preconditions.CheckNotNull(bus, "bus"); return(bus.QueueDeclareAsync(name, configure, cancellationToken) .GetAwaiter() .GetResult()); }
/// <summary> /// 创建消息队列 /// </summary> /// <param name="adbus"></param> /// <param name="queueName"></param> /// <returns></returns> private async Task <IQueue> CreateQueue <T>(IAdvancedBus adbus) { var mqInfo = this.GetMqInfo <T>(); if (adbus == null) { return(null); } if (string.IsNullOrEmpty(mqInfo.QueueName)) { return(adbus.QueueDeclare()); } return(await adbus.QueueDeclareAsync(mqInfo.QueueName)); }
/// <summary> /// Declare a queue. If the queue already exists this method does nothing /// </summary> /// <param name="bus">The bus instance</param> /// <param name="name">The name of the queue</param> /// <param name="durable">Durable queues remain active when a server restarts.</param> /// <param name="exclusive">Exclusive queues may only be accessed by the current connection, and are deleted when that connection closes.</param> /// <param name="autoDelete">If set, the queue is deleted when all consumers have finished using it.</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns> /// The queue /// </returns> public static IQueue QueueDeclare( this IAdvancedBus bus, string name, bool durable, bool exclusive, bool autoDelete, CancellationToken cancellationToken = default ) { Preconditions.CheckNotNull(bus, "bus"); return(bus.QueueDeclareAsync(name, durable, exclusive, autoDelete, cancellationToken) .GetAwaiter() .GetResult()); }
private async Task <string> SubscribeToReplyAsync(CancellationToken cancellationToken) { if (_replyQueue != null) { return(_replyQueue); } using (await _replyQueueLock.AcquireAsync(cancellationToken)) { if (_replyQueue != null) { return(_replyQueue); } var replyQueue = $"toxon.micro.rpc.reply.{Guid.NewGuid()}"; var exchange = await _bus.ExchangeDeclareAsync(ReplyExchangeName, ExchangeType.Direct); var queue = await _bus.QueueDeclareAsync(replyQueue); await _bus.BindAsync(exchange, queue, replyQueue); _bus.Consume(queue, (body, props, info) => { var correlationIdString = props.CorrelationId; if (Guid.TryParse(correlationIdString, out var correlationId) && _responseHandlers.TryRemove(correlationId, out var tcs)) { tcs.SetResult(MessageHelpers.FromArgs(body, props)); } }); _replyQueue = replyQueue; return(_replyQueue); } }
/// <summary> /// Declare a queue. If the queue already exists this method does nothing /// </summary> /// <param name="bus">The bus instance</param> /// <param name="name">The name of the queue</param> /// <param name="durable">Durable queues remain active when a server restarts.</param> /// <param name="exclusive">Exclusive queues may only be accessed by the current connection, and are deleted when that connection closes.</param> /// <param name="autoDelete">If set, the queue is deleted when all consumers have finished using it.</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns> /// The queue /// </returns> public static Task <IQueue> QueueDeclareAsync( this IAdvancedBus bus, string name, bool durable, bool exclusive, bool autoDelete, CancellationToken cancellationToken = default ) { Preconditions.CheckNotNull(bus, "bus"); return(bus.QueueDeclareAsync( name, c => c.AsDurable(durable) .AsExclusive(exclusive) .AsAutoDelete(autoDelete), cancellationToken )); }
public async Task DeclareAsync(CancellationToken cancellationToken) { _logger.LogInformation($"Entering {nameof(DeclareAsync)}"); try { var exchange = await _advancedBus.ExchangeDeclareAsync(name : _exchanges.ExchangeKey, type : ExchangeType.Direct, cancellationToken : cancellationToken).ConfigureAwait(false); var queue = await _advancedBus.QueueDeclareAsync(name : _queues.QueueKey, durable : true, exclusive : false, autoDelete : false, cancellationToken : cancellationToken).ConfigureAwait(false); await _advancedBus.BindAsync(exchange : exchange, queue : queue, routingKey : _routings.RoutingKey, headers : null, cancellationToken : cancellationToken).ConfigureAwait(false); _logger.LogInformation($"Bind - Exchange: {_exchanges.ExchangeKey}, Queue: {_queues.QueueKey}, RoutingKey: {_routings.RoutingKey}"); } catch (Exception ex) { _logger.LogError(ex, ex.Message); throw; } }
public async Task SubscribeAsync(Func <string, CancellationToken, Task> processMessageAsync, CancellationToken cancellationToken) { _logger.LogInformation($"Entering {nameof(SubscribeAsync)}"); try { var exchange = await _advancedBus.ExchangeDeclareAsync(name : _exchanges.ExchangeKey, type : ExchangeType.Direct, cancellationToken : cancellationToken).ConfigureAwait(false); var queue = await _advancedBus.QueueDeclareAsync(name : _queues.QueueKey, durable : true, exclusive : false, autoDelete : false, cancellationToken : cancellationToken).ConfigureAwait(false); await _advancedBus.BindAsync(exchange : exchange, queue : queue, routingKey : _routings.RoutingKey, headers : null, cancellationToken : cancellationToken).ConfigureAwait(false); _advancedBus.Consume(queue, (body, _, _) => Task.Factory.StartNew(async() => { var message = Encoding.UTF8.GetString(body); await processMessageAsync(message, cancellationToken).ConfigureAwait(false); }, cancellationToken)); } catch (Exception ex) { _logger.LogError(ex, ex.Message); throw; } }
public static async Task ResendErrorsAsync(this IAdvancedBus source, string errorQueueName) { var errorQueue = await source.QueueDeclareAsync(errorQueueName); var message = await source.GetMessageAsync(errorQueue); while (message != null) { var utf8Body = Encoding.UTF8.GetString(message.Body); var error = JsonConvert.DeserializeObject <Error>(utf8Body); var errorBodyBytes = Encoding.UTF8.GetBytes(error.Message); var exchange = await source.ExchangeDeclareAsync(error.Exchange, x => { // This can be adjusted to fit the exchange actual configuration x.AsDurable(true); x.AsAutoDelete(false); x.WithType("topic"); }); await source.PublishAsync(exchange, error.RoutingKey, true, error.BasicProperties, errorBodyBytes); message = await source.GetMessageAsync(errorQueue); } }
private async Task <EasyNetQ.Topology.IQueue> InitializeGetQueuesAsync() { _taskQueue ??= await _advancedBus.QueueDeclareAsync(_queueName, maxPriority : byte.MaxValue); return(_taskQueue); }
private async Task <ISubscriber> CreateSubscriberAsync(string name, bool temporary, Action temporaryQueueDisconnected, IEnumerable <string> routings, int retrycount, int prefetchcount, bool explicitAcknowledgments, ISubscriptionSelector subscriptionSelector, bool storedeadletter) { if (string.IsNullOrEmpty(name)) { throw new ArgumentException("name argument can not be null or empty"); } if ((routings != null) && (routings.Any(routing => string.IsNullOrEmpty(routing)) || !routings.Any())) { throw new ArgumentException("routings argument can not be empty or contain empty strings"); } if (retrycount < 0) { throw new ArgumentException("retrycount argument can not be less than zero"); } if (temporaryQueueDisconnected != null) { _broker.RegisterDisconnectedAction(temporaryQueueDisconnected); } //Main subscriber queue and bindings var queue = await _bus.QueueDeclareAsync( _environmentNamingConventionController.GetQueueName(name), false, true, temporary); if (routings != null) { foreach (var routing in routings) { await _bus.BindAsync(await _exchange, queue, routing); } } //Retry subscriber queue and bindings for (int i = 1; i <= retrycount; i++) { var retryqueueroutingkey = _environmentNamingConventionController.GetRetryRoutingKey(name, i); var retryqueuequeuename = _environmentNamingConventionController.GetRetryQueueName(name, i); var queueretrybinding = await _bus.BindAsync(await _exchange, queue, retryqueueroutingkey); var retryqueue = await _bus.QueueDeclareAsync(retryqueuequeuename, false, true, temporary, false, _retryfactor *i, null, null, (await _exchange).Name); var retryqueuebinding = await _bus.BindAsync(_easyNetQPublisherToWaitExchange.Exchange, retryqueue, retryqueueroutingkey); } //Dead letter subscriber queue and bindings if (storedeadletter) { var deadletterqueueroutingkey = _environmentNamingConventionController .GetDeadLetterQueueRoutingKey(name); var deadletterqueuequeuename = _environmentNamingConventionController. GetDeadLetterQueueName(name); var deadletterqueue = await _bus.QueueDeclareAsync(deadletterqueuequeuename, false, true, temporary); var deadletterqueuebinding = await _bus.BindAsync(_easyNetQPublisherToWaitExchange.Exchange, deadletterqueue, deadletterqueueroutingkey); } //Direct Routing Key binding var directroutingkey = _environmentNamingConventionController.GetDirectRoutingKey(name); var queuedirectroutingkeybinding = await _bus.BindAsync(await _exchange, queue, directroutingkey); if (_subscriberController.RegisterSubscriberName(name, out IDisposable nameHandle)) { var executionHandler = new ExecutionHandlingStrategy(name, _easyNetQPublisherToWaitExchange, retrycount, _environmentNamingConventionController, explicitAcknowledgments, _publisher, storedeadletter); return(new EasyNetQSubscriber(executionHandler, _bus, queue, (ushort)prefetchcount, subscriptionSelector, executionHandler, nameHandle)); } else { throw new SubscriberAlreadyExistsException($"The subscriber " + $"with name {name}" + $" already exists"); } }
private async Task <ISubscriptionResult> SubscribeAsyncInternal <T>( string subscriptionId, Func <T, CancellationToken, Task> onMessage, Action <ISubscriptionConfiguration> configure, CancellationToken cancellationToken ) { using var cts = cancellationToken.WithTimeout(configuration.Timeout); var subscriptionConfiguration = new SubscriptionConfiguration(configuration.PrefetchCount); configure(subscriptionConfiguration); var queueName = subscriptionConfiguration.QueueName ?? conventions.QueueNamingConvention(typeof(T), subscriptionId); var exchangeName = conventions.ExchangeNamingConvention(typeof(T)); var queue = await advancedBus.QueueDeclareAsync( queueName, c => { c.AsDurable(subscriptionConfiguration.Durable); c.AsAutoDelete(subscriptionConfiguration.AutoDelete); if (subscriptionConfiguration.Expires.HasValue) { c.WithExpires(TimeSpan.FromMilliseconds(subscriptionConfiguration.Expires.Value)); } if (subscriptionConfiguration.MaxPriority.HasValue) { c.WithMaxPriority(subscriptionConfiguration.MaxPriority.Value); } if (subscriptionConfiguration.MaxLength.HasValue) { c.WithMaxLength(subscriptionConfiguration.MaxLength.Value); } if (subscriptionConfiguration.MaxLengthBytes.HasValue) { c.WithMaxLengthBytes(subscriptionConfiguration.MaxLengthBytes.Value); } if (!string.IsNullOrEmpty(subscriptionConfiguration.QueueMode)) { c.WithQueueMode(subscriptionConfiguration.QueueMode); } }, cts.Token ).ConfigureAwait(false); var exchange = await advancedBus.ExchangeDeclareAsync(exchangeName, ExchangeType.Topic, cancellationToken : cts.Token).ConfigureAwait(false); foreach (var topic in subscriptionConfiguration.Topics.DefaultIfEmpty("#")) { await advancedBus.BindAsync(exchange, queue, topic, cts.Token).ConfigureAwait(false); } var consumerCancellation = advancedBus.Consume <T>( queue, (message, messageReceivedInfo) => onMessage(message.Body, default), x => x.WithPriority(subscriptionConfiguration.Priority) .WithPrefetchCount(subscriptionConfiguration.PrefetchCount) .WithExclusive(subscriptionConfiguration.IsExclusive) ); return(new SubscriptionResult(exchange, queue, consumerCancellation)); }