Exemplo n.º 1
0
        /// <summary>
        /// Consumer constructor
        /// </summary>
        /// <param name="configuration">constructor configuration</param>
        /// <param name="logger">Logger</param>
        internal RabbitBase(RabbitConsumerConfiguration configuration, ILogger logger = null)
            : this((RabbitConfigurationBase)configuration, logger)
        {
            // Fixings
            configuration.Workers   = configuration.Workers < 1 ? (ushort)1 : configuration.Workers;
            configuration.BatchSize = configuration.BatchSize < 1 ? (ushort)1 : configuration.BatchSize;
            configuration.RetryIntervals ??= new List <ulong> {
            };
            configuration.RetryIntervals = configuration.RetryIntervals
                                           .Where(interval => interval >= 5000)
                                           .OrderBy(interval => interval)
                                           .ToList();
            // Validations
            var invalid = configuration.Dependencies.Count == 0 ? string.Empty :
                          configuration.Dependencies.Any(dependency => string.IsNullOrEmpty(dependency.Name)) ? "Name" :
                          configuration.Dependencies.Any(dependency => string.IsNullOrEmpty(dependency.Type)) ? "Type" :
                          string.Empty;

            if (!string.IsNullOrWhiteSpace(invalid))
            {
                throw new ArgumentException(RabbitAnnotations.Exception.FactoryArgumentExceptionConsumer, "Dependecies." + invalid);
            }
            if (configuration.Dependencies.Any(dependency => !AllowedExchangeTypes.Contains(dependency.Type)))
            {
                throw new NotImplementedException(RabbitAnnotations.Exception.FactoryNotImplementedExceptionConsumer);
            }
            // Preparations
            var retry     = configuration.RetryExchangeConfiguration;
            var exchanges = new List <RabbitPublisherConfiguration> {
                retry
            };

            exchanges.AddRange(configuration.Dependencies.Select(dependency => new RabbitPublisherConfiguration
            {
                Name      = dependency.Name,
                Type      = dependency.Type,
                Arguments = dependency.Arguments
            }));
            var bindings = configuration.Dependencies
                           .Select(dependency => new RabbitConfigurationDependency
            {
                Name  = dependency.Name,
                Route = string.IsNullOrEmpty(dependency.Route) ? configuration.Name : dependency.Route
            })
                           .ToDictionary(dependency => dependency.Name, dependency => dependency.Route);

            bindings.Add(retry.Name, configuration.Name);
            // Declarations
            for (int i = 1; i <= configuration.Workers; i++)
            {
                // Declaring channel
                var channel = DeclareChannel(configuration);
                // Declaring paired exchanges
                foreach (var exchange in exchanges)
                {
                    DeclareExchange(channel, exchange.Name, exchange.Type, exchange.Arguments);
                }
                // Declaring queue and bindings
                DeclareQueue(channel, configuration.Name, bindings);
                // Registering channel
                Channels.Add(channel);
            }
        }
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="configuration">Configuration</param>
 /// <param name="logger">Logger</param>
 public RabbitConsumer(RabbitConsumerConfiguration configuration, ILogger logger = null)
     : base(configuration, logger)
 {
     // ...
     Configuration = configuration;
     Retry         = new RetryPublisher(Configuration.RetryExchangeConfiguration);
     // Creating consumers (a.k.a workers)
     for (short i = 0; i < Channels.Count; i++)
     {
         // Current channel
         var channel = Channels[i];
         // Declaring consumer
         var consumer = new RabbitConsumerBase <T>(channel, Configuration.BatchSize);
         OnStart(i);
         // The consumer will not recive messages, until current is not handled
         channel.BasicQos(0, Configuration.BatchSize, false);
         // Binding queue to the consumer
         // Message(s) will not be deleted automatically
         // The consumer will wait acknowledgement to do so
         channel.BasicConsume(Configuration.Name, false, consumer);
         // Action for recieve message(s)
         consumer.Received += (sender, package) =>
         {
             // Defining the package body
             var body = Encoding.UTF8.GetString(package.Body.ToArray());
             // Trying to preserve the body as actual message
             try
             {
                 // Convert to actual typed message
                 var message = JsonSerializer.Deserialize <T>(body, JsonOptions);
                 // Defining the next delay
                 var previous = (object)0;
                 if (package.BasicProperties.Headers != null)
                 {
                     package.BasicProperties.Headers.TryGetValue(RabbitAnnotations.Retry.HeaderKey, out previous);
                 }
                 var delay = Configuration.RetryIntervals.FirstOrDefault(interval =>
                                                                         interval > (ulong)Math.Abs(Convert.ToInt64(previous))
                                                                         );
                 // Preserve the message, make it ready for handling
                 consumer.Preserve(message, package.DeliveryTag, delay);
             }
             // If something went wrong on this stage ...
             catch (Exception exception)
             {
                 // Retrying it does not make sence
                 Task.Run(async() =>
                 {
                     // We do kill the message
                     await Handle(exception, new List <string> {
                         body
                     });
                     channel.BasicAck(package.DeliveryTag, false);
                 });
             }
         };
         // Action for handle message(s)
         consumer.Handle += (packages, tag) =>
         {
             // Run hendling
             Task.Run(async() =>
             {
                 // Trying to handle message(s)
                 try
                 {
                     // Handle beggin time
                     var stamp = DateTime.Now;
                     // Messages
                     var messages = packages.Select(m => m.Message);
                     // Handling single message
                     if (Configuration.BatchSize == 1)
                     {
                         await Handle(packages[0].Message);
                     }
                     // Handling grouped messages
                     else
                     {
                         await Handle(packages.Select(m => m.Message));
                     }
                     OnHandled((DateTime.Now - stamp).TotalMilliseconds, messages.Count());
                 }
                 // In case if something goes wrong
                 // We either retry the message(s) or kill them
                 catch (Exception exception)
                 {
                     // Handle retry
                     await Handle(
                         exception,
                         packages.Where(package => package.Delay > 0)
                         );
                     // Handle dead
                     await Handle(
                         exception,
                         packages.Where(package => package.Delay == 0)
                         .Select(package => JsonSerializer.Serialize(package.Message, JsonOptions))
                         );
                 }
                 // Message(s) will be deleted from the queue
                 finally
                 {
                     // Suggestion, that everything is ok and we can delete the message(s) from the queue
                     channel.BasicAck(tag, true);
                 }
             });
         };
     }
 }