예제 #1
0
        /// <summary>
        /// Use RabbitMQ as Pub/Sub system.
        /// </summary>
        /// <param name="bootstrapper">Bootstrapper instance to configure</param>
        /// <returns>Configured bootstrapper instance</returns>
        public static Bootstrapper UseRabbitMQ(
            this Bootstrapper bootstrapper,
            RabbitConnectionInfos connectionInfos,
            RabbitNetworkInfos networkInfos,
            Action <RabbitSubscriberConfiguration> subscriberConfiguration = null,
            Action <RabbitPublisherConfiguration> publisherConfiguration   = null)
        {
            var service = RabbitMQBootstrappService.Instance;

            var subscriberConf = new RabbitSubscriberConfiguration
            {
                ConnectionInfos = connectionInfos,
                NetworkInfos    = networkInfos
            };

            subscriberConfiguration?.Invoke(subscriberConf);

            service.BootstrappAction += (ctx) =>
            {
                var publisherConf = new RabbitPublisherConfiguration()
                {
                    ConnectionInfos = connectionInfos,
                    NetworkInfos    = networkInfos
                };
                publisherConfiguration?.Invoke(publisherConf);

                bootstrapper.AddIoCRegistration(new InstanceTypeRegistration(subscriberConf, typeof(RabbitSubscriberConfiguration)));
                bootstrapper.AddIoCRegistration(new InstanceTypeRegistration(publisherConf, typeof(RabbitPublisherConfiguration)));

                bootstrapper.AddIoCRegistration(new TypeRegistration(typeof(RabbitPublisher), true));
                bootstrapper.AddIoCRegistration(new TypeRegistration(typeof(RabbitSubscriber), true));
                if (publisherConf.RoutingKeyFactory != null)
                {
                    bootstrapper.AddIoCRegistration(new InstanceTypeRegistration(publisherConf.RoutingKeyFactory, typeof(IRoutingKeyFactory)));
                }
            };
            bootstrapper.AddService(service);
            bootstrapper.OnPostBootstrapping += (c) =>
            {
                ILoggerFactory loggerFactory = null;
                IScopeFactory  scopeFactory  = null;
                if (c.Scope != null)
                {
                    loggerFactory = c.Scope.Resolve <ILoggerFactory>();
                    scopeFactory  = c.Scope.Resolve <IScopeFactory>();
                }
                if (loggerFactory == null)
                {
                    loggerFactory = new LoggerFactory();
                    loggerFactory.AddProvider(new DebugLoggerProvider());
                }
                RabbitMQBootstrappService.RabbitSubscriber =
                    new RabbitSubscriber(
                        loggerFactory,
                        subscriberConf,
                        scopeFactory);
                RabbitMQBootstrappService.RabbitSubscriber.Start();
            };
            return(bootstrapper);
        }
예제 #2
0
        public async Task OneExchangePerService_NetworkConfiguration_AsExpected_Event()
        {
            try
            {
                bool eventReceived = false;
                var  networkInfos  = RabbitNetworkInfos.GetConfigurationFor("sub1", RabbitMQExchangeStrategy.ExchangePerService);

                networkInfos.DistantExchangeDescriptions.Add(
                    new RabbitExchangeDescription(firstProducerEventExchangeName)
                    );

                var serviceQueue = networkInfos.ServiceQueueDescriptions[0];
                serviceQueue.Bindings.Add(new RabbitQueueBindingDescription(firstProducerEventExchangeName));

                var config = new RabbitSubscriberConfiguration
                {
                    UseDeadLetterQueue = false,
                    ConnectionInfos    = GetConnectionInfos(),
                    NetworkInfos       = networkInfos,
                    DispatchInMemory   = false
                };
                config.EventCustomCallback = (e) => eventReceived = e is RabbitEvent;
                var subscriber = new RabbitSubscriber(
                    _loggerFactory,
                    config,
                    scopeFactory);

                subscriber.Start();

                var enveloppeWithFirstEvent = GetEnveloppeDataForEvent(publisher: "pub1", content: "data");

                _channel.BasicPublish(
                    exchange: firstProducerEventExchangeName,
                    routingKey: "",
                    basicProperties: null,
                    body: enveloppeWithFirstEvent);

                int awaitedTime = 0;
                while (awaitedTime <= 2000)
                {
                    if (eventReceived)
                    {
                        break;
                    }
                    await Task.Delay(10);

                    awaitedTime += 10;
                }

                eventReceived.Should().BeTrue();
            }
            finally
            {
                DeleteData();
            }
        }
예제 #3
0
        public async Task RabbitMQSubscriber_Should_Consider_AckStrategy_Ack_On_Receive_Fail_Should_Remove_MessageFromQueue_CallbackExc()
        {
            try
            {
                var networkInfos = RabbitNetworkInfos.GetConfigurationFor("sub1", RabbitMQExchangeStrategy.SingleExchange);

                var config = new RabbitSubscriberConfiguration
                {
                    UseDeadLetterQueue = true,
                    ConnectionInfos    = GetConnectionInfos(),
                    NetworkInfos       = networkInfos,
                    AckStrategy        = AckStrategy.AckOnReceive
                };
                config.EventCustomCallback += (_) => throw new InvalidOperationException();

                var subscriber = new RabbitSubscriber(
                    _loggerFactory,
                    config);

                subscriber.Start();

                var evt = new ExceptionEvent();

                _channel.BasicPublish(
                    Consts.CONST_CQE_EXCHANGE_NAME,
                    "",
                    body: Encoding.UTF8.GetBytes(
                        JsonConvert.SerializeObject(
                            new Enveloppe(
                                JsonConvert.SerializeObject(evt), typeof(ExceptionEvent), publisher1Name))));
                await Task.Delay(250);

                int            awaitedTime = 0;
                BasicGetResult result      = null;
                while (awaitedTime <= 750)
                {
                    result = _channel.BasicGet(Consts.CONST_DEAD_LETTER_QUEUE_NAME, true);
                    if (result != null)
                    {
                        break;
                    }
                    await Task.Delay(10);

                    awaitedTime += 10;
                }
                result.Should().BeNull();
            }
            finally
            {
                DeleteData();
            }
        }
예제 #4
0
        public async Task RabbitSubscriber_Should_Consider_AckStrategy_Ack_On_Success_CallbackExc()
        {
            try
            {
                bool eventReceived = false;
                var  messages      = new List <object>();
                var  networkInfos  = RabbitNetworkInfos.GetConfigurationFor("sub1", RabbitMQExchangeStrategy.SingleExchange);

                var config = new RabbitSubscriberConfiguration
                {
                    UseDeadLetterQueue = true,
                    ConnectionInfos    = GetConnectionInfos(),
                    NetworkInfos       = networkInfos,
                    DispatchInMemory   = false
                };
                config.EventCustomCallback = (e) => { messages.Add(e); eventReceived = true; };

                var subscriber = new RabbitSubscriber(
                    _loggerFactory,
                    config);

                subscriber.Start();

                var evt = new AutoAckEvent();

                _channel.BasicPublish(
                    Consts.CONST_CQE_EXCHANGE_NAME,
                    "",
                    body: Encoding.UTF8.GetBytes(
                        JsonConvert.SerializeObject(
                            new Enveloppe(
                                JsonConvert.SerializeObject(evt), typeof(AutoAckEvent), publisher1Name))));
                int awaitedTime = 0;
                while (awaitedTime <= 2000)
                {
                    if (eventReceived)
                    {
                        break;
                    }
                    await Task.Delay(10);

                    awaitedTime += 10;
                }
                eventReceived.Should().BeTrue();
                var result = _channel.BasicGet(Consts.CONST_DEAD_LETTER_QUEUE_NAME, true);
                result.Should().BeNull();
            }
            finally
            {
                DeleteData();
            }
        }
예제 #5
0
        public async Task Command_Should_Be_Send_AsDirect()
        {
            try
            {
                bool commandReceived = false;
                var  networkInfos    = RabbitNetworkInfos.GetConfigurationFor("sub1", RabbitMQExchangeStrategy.SingleExchange);
                var  serviceQueue    = networkInfos.ServiceQueueDescriptions[0];
                var  config          = new RabbitSubscriberConfiguration
                {
                    UseDeadLetterQueue = false,
                    ConnectionInfos    = GetConnectionInfos(),
                    NetworkInfos       = networkInfos,
                    DispatchInMemory   = false
                };
                config.CommandCustomCallback = (c) => commandReceived = c is RabbitCommand;

                var subscriber = new RabbitSubscriber(
                    _loggerFactory,
                    config,
                    scopeFactory);

                subscriber.Start();

                var enveloppeWithCommand = GetEnveloppeDataForCommand(publisher: "pub1", content: "data");

                _channel.BasicPublish(
                    exchange: "",
                    routingKey: serviceQueue.QueueName,
                    basicProperties: null,
                    body: enveloppeWithCommand);

                int awaitedTime = 0;
                while (awaitedTime <= 2000)
                {
                    if (commandReceived)
                    {
                        break;
                    }
                    await Task.Delay(10);

                    awaitedTime += 10;
                }

                commandReceived.Should().BeTrue();
            }
            finally
            {
                DeleteData();
            }
        }
예제 #6
0
        public async Task OneExchange_Network_Configuration_AsExpected_Event()
        {
            try
            {
                bool eventReceived = false;
                var  networkInfos  = RabbitNetworkInfos.GetConfigurationFor("sub1", RabbitMQExchangeStrategy.SingleExchange);
                var  config        = new RabbitSubscriberConfiguration
                {
                    UseDeadLetterQueue = false,
                    ConnectionInfos    = GetConnectionInfos(),
                    NetworkInfos       = networkInfos,
                    DispatchInMemory   = false
                };
                config.EventCustomCallback = (e) => eventReceived = e is RabbitEvent;
                var subscriber = new RabbitSubscriber(
                    _loggerFactory,
                    config,
                    scopeFactory);

                subscriber.Start();

                var enveloppeWithFirstEvent = GetEnveloppeDataForEvent(publisher: "pub1", content: "data");

                _channel.BasicPublish(
                    exchange: Consts.CONST_CQE_EXCHANGE_NAME,
                    routingKey: "",
                    basicProperties: null,
                    body: enveloppeWithFirstEvent);

                int awaitedTime = 0;
                while (awaitedTime <= 2000)
                {
                    if (eventReceived)
                    {
                        break;
                    }
                    await Task.Delay(10);

                    awaitedTime += 10;
                }

                eventReceived.Should().BeTrue();
            }
            finally
            {
                DeleteData();
            }
        }
        public static void PublishMessageTest()
        {
            IRabbitPublisherConfiguration publishConfig = new RabbitPublisherConfiguration();
            RabbitPublisher <CommandArgs> publisher     = new RabbitPublisher <CommandArgs>(publishConfig);

            IRabbitSubscriberConfiguration subscriberConfig = new RabbitSubscriberConfiguration();
            SandboxConsumer consumer = new SandboxConsumer();

            RabbitSubscriber <SandboxConsumer> subscriber = new RabbitSubscriber <SandboxConsumer>(consumer, subscriberConfig);

            subscriber.Start();

            publisher.Publish(new CommandArgs {
                Value = "some data"
            });



            Console.ReadKey();
        }
예제 #8
0
        public static void DeclareExchangesAndQueueForSubscriber(
            IModel channel,
            RabbitSubscriberConfiguration config)
        {
            if (config.UseDeadLetterQueue)
            {
                channel.ExchangeDeclare(Consts.CONST_DEAD_LETTER_EXCHANGE_NAME, "fanout", true, false, null);
                channel.QueueDeclare(Consts.CONST_DEAD_LETTER_QUEUE_NAME, true, false, false, null);
                channel.QueueBind(Consts.CONST_DEAD_LETTER_QUEUE_NAME, Consts.CONST_DEAD_LETTER_EXCHANGE_NAME, "", null);
            }

            DeclareExchanges(channel, config.NetworkInfos.ServiceExchangeDescriptions.Concat(config.NetworkInfos.DistantExchangeDescriptions));

            foreach (var queueDescription in config.NetworkInfos.ServiceQueueDescriptions)
            {
                if (config.UseDeadLetterQueue && !queueDescription.AdditionnalProperties.ContainsKey(Consts.CONST_DEAD_LETTER_EXCHANGE_RABBIT_KEY))
                {
                    queueDescription.AdditionnalProperties.Add(Consts.CONST_DEAD_LETTER_EXCHANGE_RABBIT_KEY, Consts.CONST_DEAD_LETTER_EXCHANGE_NAME);
                }
                DeclareQueue(channel, queueDescription);
            }
        }
예제 #9
0
        public void Test1()
        {
            var option = new RabbitSubscriberConfiguration
            {
                ExchangeName = "ex",
                QueueName    = "ex.q",
                ConsumerTag  = "ct",
                ExchangeType = "Fanout"
            };

            var message = "{\"message\": \"no h\"}";
            var body    = Encoding.UTF8.GetBytes(message);

            sut.StartProcess <SampleMessage>(option, (payload) =>
            {
                Console.WriteLine("model {0}", payload);

                Assert.Equal("no h", payload.Message);
                return(Task.CompletedTask);
            }, null);

            channel.BasicPublish(option.ExchangeName, "", body: body);
        }
예제 #10
0
        public void Test2()
        {
            var option = new RabbitSubscriberConfiguration
            {
                ExchangeName = "ex",
                QueueName    = "ex.q",
                ConsumerTag  = "ct",
                ExchangeType = "Fanout"
            };
            var waitHandle = new AutoResetEvent(false);

            var message = "{\"message\": \"no h\"}";
            var body    = Encoding.UTF8.GetBytes(message);

            sut.StartProcess <SampleMessage>(option, (payload) =>
            {
                if (payload.Message == "no h")
                {
                    throw new Exception("error");
                }

                return(Task.CompletedTask);
            }, payload =>
            {
                Assert.Equal("no h", payload.Message.Message);
                waitHandle.Set();
                return(Task.CompletedTask);
            });

            channel.BasicPublish(option.ExchangeName, "", body: body);

            if (!waitHandle.WaitOne(1000, false))
            {
                Assert.False(true, "Timeout");
            }
        }
예제 #11
0
        public Task StartProcess <T>(
            RabbitSubscriberConfiguration option,
            Func <T, Task> queueProcessor,
            Func <DlxMessage <T>, Task> dlxProcessor) where T : class
        {
            var(queueName, exchangeName, consumerTag, maxRetry, backoffRateInSeconds, exchangeType, routingKey) =
                (option.QueueName, option.ExchangeName, option.ConsumerTag, option.MaxRetry, option.BackoffRateInSeconds, option.ExchangeType, option.RoutingKey);

            if (maxRetry.HasValue && !backoffRateInSeconds.HasValue)
            {
                throw new ArgumentException("Retry count should be accompanied by back off rate");
            }

            if (exchangeType.ToLower() != "fanout" && string.IsNullOrWhiteSpace(routingKey))
            {
                throw new ArgumentException("non-fanout exchange type must have routing key");
            }

            connection = rabbitConnFactory.CreateConnection();
            var channel = connection.CreateModel();

            channelsToDispose.Add(channel);

            CreateDlx(dlxProcessor, channel, exchangeName, queueName, consumerTag);

            channel.ExchangeDeclare(exchange: exchangeName, type: exchangeType.ToLower());

            var qName = channel.QueueDeclare(queueName, true, false, false, null).QueueName;

            channel.QueueBind(qName, exchangeName, routingKey ?? "", null);

            var consumer = new AsyncEventingBasicConsumer(channel);

            consumer.Received += async(model, ea) =>
            {
                T deserializedObject = default;
                try
                {
                    var body    = ea.Body.ToArray();
                    var message = Encoding.UTF8.GetString(body);

                    if (message is null)
                    {
                        logger.LogInformation("Null message payload, not processing message");
                        channel.BasicAck(ea.DeliveryTag, false);
                        return;
                    }

                    deserializedObject = JsonSerializer.Deserialize <T>(message, new JsonSerializerOptions
                    {
                        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                    });

                    if (deserializedObject is null)
                    {
                        logger.LogInformation("Deserialised message resulted in null object, not processing message");
                        return;
                    }

                    await queueProcessor(deserializedObject);

                    channel.BasicAck(ea.DeliveryTag, false);
                }
                catch (Exception e)
                {
                    // Get routing keys from (first (latest)) headers to determine the current retry backoff
                    var routingKeysInMillis = ea.BasicProperties?.Headers != null ? ((ea.BasicProperties.Headers["x-death"] as List <object>).FirstOrDefault() as Dictionary <string, object>)?["routing-keys"] : null;

                    try
                    {
                        if (!maxRetry.HasValue || maxRetry <= 0)
                        {
                            // No retry, just dead letter directly
                            SendToDlx(channel, exchangeName, deserializedObject, e, ea);
                            return;
                        }

                        // try convert routingkeys backoff and convert to retry count below
                        if (routingKeysInMillis != null && UInt32.TryParse(Encoding.UTF8.GetString((routingKeysInMillis as List <object>)[0] as byte[]), out uint retryCount))
                        {
                            retryCount = (retryCount / (backoffRateInSeconds.Value * 1000)) + 1; // when it's in here, means it has gone through first round
                            if (retryCount <= maxRetry)
                            {
                                SendToRetryExchange(channel, retryCount, backoffRateInSeconds.Value, exchangeName, queueName, routingKey, ea);
                                return;
                            }

                            SendToDlx(channel, exchangeName, deserializedObject, e, ea);

                            return;
                        }

                        SendToRetryExchange(channel, 1, backoffRateInSeconds.Value, exchangeName, queueName, routingKey, ea);
                    }
                    catch (Exception ex)
                    {
                        logger.LogError(ex, "Error: {0}", ex.Message);
                    }
                }
            };

            channel.BasicConsume(
                queue: qName,
                autoAck: false,
                consumerTag: $"{consumerTag ?? Assembly.GetExecutingAssembly().ToString()} - queue",
                consumer: consumer);

            return(Task.CompletedTask);
        }