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); } } } } }
/// <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(); }