예제 #1
0
        public void SetupReceivers(ReceiveSettings[] receivers, Action <string, Exception, CancellationToken> criticalErrorAction)
        {
            var messagePumps = new Dictionary <string, IMessageReceiver>(receivers.Length);

            foreach (var receiver in receivers)
            {
                if (receiver.UsePublishSubscribe)
                {
                    throw new NotImplementedException("MSMQ does not support native pub/sub.");
                }

                // The following check avoids creating some sub-queues, if the endpoint sub queue has the capability to exceed the max length limitation for queue format name.
                CheckEndpointNameComplianceForMsmq.Check(receiver.ReceiveAddress);
                QueuePermissions.CheckQueue(receiver.ReceiveAddress);

                var pump = new MessagePump(
                    transactionMode =>
                    SelectReceiveStrategy(transactionMode, transportSettings.TransactionScopeOptions.TransactionOptions),
                    transportSettings.MessageEnumeratorTimeout,
                    criticalErrorAction,
                    transportSettings,
                    receiver);
                messagePumps.Add(pump.Id, pump);
            }

            Receivers = messagePumps;
        }
        // This is static to prevent the method from accessing feilds in the pump since that causes variable capturing and cause extra allocations
        static async Task InnerReceiveMessages(MessagePump messagePump)
        {
            try
            {
                await messagePump.receiveStrategy.ReceiveMessage(messagePump.messageProcessingCancellationTokenSource.Token).ConfigureAwait(false);

                messagePump.receiveCircuitBreaker.Success();
            }
            catch (OperationCanceledException) when(messagePump.messageProcessingCancellationTokenSource.IsCancellationRequested)
            {
                // Graceful shutdown
            }
            catch (Exception ex)
            {
                Logger.Warn("MSMQ receive operation failed", ex);
                await messagePump.receiveCircuitBreaker.Failure(ex).ConfigureAwait(false);
            }
            finally
            {
                messagePump.concurrencyLimiter.Release();
            }
        }
        // This is static to prevent the method from accessing fields in the pump since that causes variable capturing and cause extra allocations
        static async Task ReceiveMessagesSwallowExceptionsAndReleaseConcurrencyLimiter(MessagePump messagePump, SemaphoreSlim localConcurrencyLimiter, CancellationToken messagePumpCancellationToken)
        {
#pragma warning disable PS0021 // Highlight when a try block passes multiple cancellation tokens - justification:
            // The message processing cancellation token is being used for the receive strategies,
            // since we want those only to be canceled when the public token passed to Stop() is canceled.
            // The message pump token is being used elsewhere, because we want those operations to be canceled as soon as Stop() is called.
            // The catch clauses on the inner try are correctly filtered on the message processing cancellation token and
            // the catch clause on the outer try is correctly filtered on the message pump cancellation token.
            try
#pragma warning restore PS0021 // Highlight when a try block passes multiple cancellation tokens
            {
                try
                {
                    // we are switching token here so we need to catch cancellation
                    await messagePump.receiveStrategy.ReceiveMessage(messagePump.messageProcessingCancellationTokenSource.Token).ConfigureAwait(false);

                    messagePump.receiveCircuitBreaker.Success();
                }
                catch (Exception ex) when(ex.IsCausedBy(messagePump.messageProcessingCancellationTokenSource.Token))
                {
                    Logger.Warn("MSMQ receive operation canceled", ex);
                }
                catch (Exception ex)
                {
                    Logger.Warn("MSMQ receive operation failed", ex);
                    await messagePump.receiveCircuitBreaker.Failure(ex, messagePumpCancellationToken).ConfigureAwait(false);
                }
            }
#pragma warning disable PS0019 // When catching System.Exception, cancellation needs to be properly accounted for - justification: see PS0021 suppression justification
            catch (Exception ex) when(ex.IsCausedBy(messagePumpCancellationToken))
#pragma warning restore PS0019 // When catching System.Exception, cancellation needs to be properly accounted for
            {
                // private token, sender is being stopped, log the exception in case the stack trace is ever needed for debugging
                Logger.Debug("Operation canceled while stopping message pump.", ex);
            }
            finally
            {
                localConcurrencyLimiter.Release();
            }
        }