public async Task <bool> ReplyAsync <T>(T message, IDictionary <string, object> headers = null) { await Task.Delay(0, LocalCancellationToken); if (EndpointType != EndpointTypeEnum.RpcConsumer) { var e = new InvalidOperationException("Attempt to reply to a message on a non Rpc Consumer prohibited."); VerboseLoggingHandler.Log(e); throw e; } var bMessage = EnsureByteArrayFromGeneric(message); if (bMessage == null) { return(false); } VerboseLoggingHandler.Log($"Replying to message"); var args = new Dictionary <string, object>(); if (_rpcResponseChannel == null) { VerboseLoggingHandler.Log($"Response queue not open yet. Creating one now"); if (QueueTtlValues.ContainsKey(ReplyToQueueName)) { args.Add(DictionaryKey_QueueTtl, QueueTtlValues[ReplyToQueueName]); } VerboseLoggingHandler.Log($"Using requested ttl='{QueueTtlValues[ReplyToQueueName]}'"); _rpcResponseChannel = _RabbitOut.CreateModel(); // Use the same TTL value as was used to create the queue on the other side _rpcResponseChannel.QueueDeclare(ReplyToQueueName, false, false, true, args); VerboseLoggingHandler.Log($"Queue ready"); } var messageProperties = _rpcResponseChannel.CreateBasicProperties(); var rp = ReplyPriority; if (rp > 0) { rp--; } messageProperties.Priority = rp; messageProperties.Headers = headers; messageProperties.Type = MessageTypeFragmentsReply; messageProperties.Persistent = false; VerboseLoggingHandler.Log($"Publishing reply"); _rpcResponseChannel.BasicPublish("", ReplyToQueueName, true, messageProperties, bMessage); VerboseLoggingHandler.Log($"Sent"); return(true); }
public static RabbitMqEndpoint NewOutboundPublisher(string name, string type, string routingKeyOrTopicName, PublisherParameters p = null, MessageParameters defaultMessageParameters = null, CancellationToken token = default(CancellationToken)) { var exchangeType = GetFullExchangeType(type); p ??= new PublisherParameters() { AcceptReplies = false, AutoDelete = true, Durable = false, EnableConfirmSelect = false, ReplyQueueTtl = 0, Ttl = 6000 }; defaultMessageParameters ??= new MessageParameters() { AutoAck = false, Durable = false, Mandatory = false, Persistent = false, Priority = 3, Resilient = false, TimeOut = 6000 }; var ret = new RabbitMqEndpoint { Channel = _RabbitOut.CreateModel(), EndpointType = EndpointTypeEnum.Publisher, ExchangeName = name, QueueName = DefineQueueName(name, exchangeType, "", false), RoutingKeyOrTopicName = routingKeyOrTopicName, ExchangeId = Guid.NewGuid(), LocalCancellationToken = _RabbitOutCts?.Token ?? token, ChannelParameters = p, ExchangeType = GetFullExchangeType(type), DefaultMessageParameters = defaultMessageParameters }; VerboseLoggingHandler.Log($"Building a {exchangeType} outbound publisher, name='{name}', routingKeyOrTopicName='{routingKeyOrTopicName}', durable='{p.Durable}', ttl='{p.Ttl}', autoDelete='{p.AutoDelete}', acceptReplies='{p.AcceptReplies}', confirmSelect='{p.EnableConfirmSelect}'"); ret.ConnectToExchange(); if (p.EnableConfirmSelect) { ret.Channel.ConfirmSelect(); } if (p.AcceptReplies) { VerboseLoggingHandler.Log($"Building a return consumer, ExchangeId='{ret.ExchangeId}', replyQueueTtl='{p.ReplyQueueTtl}'"); ret.DefineRpcConsumer(ret.ExchangeName, ret.ExchangeId, p.ReplyQueueTtl); } VerboseLoggingHandler.Log("Exchange ready"); return(ret); }
private void DefineRpcConsumer(string exchangeName, Guid connectedExchangeId, int ttl = 2000000) { ttl = 10000000; var args = new Dictionary <string, object> { { DictionaryKey_QueueTtl, ttl } }; ReturnChannelQueueTtl = ttl; ReturnChannel = _RabbitIn.CreateModel(); ReturnChannelQueueName = $"RPC:{exchangeName}:{connectedExchangeId}"; ReturnChannel.QueueDeclare(ReturnChannelQueueName, false, false, true, args); ReturnChannelConsumer = new AsyncEventingBasicConsumer(_rpcResponseChannel); ReturnChannelConsumer.Received += RpcReturnChannelConsumerOnReceivedAsync; ReturnChannel.BasicConsume(ReturnChannelQueueName, true, ReturnChannelConsumer); ReturnChannelLatch = new SemaphoreSlim(0, 1); VerboseLoggingHandler.Log($"Reply queue created, name='{ReturnChannelQueueName}'"); }
public static RabbitMqEndpoint NewInboundConsumer(string name, string type, string routingKeyOrTopicName = "", ConsumerParameters p = null, CancellationToken token = default(CancellationToken)) { p ??= new ConsumerParameters(); var exchangeType = GetFullExchangeType(type); var ret = new RabbitMqEndpoint { Channel = _RabbitIn.CreateModel(), EndpointType = EndpointTypeEnum.Consumer, ExchangeName = name, LocalCancellationToken = _RabbitInCts?.Token ?? token, QueueTtlValues = new Dictionary <string, int>(), ChannelParameters = p, ExchangeType = GetFullExchangeType(type), QueueName = DefineQueueName(name, exchangeType, routingKeyOrTopicName, true), }; VerboseLoggingHandler.Log($"Building a {exchangeType} inbound consumer, name='{name}', routingKeyOrTopicName='{routingKeyOrTopicName}', durable='{p.Durable}', ttl='{p.Ttl}', autoDelete='{p.AutoDelete}', autoAckMode='{p.AutoAckMode}'"); ret.ConnectToExchange(); try { VerboseLoggingHandler.Log($"Binding queue '{ret.QueueName}'"); ret.Channel.QueueBind(ret.QueueName, name, routingKeyOrTopicName, new Dictionary <string, object>()); } catch (Exception e) { VerboseLoggingHandler.Log(e); throw e; } VerboseLoggingHandler.Log("Initiating the consumer"); ret.Consumer = new AsyncEventingBasicConsumer(ret.Channel); ret.Consumer.Received += ret.OnIncomingMessageAsync; VerboseLoggingHandler.Log("Consumer ready"); return(ret); }
private async Task <byte[]> SendMessageInternal(string routingKeyOrTopicName, byte[] message, IDictionary <string, object> headers, MessageType messageType, MessageParameters p = null) { if (EndpointType != EndpointTypeEnum.Publisher) { var e = new InvalidOperationException("Attempt to send a message on a consumer prohibited."); VerboseLoggingHandler.Log(e); throw e; } p ??= DefaultMessageParameters; if (string.IsNullOrEmpty(routingKeyOrTopicName)) { routingKeyOrTopicName = RoutingKeyOrTopicName; } VerboseLoggingHandler.Log($"SendMessageInternal sending message, type='{messageType}', routingKeyOrTopicName='{routingKeyOrTopicName}', durable='{p.Durable}', mandatory='{p.Mandatory}', persistent='{p.Persistent}', priority='{p.Priority}', autoAck='{p.AutoAck}', resilient='{p.Resilient}', timeout='{p.TimeOut}' (ms)"); headers ??= new Dictionary <string, object>(); byte[] ret = null; var messageTypeText = new StringBuilder(); var messageProperties = Channel.CreateBasicProperties(); messageProperties.Type = MessageTypeFragmentsChat; messageProperties.Timestamp = AmqpTimestampNow; messageProperties.Priority = p.Priority; messageProperties.Persistent = p.Persistent; messageProperties.Expiration = p.TimeOut.ToString(); if (p.Durable) { messageProperties.DeliveryMode = 2; } messageProperties.CorrelationId = ConversationId.ToString(); if (!p.AutoAck) { messageTypeText.Append((MessageTypeFragmentsRequestAmqAck)); } if ((messageType & MessageType.RequireAck) == MessageType.RequireAck) { messageTypeText.Append(MessageTypeFragmentsRequestAck); } if ((messageType & MessageType.RequireResponse) == MessageType.RequireResponse) { messageTypeText.Append(MessageTypeFragmentsRequestReply); } headers.Add(DictionaryKey_PassedQueueTtl, ReturnChannelQueueTtl); messageProperties.Type = messageTypeText.ToString(); messageProperties.Headers = headers; try { ReturnData = null; messageProperties.ReplyTo = ReturnChannelQueueName; VerboseLoggingHandler.Log($"Publishing now"); Channel.BasicPublish(ExchangeName, routingKeyOrTopicName, p.Mandatory, messageProperties, message); //_channel.WaitForConfirmsOrDie(new TimeSpan(0, 0, 0, 0, timeout.Value)); if (((PublisherParameters)(ChannelParameters)).EnableConfirmSelect) { try { Channel.WaitForConfirms(); } catch (InvalidOperationException e1) { if (e1.Message == "Confirms not selected") { var e = new InvalidOperationException("Invalid configuration. WaitForConfirms behavior requested, but Exchange not configured for them.", e1); VerboseLoggingHandler.Log(e); throw e; } else { VerboseLoggingHandler.Log(e1); throw e1; } } } if (messageType == MessageType.Normal) { return(null); } VerboseLoggingHandler.Log($"Published. LatchCount='{ReturnChannelLatch.CurrentCount}', timeout='{p.TimeOut}' (ms)"); await ReturnChannelLatch.WaitAsync(p.TimeOut, LocalCancellationToken); VerboseLoggingHandler.Log($"Released. LatchCount='{ReturnChannelLatch.CurrentCount}'"); } finally { if (messageType != MessageType.Normal) { VerboseLoggingHandler.Log($"Completed. LatchCount='{ReturnChannelLatch.CurrentCount}'"); ret = ReturnData; } } return(ret); }
protected virtual async Task OnIncomingMessageAsync(object sender, BasicDeliverEventArgs ea) { await Task.Delay(0, LocalCancellationToken); var ackMode = ((ConsumerParameters)ChannelParameters).AutoAckMode; var isRpc = false; var requiresAck = false; VerboseLoggingHandler.Log($"Event OnIncomingMessageAsync triggered For tag='{ea.DeliveryTag}', exchange='{ea.Exchange}', routingKeyOrTopicName='{ea.RoutingKey}', consumerTag='{ea.ConsumerTag}', deliveryTag='{ea.DeliveryTag}', redelivered='{ea.Redelivered}'"); var messageType = ea.BasicProperties.Type; if (!ea.BasicProperties.IsCorrelationIdPresent()) { var e = new InvalidOperationException($"Missing correlationId in message from exchange={ea.Exchange} deliveryTag=${ea.DeliveryTag}"); VerboseLoggingHandler.Log(e); throw e; } VerboseLoggingHandler.Log($"MessageType='{messageType}'"); if (messageType.Contains(MessageTypeFragmentsRequestAmqAck)) { if (ackMode == ConsumerParameters.AutoAckModeEnum.OnReceipt) { Channel.BasicAck(ea.DeliveryTag, false); } if (ackMode == ConsumerParameters.AutoAckModeEnum.Manual) { requiresAck = true; } } if ((messageType.Contains(MessageTypeFragmentsRequestAck) || messageType.Contains(MessageTypeFragmentsRequestReply) && ea.BasicProperties.IsReplyToPresent())) { isRpc = true; EndpointType = EndpointTypeEnum.RpcConsumer; ReplyToQueueName = ea.BasicProperties.ReplyTo; VerboseLoggingHandler.Log($"Reply requested to '{ReplyToQueueName}'"); if (ea.BasicProperties.IsHeadersPresent()) { VerboseLoggingHandler.Log($"Headers found"); var headers = ea.BasicProperties.Headers; if (headers.ContainsKey(DictionaryKey_PassedQueueTtl)) { var ttl = Convert.ToInt32(headers[DictionaryKey_PassedQueueTtl]); VerboseLoggingHandler.Log($"ttl='{ttl}'"); if (QueueTtlValues.ContainsKey(ReplyToQueueName)) { QueueTtlValues[ReplyToQueueName] = ttl; } else { QueueTtlValues.Add(ReplyToQueueName, ttl); } ea.BasicProperties.Headers.Remove(DictionaryKey_PassedQueueTtl); } } // Handle basic message acknowledgement here. if (messageType.Contains(MessageTypeFragmentsRequestAck) && ConvertMessageToString(ea.Body) == MessageContent_Ping) { VerboseLoggingHandler.Log($"Ack'ing the Ping"); await ReplyAsync(MessageContent_PingResponse, null); } } VerboseLoggingHandler.Log($"Delegate check - does messageType have '{MessageTypeFragmentsRequestReply}'?"); if (messageType.Contains(MessageTypeFragmentsRequestReply)) { VerboseLoggingHandler.Log($"Confirmed. Fire delegate"); var args = new IncomingRabbitMqMessageEventArgs(isRpc, requiresAck, ea); await Task.Run(() => IncomingMessage?.Invoke(this, args), LocalCancellationToken); } }
public static bool ConnectToRabbit(List <string> hosts, string virtualHost, string userName, string password, int port, string applicationName, int connectionRetryWait = 6000, int retries = 60, CancellationTokenSource inOrInAndOutCts = null, CancellationTokenSource outCts = null) { if (Initialized) { return(true); } var factory = new ConnectionFactory { UserName = userName, Password = password, VirtualHost = virtualHost, DispatchConsumersAsync = true, RequestedHeartbeat = new TimeSpan(0, 0, 10), AutomaticRecoveryEnabled = true, NetworkRecoveryInterval = TimeSpan.FromSeconds(10), Port = port switch { 0 => AmqpTcpEndpoint.UseDefaultPort, -1 => AmqpTcpEndpoint.DefaultAmqpSslPort, _ => port } }; _RabbitIn = Connect(_RabbitIn, "Consumer", retries, connectionRetryWait); _RabbitOut = Connect(_RabbitOut, "Exchange", retries, connectionRetryWait); _RabbitInCts = inOrInAndOutCts ?? new CancellationTokenSource(); _RabbitOutCts = outCts ?? inOrInAndOutCts; return(Initialized); IConnection Connect(IConnection c, string type, int r, int t) { Exception le = null; if (c != null && c.IsOpen) { return(c); } while (c == null || (!c.IsOpen && --r != 0)) { try { c = factory.CreateConnection(hosts, applicationName); if (c.IsOpen) { return(c); } } catch (BrokerUnreachableException e) { Task.Delay(t); le = e; } catch (Exception e) { le = e; break; } } var ex = new CannotConnectToRabbitException($"Unable to make {type} connections to rabbit", le); VerboseLoggingHandler.Log(ex); throw ex; } }
private static void ChannelOnBasicAcks(object sender, BasicAckEventArgs e) { VerboseLoggingHandler.Log($"Event ChannelOnBasicAcks triggered For tag='{e.DeliveryTag}', multiple='{e.Multiple}'"); }
private static void ChannelOnModelShutdown(object sender, ShutdownEventArgs e) { VerboseLoggingHandler.Log($"Event ChannelOnModelShutdown triggered - Initiator {e.Initiator.ToString()}, classId={e.ClassId} methodId={e.MethodId}, replyCode={e.ReplyCode}. ReplyText '{e.ReplyText}'"); }
private static void ChannelOnCallbackException(object sender, CallbackExceptionEventArgs e) { VerboseLoggingHandler.Log($"Event ChannelOnCallbackException triggered: {e.Detail}"); VerboseLoggingHandler.Log(e.Exception); }
private static void ChannelOnBasicReturn(object sender, BasicReturnEventArgs e) { VerboseLoggingHandler.Log($"Event ChannelOnBasicReturn triggered Exchange='{e.Exchange}', routingKeyOrTopicName='{e.RoutingKey}', replyText='{e.ReplyText}'"); }