public void ExceptionExclusion() { RetryHandler retryHandler = new RetryHandler(5, 15, 7, false); retryHandler .ExcludeForRetry <TimeoutException>() .ExcludeForRetry <NullReferenceException>(); Assert.False(retryHandler.IsForRetry(new TimeoutException())); Assert.False(retryHandler.IsForRetry(new NullReferenceException())); Assert.True(retryHandler.IsForRetry(new ArgumentNullException())); }
public IExcludeForRetry Subscribe <TSubscriber, TEvent>(string tag, AbstractValidator <TEvent> validator, string retryCron, ushort?retryLimit) where TSubscriber : IEventHandler <TEvent> where TEvent : class { //naming validation if (tag != null) { tag.CheckNaming(); } //subscriber registration in a container Injection.AddTransient(typeof(TSubscriber)); //retry handler IRetry retryHandler = new RetryHandler(0, 0, 0, false); //creates queue and exchanges string directory = typeof(TEvent).GetTypeInfo().GetCustomAttribute <BusNamespace>().Directory; string subdirectory = typeof(TEvent).GetTypeInfo().GetCustomAttribute <BusNamespace>().Subdirectory; string exchange = "event_" + directory.ToLower() + "_" + subdirectory.ToLower(); string routingKey = typeof(TEvent).Name.ToLower() + "." + (tag != null ? tag.ToLower() : "*"); string restoreRoutingKey = _appId.ToLower() + "." + directory.ToLower() + "." + subdirectory.ToLower() + "." + typeof(TEvent).Name.ToLower() + (tag != null ? ("." + tag.ToLower()) : ""); string queue = _appId.ToLower() + "-event-" + directory.ToLower() + "-" + subdirectory.ToLower() + "-" + typeof(TEvent).Name.ToLower() + (tag != null ? ("-" + tag.ToLower()) : ""); _eventHandlerChannel.ExchangeDeclare(exchange, ExchangeType.Topic, true, false); _eventHandlerChannel.ExchangeDeclare(DEAD_LETTER_QUEUE_EXCHANGE, ExchangeType.Direct, true, false); _eventHandlerChannel.QueueDeclare(queue, true, false, false, new Dictionary <string, object> { { "x-queue-mode", "lazy" } }); _eventHandlerChannel.QueueBind(queue, exchange, routingKey); _eventHandlerChannel.QueueBind(queue, DEAD_LETTER_QUEUE_EXCHANGE, restoreRoutingKey); //creates dead letter queue string deadLetterQueue = queue + "-dlq"; string dlqRoutingKey = _appId.ToLower() + "." + directory.ToLower() + "." + subdirectory.ToLower() + "." + typeof(TEvent).Name.ToLower() + (tag != null ? ("." + tag.ToLower()) : "") + ".dlq"; _deadLetterQueueChannel.QueueDeclare(deadLetterQueue, true, false, false, new Dictionary <string, object> { { "x-queue-mode", "lazy" } }); _deadLetterQueueChannel.QueueBind(deadLetterQueue, DEAD_LETTER_QUEUE_EXCHANGE, dlqRoutingKey); //message listener EventingBasicConsumer consumer = new EventingBasicConsumer(_eventHandlerChannel); consumer.Received += (obj, args) => { Task.Factory.StartNew(async() => { string messageBody = Encoding.UTF8.GetString(args.Body.ToArray()); try { TEvent messageEvent = _jsonConverter.Deserialize <TEvent>(messageBody); if (validator != null) { await validator.ValidateAndThrowAsync(messageEvent, (directory + "." + subdirectory + "." + typeof(TEvent).Name + " is not valid")); } using (IServiceScope serviceScope = _serviceProvider.CreateScope()) using (ITraceScope traceScope = new TraceScope("Handle-" + directory + "." + subdirectory + "." + typeof(TEvent).Name, _tracer)) { TSubscriber subscriber = serviceScope.ServiceProvider.GetService <TSubscriber>(); subscriber.Bus = this; subscriber.TraceScope = traceScope; traceScope.Attributes.Add("AppId", _appId); traceScope.Attributes.Add("MessageId", args.BasicProperties.MessageId); await subscriber.HandleAsync(messageEvent); } _logger.Send(new MessageDetail { Id = args.BasicProperties.MessageId, CorrelationId = null, Type = MessageType.Event, Directory = directory, Subdirectory = subdirectory, Name = typeof(TEvent).Name, Body = messageBody, AppId = args.BasicProperties.AppId, Exception = null, ToRetry = false }); } catch (Exception exception) { bool toRetry = false; ushort retryIndex = ushort.Parse(Encoding.UTF8.GetString((byte[])args.BasicProperties.Headers["RetryIndex"])); if (retryHandler.IsForRetry(exception) && !string.IsNullOrEmpty(retryCron) && retryLimit != null && retryIndex < retryLimit) { IBasicProperties properties = _deadLetterQueueChannel.CreateBasicProperties(); properties.MessageId = args.BasicProperties.MessageId; properties.AppId = args.BasicProperties.AppId; properties.Headers = new Dictionary <string, object>(); properties.Headers.Add("RetryIndex", (++retryIndex).ToString()); properties.Persistent = true; _deadLetterQueueChannel.BasicPublish(DEAD_LETTER_QUEUE_EXCHANGE, dlqRoutingKey, properties, args.Body); toRetry = true; } _logger.Send(new MessageDetail { Id = args.BasicProperties.MessageId, CorrelationId = null, Type = MessageType.Event, Directory = directory, Subdirectory = subdirectory, Name = typeof(TEvent).Name, Body = messageBody, AppId = args.BasicProperties.AppId, Exception = exception, ToRetry = toRetry }); } //acknowledgment _eventHandlerChannel.BasicAck(args.DeliveryTag, false); }, _cancellationTokenSource.Token, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); }; _toActivateConsumers.Add(new Tuple <IModel, string, EventingBasicConsumer>(_eventHandlerChannel, queue, consumer)); //message restore Task.Factory.StartNew(async() => { while (true) { await retryCron.CronDelay(); List <BasicGetResult> bgrs = new List <BasicGetResult>(); for (int i = 0; i < DEAD_LETTER_QUEUE_RECOVERY_LIMIT; i++) { BasicGetResult bgr = _deadLetterQueueChannel.BasicGet(deadLetterQueue, false); if (bgr == null) { break; } bgrs.Add(bgr); } foreach (BasicGetResult bgr in bgrs) { _deadLetterQueueChannel.BasicPublish(DEAD_LETTER_QUEUE_EXCHANGE, restoreRoutingKey, bgr.BasicProperties, bgr.Body); _deadLetterQueueChannel.BasicAck(bgr.DeliveryTag, false); } } }, _cancellationTokenSource.Token, TaskCreationOptions.DenyChildAttach, _deadLetterQueueTaskScheduler); return(retryHandler); }