public void SendMessage(byte[] message, string queueName, string exchange = "", string routingKey = null)
        {
            if (message is null)
            {
                throw new ArgumentNullException(nameof(message));
            }
            else if (queueName != null && queueName.Trim().Length == 0)
            {
                throw new ArgumentException("Queue name can not be empty or whitespace!");
            }

            using (var conn = AmqpConnectionWrapper.GetInstance())
                using (var channel = conn.CreateModel())
                {
                    if (queueName != null && NonDeclaredYet(queueName))
                    {
                        channel.QueueDeclare(
                            queue: queueName,
                            durable: false,
                            exclusive: false,
                            autoDelete: false,
                            arguments: null);
                    }

                    channel.BasicPublish(
                        exchange: exchange,
                        routingKey: routingKey ?? queueName ?? string.Empty,
                        basicProperties: null,
                        body: message);
                }
        }
        private void Setup(string type, string exchange, IDictionary <string, object> routing)
        {
            if (string.IsNullOrWhiteSpace(exchange))
            {
                throw new ArgumentNullException(nameof(exchange));
            }
            else if (routing is null)
            {
                throw new ArgumentNullException(nameof(routing));
            }

            if (NonDeclaredYet(type, exchange, routing.Keys, routing.Values))
            {
                using (var conn = AmqpConnectionWrapper.GetInstance())
                    using (var channel = conn.CreateModel())
                    {
                        channel.ExchangeDeclare(exchange, type);

                        foreach (var r in routing)
                        {
                            if (NonDeclaredYet(r.Key))
                            {
                                channel.QueueDeclare(
                                    queue: r.Key,
                                    durable: false,
                                    exclusive: false,
                                    autoDelete: false,
                                    arguments: null);
                            }

                            foreach (var rountingKey in GetRountingKeysByValue(r.Value))
                            {
                                channel.QueueBind(r.Key, exchange, rountingKey);
                            }
                        }
                    }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Registre um consumidor para obter mensagens da fila alvo.
        /// O processo ficará preso até solicitar o <see cref="Unregister{TModel}(string)"/> ou
        /// <see cref="UnregisterAll"/> que finalizará o consumer.
        /// </summary>
        /// <typeparam name="TModel">
        /// tipo do modelo serializado na mensagem na fila.
        /// </typeparam>
        /// <param name="onConsumedCallback">
        /// callback que será executado para receber o objeto json
        /// consumido da fila.
        /// </param>
        /// <param name="queueName">
        /// nome da fila
        /// </param>
        /// <param name="autoAck">
        /// true, define mensagem consumida automaticamente, false,
        /// aguarda finalizacao do callback para confirmar consumo.
        /// </param>
        /// <param name="requeue">
        /// define se deve reenfileirar mensagem caso nao seja ocorra algum erro.
        /// </param>
        /// <param name="throwsException">
        /// define se deve lançar a exceção caso ocorra ao tentar consumir mensagem da fila.
        /// </param>
        /// <param name="balanceCount">
        /// caso maior que 0 (zero), o message broker vai entregar por requisição do consumer
        /// apenas o numero de mensagens definido.
        /// Caso menor ou igual a 0 (zero), será verificado se existe a variavel de ambiente
        /// AMQP_DBC, caso exista com um valor numérico maior que zero, será considerado
        /// com a regra descrita acima, caso constrário, não habilita Qos Prefetch.
        /// Viavel para balanceamento de carga.
        /// </param>
        /// <param name="logger">
        /// log que armazenará mensagens de erro gerada.
        /// </param>
        /// <returns>
        /// true, processo foi finalizado corretamente através de um
        /// Unregister. false, processo foi interrompido por algum erro.
        /// </returns>
        public bool Register <TModel>(
            Action <TModel> onConsumedCallback,
            string queueName     = null,
            bool autoAck         = false,
            bool requeue         = true,
            bool throwsException = true,
            ushort balanceCount  = 0 /*not enabled*/,
            ILogger logger       = null)
        {
            if (queueName is null)
            {
                queueName = nameof(TModel);
            }
            else if (queueName.Trim().Length == 0)
            {
                throw new ArgumentException("Invalid queue name!");
            }

            Exception error = null;
            var       hash  = GetResetEventHash <TModel>(queueName);

            if (resetEvents.TryGetValue(hash, out var resetEvent))
            {
                throw new InvalidOperationException("Operation already registered before to " +
                                                    "the same model type and target queue!");
            }
            else if (!resetEvents.TryAdd(hash, resetEvent = new ManualResetEvent(false)))
            {
                return(false);//another process did it.
            }

            try
            {
                using (var conn = AmqpConnectionWrapper.GetInstance())
                    using (var channel = conn.CreateModel())
                    {
                        if (balanceCount > 0 ||
                            (balanceCount = ConnectionEnvironment.AMQP_DBC.ToUInt16()) > 0)
                        {
                            channel.BasicQos(0u, balanceCount, false /*per consumer*/);
                        }

                        channel.QueueDeclare(
                            queue: queueName,
                            durable: false,
                            exclusive: false,
                            autoDelete: false,
                            arguments: null);

                        var consumer = new EventingBasicConsumer(channel);

                        void received(object ch, BasicDeliverEventArgs args)
                        {
                            try
                            {
                                var    body  = args.Body.Span;
                                TModel model = JsonSerializer.Deserialize <TModel>(body);
                                onConsumedCallback.Invoke(model);
                                if (!autoAck && channel.IsOpen)
                                {
                                    channel.BasicAck(args.DeliveryTag, false);
                                }
                            }
                            catch (Exception ex)
                            {
                                if (!autoAck && channel.IsOpen)
                                {
                                    channel.BasicNack(args.DeliveryTag, false, requeue);
                                }
                                logger?.LogE(ex);
                                if (throwsException)
                                {
                                    error = ex;
                                }
                                resetEvent.Set();
                            }
                        }

                        void shutdown(object ch, ShutdownEventArgs args)
                        {
                            consumer.Received -= received;
                            consumer.Shutdown -= shutdown;
                            try
                            {
                                resetEvent.Set();
                            }
                            catch (ObjectDisposedException) { /*disposed*/ }
                        };

                        consumer.Received += received;
                        consumer.Shutdown += shutdown;

                        channel.BasicConsume(
                            queue: queueName,
                            autoAck: autoAck,
                            consumer: consumer);

                        bool result = resetEvent.WaitOne();
                        if (result && throwsException && error != null)
                        {
                            throw new AggregateException(error);
                        }

                        return(!result);
                    }
            }
            catch (Exception)
            {
                Unregister <TModel>(queueName);
                throw;
            }
        }
 static AmqpConnectionWrapper()
 {
     locker   = new object();
     instance = new AmqpConnectionWrapper();
 }