private Task SendMessageOnConversation(SqlConnection connection, SqlTransaction transaction, OutboundBrokeredMessage brokeredMessage, string messageTypeName, Guid newConvHandle) { byte[] message = brokeredMessage.Body; if (messageTypeName == ServicesMessageTypes.ChatterBrokeredMessageType) { var bodyConverter = _bodyConverterFactory.CreateBodyConverter(_options.MessageBodyType); message = bodyConverter.Convert(brokeredMessage); } return(new SendOnConversationCommand(connection, newConvHandle, message, transaction, _options.CompressMessageBody, messageTypeName).ExecuteAsync()); }
public async Task Process(OutboxMessage message) { var appProps = JsonConvert.DeserializeObject <IDictionary <string, object> >(message.StringifiedApplicationProperties); appProps.TryGetValue(ApplicationProperties.ContentType, out var contentType); var outbound = message.AsOutboundBrokeredMessage(appProps, _bodyConverterFactory.CreateBodyConverter((string)contentType)); _logger.LogTrace($"Processing message '{message.MessageId}' from outbox."); await _brokeredMessageInfrastructureDispatcher.Dispatch(outbound, null); _logger.LogTrace($"Message '{message.MessageId}' dispatched to messaging infrastructure from outbox."); await _brokeredMessageOutbox.UpdateProcessedDate(message); }
public async Task Process(OutboxMessage message, CancellationToken cancellationToken = default) { try { IDictionary <string, object> messageContext = new Dictionary <string, object>(); if (!string.IsNullOrWhiteSpace(message.MessageContext)) { messageContext = JsonConvert.DeserializeObject <IDictionary <string, object> >(message.MessageContext); } var contentType = message.MessageContentType; if (string.IsNullOrWhiteSpace(message.MessageContentType)) { contentType = (string)messageContext[MessageContext.ContentType]; _logger.LogTrace($"Outbox message did not contain content type. Retrieved from message context."); } messageContext.TryGetValue(MessageContext.InfrastructureType, out var infra); var dispatcherInfrastructure = _infrastructureProvider.GetDispatcher((string)infra); if (string.IsNullOrWhiteSpace(contentType)) { _logger.LogTrace($"No content type set in outbox message or message context. Unable to dispatch message."); throw new ArgumentNullException(nameof(contentType), "A content type is required to serialize and send brokered message."); } var converter = _bodyConverterFactory.CreateBodyConverter(contentType); var outbound = new OutboundBrokeredMessage(message.MessageId, converter.GetBytes(message.MessageBody), messageContext, message.Destination, converter); _logger.LogTrace($"Processing message '{message.MessageId}' from outbox."); await((IUnitOfWork)_brokeredMessageOutbox).ExecuteAsync(async ct => { await _brokeredMessageOutbox.UpdateProcessedDate(message, ct); await dispatcherInfrastructure.Dispatch(outbound, null); _logger.LogTrace($"Message '{message.MessageId}' dispatched to messaging infrastructure from outbox."); }, null, cancellationToken); } catch (Exception e) { _logger.LogError(e, $"Unable to process outbox message with id '{message.Id}'"); } }
Task Dispatch <TMessage, TOptions>(TMessage message, string destinationPath, TransactionContext transactionContext, TOptions options) where TMessage : IMessage where TOptions : RoutingOptions, new() { if (options == null) { options = new TOptions(); } if (string.IsNullOrWhiteSpace(options.ContentType)) { throw new ArgumentNullException(nameof(options.ContentType), "Message content type is required"); } var converter = _bodyConverterFactory.CreateBodyConverter(options.ContentType); var outbound = new OutboundBrokeredMessage(options.MessageId, message, options.ApplicationProperties, destinationPath, converter); return(_messageRouter.Route(outbound, transactionContext)); }
IEnumerable <OutboundBrokeredMessage> Dispatch <TMessage, TOptions>(IEnumerable <TMessage> messages, string destinationPath, TOptions options) where TMessage : IMessage where TOptions : RoutingOptions, new() { if (options == null) { options = new TOptions(); } if (string.IsNullOrWhiteSpace(options.ContentType)) { throw new ArgumentNullException(nameof(options.ContentType), "Message content type is required"); } var converter = _bodyConverterFactory.CreateBodyConverter(options.ContentType); foreach (var message in messages) { var destination = string.IsNullOrWhiteSpace(destinationPath) ? _brokeredMessageDetailProvider.GetMessageName(message.GetType()) : destinationPath; if (string.IsNullOrWhiteSpace(destination)) { throw new ArgumentNullException(nameof(destination), $"Routing destination is required. Use {typeof(BrokeredMessageAttribute).Name} or overload that accepts 'destinationPath'"); } OutboundBrokeredMessage outbound; if (string.IsNullOrWhiteSpace(options.MessageId)) { outbound = new OutboundBrokeredMessage(_messageIdGenerator, message, options.MessageContext, destination, converter); } else { outbound = new OutboundBrokeredMessage(options.MessageId, message, options.MessageContext, destination, converter); } yield return(outbound); } }
public async Task <MessageBrokerContext> ReceiveMessageAsync(TransactionContext transactionContext, CancellationToken cancellationToken) { Message message; try { message = await this.InnerReceiver.ReceiveAsync(); } catch (ServiceBusException sbe) when(sbe.IsTransient) { _logger.LogWarning(sbe, "Failure to receive message from Azure Service Bus due to transient error"); throw; } catch (ObjectDisposedException e) when(!cancellationToken.IsCancellationRequested && _innerReceiver.IsClosedOrClosing) { lock (_syncLock) { _innerReceiver = null; } _logger.LogWarning(e, "Service Bus receiver connection was closed."); return(null); } catch (Exception e) { _logger.LogError(e, "Failure to receive message from Azure Serivce Bus"); throw; } if (message is null) { return(null); } MessageBrokerContext messageContext; IBrokeredMessageBodyConverter bodyConverter = new JsonBodyConverter(); try { bodyConverter = _bodyConverterFactory.CreateBodyConverter(message.ContentType); } catch (Exception e) { _logger.LogWarning(e, $"Error creating body converter for content type '{message.ContentType}'. Defaulting to {nameof(JsonBodyConverter)}."); } finally { message.AddUserProperty(MessageContext.TimeToLive, message.TimeToLive); message.AddUserProperty(MessageContext.ExpiryTimeUtc, message.ExpiresAtUtc); message.AddUserProperty(MessageContext.InfrastructureType, ASBMessageContext.InfrastructureType); message.AddUserProperty(MessageContext.ReceiveAttempts, message.SystemProperties.DeliveryCount); messageContext = new MessageBrokerContext(message.MessageId, message.Body, message.UserProperties, _options.MessageReceiverPath, cancellationToken, bodyConverter); messageContext.Container.Include(message); transactionContext.Container.Include(this.InnerReceiver); if (_options.TransactionMode == TransactionMode.FullAtomicityViaInfrastructure) { transactionContext.Container.Include(this.InnerReceiver.ServiceBusConnection); } } return(messageContext); }
public void StartReceiver(Func <MessageBrokerContext, TransactionContext, Task> brokeredMessageHandler, CancellationToken receiverTerminationToken) { receiverTerminationToken.Register( () => { this.InnerReceiver.CloseAsync(); _innerReceiver = null; }); var messageHandlerOptions = new MessageHandlerOptions(HandleMessageException) { AutoComplete = false, MaxConcurrentCalls = _maxConcurrentCalls }; this.InnerReceiver.RegisterMessageHandler(async(msg, receivePumpToken) => { var transactionMode = msg.GetTransactionMode(); using var scope = CreateTransactionScope(transactionMode); try { msg.AddUserProperty(ApplicationProperties.TimeToLive, msg.TimeToLive); msg.AddUserProperty(ApplicationProperties.ExpiryTimeUtc, msg.ExpiresAtUtc); var bodyConverter = _bodyConverterFactory.CreateBodyConverter(msg.ContentType); var messageContext = new MessageBrokerContext(msg.MessageId, msg.Body, msg.UserProperties, this.MessageReceiverPath, bodyConverter); messageContext.Container.Include(msg); var transactionContext = new TransactionContext(this.MessageReceiverPath, transactionMode); transactionContext.Container.Include(this.InnerReceiver); if (transactionMode == TransactionMode.FullAtomicityViaInfrastructure) { transactionContext.Container.Include(this.ServiceBusConnection); } await brokeredMessageHandler(messageContext, transactionContext).ConfigureAwait(false); await this.InnerReceiver.CompleteAsync(msg.SystemProperties.LockToken).ConfigureAwait(false); } catch (CriticalBrokeredMessageReceiverException cbmre) { await AbandonWithExponentialDelayAsync(msg).ConfigureAwait(false); _logger.LogError($"Critical error encountered receiving message. MessageId: '{msg.MessageId}, CorrelationId: '{msg.CorrelationId}'" + "\n" + $"{cbmre.ErrorContext}"); } catch (Exception e) { await AbandonWithExponentialDelayAsync(msg).ConfigureAwait(false); _logger.LogWarning($"Message with Id '{msg.MessageId}' has been abandoned:\n '{e}'"); } finally { scope?.Complete(); } }, messageHandlerOptions); }
public async Task <MessageBrokerContext> ReceiveMessageAsync(TransactionContext transactionContext, CancellationToken cancellationToken) { ReceivedMessage message = null; MessageBrokerContext messageContext = null; SqlConnection connection = null; SqlTransaction transaction = null; try { connection = new SqlConnection(_ssbOptions.ConnectionString); await connection.OpenAsync(cancellationToken); transaction = await CreateTransaction(connection, cancellationToken); } catch (Exception ex) when(ex is ArgumentException || ex is InvalidOperationException) { transaction?.Dispose(); connection?.Dispose(); throw new CriticalReceiverException("Error connecting to sql", ex); } try { message = await ReceiveAsync(connection, transaction, cancellationToken); } #if NET5_0_OR_GREATER catch (SqlException e) when(e.IsTransient) { _logger.LogWarning(e, "Failure to receive message from Sql Service Broker due to transient error"); throw; } #endif catch (SqlException e) when(e.Number == 102) { throw new CriticalReceiverException($"Unable to receive message from configured queue '{_options.MessageReceiverPath}'", e); } catch (Exception e) { _logger.LogError(e, $"Error receiving sql service broker message from queue '{_options.MessageReceiverPath}'"); throw; } finally { if (message == null) { transaction?.Dispose(); connection?.Dispose(); } } if (message is null) { await DiscardMessageAsync(connection, transaction, "Discarding null message", cancellationToken); return(null); } if (message.MessageTypeName != ServicesMessageTypes.DefaultType && message.MessageTypeName != ServicesMessageTypes.ChatterBrokeredMessageType) { await DiscardMessageAsync(connection, transaction , $"Discarding message of type '{message.MessageTypeName}'. Only messages of type '{ServicesMessageTypes.DefaultType}' or '{ServicesMessageTypes.ChatterBrokeredMessageType}' will be received." , cancellationToken); return(null); } if (message.Body == null) { await DiscardMessageAsync(connection, transaction , $"Discarding message of type '{message.MessageTypeName}' with null message body" , cancellationToken); return(null); } transactionContext.Container.Include(connection); if (_transactionMode != TransactionMode.None && transaction != null) { transactionContext.Container.Include(transaction); } _localReceiverDeliveryAttempts.AddOrUpdate(message.ConvHandle, 1, (ch, deliveryAttempts) => deliveryAttempts + 1); IBrokeredMessageBodyConverter bodyConverter = new JsonUnicodeBodyConverter(); byte[] messagePayload = message.Body; string messageId = message.ConvHandle.ToString(); IDictionary <string, object> headers = new Dictionary <string, object>(); try { bodyConverter = _bodyConverterFactory.CreateBodyConverter(_ssbOptions.MessageBodyType); if (message.MessageTypeName == ServicesMessageTypes.ChatterBrokeredMessageType) { var brokeredMessage = bodyConverter.Convert <OutboundBrokeredMessage>(message.Body); if (brokeredMessage == null) { throw new ArgumentNullException(nameof(brokeredMessage), $"Unable to deserialize {nameof(OutboundBrokeredMessage)} from message body"); } messagePayload = brokeredMessage.Body; messageId = brokeredMessage.MessageId; headers = brokeredMessage.MessageContext; } } catch (Exception e) { _logger.LogWarning(e, $"Error creating body converter for content type '{_ssbOptions.MessageBodyType}'. Defaulting to {nameof(JsonUnicodeBodyConverter)}."); } finally { _localReceiverDeliveryAttempts.TryGetValue(message.ConvHandle, out var deliveryAttempts); headers[SSBMessageContext.ConversationGroupId] = message.ConvGroupHandle; headers[SSBMessageContext.ConversationHandle] = message.ConvHandle; headers[SSBMessageContext.MessageSequenceNumber] = message.MessageSeqNo; headers[SSBMessageContext.ServiceName] = message.ServiceName; headers[SSBMessageContext.ServiceContractName] = message.ServiceContractName; headers[SSBMessageContext.MessageTypeName] = message.MessageTypeName; headers[MessageContext.InfrastructureType] = SSBMessageContext.InfrastructureType; headers[MessageContext.ReceiveAttempts] = deliveryAttempts; messageContext = new MessageBrokerContext(messageId, messagePayload, headers, _options.MessageReceiverPath, cancellationToken, bodyConverter); messageContext.Container.Include(message); } return(messageContext); }