/// <summary> /// 获取新序号 /// </summary> /// <param name="dateTime"></param> /// <returns></returns> public void Generate(DateTime dateTime) { lock (_locker) { Interlocked.Increment(ref _versionId); var unixTime = DateTime2UnixTime.ToUnixTime(dateTime.Date.AddHours(dateTime.Hour)); if (unixTime <= _unixTime) { ++Sequence; } else { _unixTime = unixTime; Infix = DateTime2UnixTime.FromUnixTime(_unixTime).ToString("yyMMddHH"); Sequence = 1; } } }
/// <summary> /// 从字节数组中加载 /// </summary> /// <param name="bytes">48字节</param> /// <exception cref="InvalidCastException"></exception> public BusinessCode(byte[] bytes) { if (bytes != null && bytes.Length == BYTES_LENGTH) { ApplicationId = new Guid(bytes.Take(16).ToArray()); CodeType = BitConverter.ToInt32(bytes, 16); _unixTime = BitConverter.ToInt64(bytes, 20); Infix = DateTime2UnixTime.FromUnixTime(_unixTime).ToString("yyMMddHH"); Sequence = BitConverter.ToInt32(bytes, 28); if (Sequence < 1) { throw new InvalidCastException("Sequence is invalid"); } _versionId = BitConverter.ToInt64(bytes, 40); if (_versionId < 1) { throw new InvalidCastException("Version is invalid"); } } else { throw new InvalidCastException("Data must 48 bytes"); } }
/// <summary> /// 处理消息队列的消息 /// </summary> /// <param name="currentConsumer"></param> /// <param name="props"></param> /// <param name="exchangeName"></param> /// <param name="queueName"></param> /// <param name="routingKey"></param> /// <param name="body"></param> /// <param name="deliveryTag"></param> /// <param name="redelivered"></param> /// <param name="dropIfMessageHandlerNotRegistered">消息处理器未注册时直接丢弃</param> protected void HandleMessage(IBasicConsumer currentConsumer, IBasicProperties props, string exchangeName, string queueName, string routingKey, byte[] body, ulong deliveryTag, bool redelivered, bool dropIfMessageHandlerNotRegistered) { var currentChannel = currentConsumer.Model; var messageTypeName = props.Type; if (string.IsNullOrEmpty(props.Type)) { // 如果未传入类型,则以队列名作为类型名 if (queueName.StartsWith("rpc.")) { messageTypeName = queueName.Substring(4); } else { messageTypeName = queueName; } } var context = new MessageHandlingTransportationContext(exchangeName, queueName, new Dictionary <string, object> { { MessagePropertyConstants.MESSAGE_ID, string.IsNullOrEmpty(props.MessageId) ? Guid.NewGuid().ToString() : props.MessageId }, { MessagePropertyConstants.MESSAGE_TYPE, messageTypeName }, { MessagePropertyConstants.TIMESTAMP, props.Timestamp.UnixTime == 0 ? DateTime.Now : DateTime2UnixTime.FromUnixTime(props.Timestamp.UnixTime) }, { MessagePropertyConstants.CONTENT_TYPE, string.IsNullOrEmpty(props.ContentType) ? Serializer.ContentType : props.ContentType }, { MessagePropertyConstants.PAYLOAD, Serializer.GetString(body) }, { MessagePropertyConstants.ROUTING_KEY, routingKey }, { MessagePropertyConstants.REPLY_TO, props.ReplyTo }, { MessagePropertyConstants.CORRELATION_ID, props.CorrelationId } }); var messageHandlerCategory = string.Empty; try { if (_messageTypes.ContainsKey(messageTypeName)) { object message = null; try { message = Serializer.Deserialize(body, _messageTypes[messageTypeName]); } catch { // 消费端无法反序列化消息,则直接丢弃 _logger.Error($"Message [\"{context.GetMessageType()}\"] \"{context.GetPayload()}\" deserialized fail in queue \"{queueName}\"."); LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicReject(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } if (message == null) { return; } var messageHandler = GetMessageHandler(messageTypeName); if (messageHandler != null) { messageHandlerCategory = MessageHandlerTypeAttribute.GetCategory(messageHandler.GetType()); TriggerOnMessageReceived(new MessageReceivedEventArgs(messageHandlerCategory, this.GetReceiverType(), context)); RetryPolicy.Retry(() => { try { messageHandler.GetType().InvokeMember("Handle", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, messageHandler, new object[] { message, context }); } catch (Exception handleEx) { context.LastException = handleEx is TargetInvocationException ? handleEx.InnerException : handleEx; throw handleEx; } }, cancelOnFailed: (retryCount, retryException) => { if (retryCount >= MaxRetryCount) { return(true); } context.LastException = retryException is TargetInvocationException ? retryException.InnerException : retryException; context.RetryCount = retryCount + 1; return(false); }); TriggerOnMessageHandlingSucceeded(new MessageHandlingSucceededEventArgs(messageHandlerCategory, this.GetReceiverType(), context)); LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicAck(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } else if (dropIfMessageHandlerNotRegistered) { // 消费端无对应的消息处理器,则忽略,直接响应成功 LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicAck(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } else { context.LastException = new MessageHandlerNotRegisteredException(messageTypeName); if (TriggerOnMessageHandlingFailed(new MessageHandlingFailedEventArgs(messageHandlerCategory, this.GetReceiverType(), context))) { LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicAck(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } else { LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicReject(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } } } else if (dropIfMessageHandlerNotRegistered) { // 消费端无对应的消息处理器,则忽略,直接响应成功 LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicAck(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } else { context.LastException = new MessageHandlerNotRegisteredException(messageTypeName); if (TriggerOnMessageHandlingFailed(new MessageHandlingFailedEventArgs(messageHandlerCategory, this.GetReceiverType(), context))) { LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicAck(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } else { LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicReject(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } } } catch (Exception ex) { // 消费端处理其他异常 context.LastException = ex is TargetInvocationException ? ex.InnerException : ex; if (TriggerOnMessageHandlingFailed(new MessageHandlingFailedEventArgs(messageHandlerCategory, this.GetReceiverType(), context))) { LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicAck(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } else { LockerExecuter.Execute(currentChannel, () => { RetryPolicy.Retry(() => currentChannel.BasicReject(deliveryTag, false), retryCondition: retryEx => IOHelper.IsIOError(retryEx) || RabbitMQExceptionHelper.IsChannelError(retryEx), retryTimeInterval: 1000); }); } } }
/// <summary> /// 重发到交换器 /// </summary> /// <param name="exchangeName">交换器名</param> /// <param name="messageId">消息ID</param> /// <param name="messageTypeName">消息类型名</param> /// <param name="messageTimestamp">消息时间戳</param> /// <param name="messageBody">消息体</param> /// <param name="routingKey">路由键</param> /// <param name="correlationId">关联ID</param> /// <param name="replyTo">响应队列名</param> public void RedeliverToExchange(string exchangeName, string messageId, string messageTypeName, DateTime messageTimestamp, string messageBody, string routingKey, string correlationId = "", string replyTo = "") { if (string.IsNullOrEmpty(messageId)) { throw new ArgumentNullException("messageId", "must not empty"); } if (string.IsNullOrEmpty(messageTypeName)) { throw new ArgumentNullException("messageTypeName", "must not empty"); } if (messageTimestamp == DateTime.MinValue) { throw new ArgumentNullException("messageTimestamp", "must not empty"); } if (string.IsNullOrEmpty(messageBody)) { throw new ArgumentNullException("messageBody", "must not empty"); } var body = Serializer.GetBytes(messageBody); var envelopedMessage = new EnvelopedMessage(messageId, messageTypeName, messageTimestamp, messageBody, routingKey, correlationId, replyTo); try { RetryPolicy.Retry(() => { using (var channel = _conn.CreateChannel()) { var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.DeliveryMode = 2; properties.ContentType = Serializer.ContentType; properties.MessageId = messageId; properties.Type = messageTypeName; properties.Timestamp = new AmqpTimestamp(DateTime2UnixTime.ToUnixTime(messageTimestamp)); if (!string.IsNullOrEmpty(correlationId) && !string.IsNullOrEmpty(replyTo)) { properties.CorrelationId = correlationId; properties.ReplyTo = replyTo; } channel.BasicPublish(exchange: exchangeName ?? string.Empty, routingKey: routingKey ?? string.Empty, mandatory: true, basicProperties: properties, body: body); channel.Close(); } }, cancelOnFailed: (retryCount, retryException) => { if (retryCount == 0) { _logger.Error($"Message [{messageTypeName}] \"{Serializer.GetString(body)}\" send to exchange \"{exchangeName}\", routing key={routingKey} failed.", retryException); } return(false); }, retryCondition: ex => IOHelper.IsIOError(ex) || RabbitMQExceptionHelper.IsChannelError(ex), maxRetryCount: 1, retryTimeInterval: 1000); _logger.Info($"Message [{messageTypeName}] \"{Serializer.GetString(body)}\" send to exchange \"{exchangeName}\", routing key={routingKey} successful."); } catch (Exception ex) { var realEx = ex is TargetInvocationException ? ex.InnerException : ex; _logger.Error($"Message [{ messageTypeName}] \"{Serializer.GetString(body)}\" send to exchange \"{exchangeName}\", routing key={routingKey} failed.", realEx); throw new MessageSendFailedException(envelopedMessage, exchangeName, realEx); } }
/// <summary> /// 重发到队列 /// </summary> /// <param name="queueName">队列名</param> /// <param name="messageId">消息ID</param> /// <param name="messageTypeName">消息类型名</param> /// <param name="messageTimestamp">消息时间戳</param> /// <param name="messageBody">消息体</param> /// <param name="correlationId">关联ID</param> /// <param name="replyTo">响应队列名</param> public void RedeliverToQueue(string queueName, string messageId, string messageTypeName, DateTime messageTimestamp, string messageBody, string correlationId = "", string replyTo = "") { if (string.IsNullOrEmpty(queueName)) { throw new ArgumentNullException("queueName", "must not empty"); } if (string.IsNullOrEmpty(messageId)) { throw new ArgumentNullException("messageId", "must not empty"); } if (string.IsNullOrEmpty(messageTypeName)) { throw new ArgumentNullException("messageTypeName", "must not empty"); } if (messageTimestamp == DateTime.MinValue) { throw new ArgumentNullException("messageTimestamp", "must not empty"); } if (string.IsNullOrEmpty(messageBody)) { throw new ArgumentNullException("messageBody", "must not empty"); } var body = Serializer.GetBytes(messageBody); var routingKey = queueName; var envelopedMessage = new EnvelopedMessage(messageId, messageTypeName, messageTimestamp, messageBody, routingKey, correlationId, replyTo); try { var context = new MessageSendingTransportationContext(string.Empty, new Dictionary <string, object> { { MessagePropertyConstants.MESSAGE_ID, string.IsNullOrEmpty(messageId) ? Guid.NewGuid().ToString() : messageId }, { MessagePropertyConstants.MESSAGE_TYPE, messageTypeName }, { MessagePropertyConstants.TIMESTAMP, messageTimestamp }, { MessagePropertyConstants.CONTENT_TYPE, Serializer.ContentType }, { MessagePropertyConstants.PAYLOAD, Serializer.GetString(body) }, { MessagePropertyConstants.ROUTING_KEY, routingKey }, { MessagePropertyConstants.REPLY_TO, replyTo }, { MessagePropertyConstants.CORRELATION_ID, correlationId } }); RetryPolicy.Retry(() => { using (var channel = _conn.CreateChannel()) { var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.DeliveryMode = 2; properties.ContentType = Serializer.ContentType; properties.MessageId = messageId; properties.Type = messageTypeName; properties.Timestamp = new AmqpTimestamp(DateTime2UnixTime.ToUnixTime(messageTimestamp)); if (!string.IsNullOrEmpty(correlationId) && !string.IsNullOrEmpty(replyTo)) { properties.CorrelationId = correlationId; properties.ReplyTo = replyTo; } channel.BasicPublish(exchange: string.Empty, routingKey: routingKey, mandatory: true, basicProperties: properties, body: body); channel.Close(); } }, cancelOnFailed: (retryCount, retryException) => { if (retryCount == 0) { _logger.Error($"Message [{messageTypeName}] \"{Serializer.GetString(body)}\" send to queue \"{queueName}\" failed.", retryException); } return(false); }, retryCondition: ex => IOHelper.IsIOError(ex) || RabbitMQExceptionHelper.IsChannelError(ex), maxRetryCount: 1, retryTimeInterval: 1000); if (_debugEnabled) { _logger.Info($"Message [{messageTypeName}] \"{Serializer.GetString(body)}\" send to queue \"{queueName}\" successful."); } } catch (Exception ex) { var realEx = ex is TargetInvocationException ? ex.InnerException : ex; _logger.Error($"Message [{ messageTypeName}] \"{Serializer.GetString(body)}\" send to queue \"{queueName}\" failed.", realEx); throw new MessageSendFailedException(envelopedMessage, string.Empty, realEx); } }
/// <summary> /// 发送消息 /// </summary> /// <param name="message">消息</param> /// <param name="routingKey">路由key</param> /// <returns></returns> public SendResult SendMessage(Message message, string routingKey) { if (_isRunning == 0) { return(new SendResult(SendStatus.Failed, null, "Couldn't send message when is not running.")); } if (message == null) { return(new SendResult(SendStatus.Failed, null, "Message is null.")); } if (!_topics.ContainsKey(message.Topic)) { return(new SendResult(SendStatus.Failed, null, $"Topic {message.Topic} not registered.")); } IRabbitMQChannel channel = null; try { var queueCount = _topics[message.Topic]; var queueId = string.IsNullOrEmpty(routingKey) ? 0 : (Crc16.GetHashCode(routingKey) % queueCount); var messageId = Guid.NewGuid().ToString(); var createdTime = message.CreatedTime == DateTime.MinValue ? DateTime.Now : message.CreatedTime; if (_channelPool.TryDequeue(out Tuple <DateTime, IRabbitMQChannel> item)) { channel = item.Item2; } else { channel = _amqpConnection.CreateModel(); channel.ConfirmSelect(); } var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.ContentType = message.ContentType ?? string.Empty; properties.MessageId = Guid.NewGuid().ToString(); properties.Type = message.Tag ?? string.Empty; properties.Timestamp = new AmqpTimestamp(DateTime2UnixTime.ToUnixTime(createdTime)); if (_delayedMessageEnabled && message.DelayedMilliseconds > 0) { properties.Headers = new Dictionary <string, object> { { "x-delay", message.DelayedMilliseconds } }; } channel.BasicPublish(exchange: _delayedMessageEnabled && message.DelayedMilliseconds > 0 ? $"{message.Topic}-delayed" : message.Topic, routingKey: queueId.ToString(), mandatory: true, basicProperties: properties, body: message.Body); if (!channel.WaitForConfirms(_sendMsgTimeout, out bool timedOut)) { return(new SendResult(timedOut ? SendStatus.Timeout : SendStatus.Failed, null, "Wait for confirms failed.")); } var storeResult = new MessageStoreResult(messageId, message.Code, message.Topic, queueId, createdTime, message.Tag); return(new SendResult(SendStatus.Success, storeResult, null)); } catch (Exception ex) { throw new System.IO.IOException("Send message has exception.", ex); } finally { if (channel != null) { _channelPool.Enqueue(new Tuple <DateTime, IRabbitMQChannel>(DateTime.Now, channel)); } } }
private void ConsumeByPull() { foreach (var topic in _topics.Keys) { var queueCount = _topics[topic]; var subscribeQueues = GetSubscribeQueues(queueCount, _consumerCount, _consumerSequence); var subTopic = GetSubTopic(topic); var channels = new ConcurrentDictionary <byte, Tuple <IModel, Thread> >(); if (!_globalChannels.TryAdd(subTopic, channels)) { throw new Exception($"{subTopic} has subscribed."); } for (byte queueIndex = 0; queueIndex < queueCount; queueIndex++) { if (subscribeQueues == null || !subscribeQueues.Any(a => a == queueIndex)) { continue; } channels.TryGetValue(queueIndex, out Tuple <IModel, Thread> channelTuple); if (channelTuple != null) { continue; } try { var queueName = GetQueue(topic, queueIndex); var channel = _amqpConnection.CreateModel(); var consumerThread = new Thread((state) => { var currentChannelTopicQueueIndexPair = (Tuple <IModel, string, int>)state; var currentChannel = currentChannelTopicQueueIndexPair.Item1; var currentTopic = currentChannelTopicQueueIndexPair.Item2; var currentQueueIndex = currentChannelTopicQueueIndexPair.Item3; var currentQueueName = GetQueue(currentTopic, currentQueueIndex); int unackCount = 0; int noMsgCount = 0; while (true) { if (!currentChannel.IsOpen) { var closeReason = currentChannel.CloseReason; if (closeReason.ReplyCode == RabbitMQConstants.ConnectionForced) { break; } if (closeReason.ReplyCode == RabbitMQConstants.ChannelError) { Thread.Sleep(1000); continue; } else { throw new Exception($"{closeReason.ReplyText}"); } } if (OnMessageReceived == null) { Thread.Sleep(1000); continue; } if (unackCount > _prefetchCount) { // 当消费堆积数,达到设定值后,将延迟1秒拉消息 Thread.Sleep(1000); } try { var mqMessage = currentChannel.BasicGet(currentQueueName, false); if (mqMessage == null) { if (noMsgCount < 1000) { // 约10秒以内 Interlocked.Increment(ref noMsgCount); Thread.Sleep(10); } else if (noMsgCount < 1500) { // 约1分钟以内 Interlocked.Increment(ref noMsgCount); Thread.Sleep(100); } else { // 超过1分钟 Thread.Sleep(1000); } continue; } Interlocked.Exchange(ref noMsgCount, 0); Interlocked.Increment(ref unackCount); var context = new MessageHandlingTransportationContext(topic, currentQueueIndex, _groupName, currentChannel, mqMessage.DeliveryTag, new Dictionary <string, object> { { MessagePropertyConstants.MESSAGE_ID, mqMessage.BasicProperties.MessageId }, { MessagePropertyConstants.MESSAGE_TYPE, mqMessage.BasicProperties.Type }, { MessagePropertyConstants.TIMESTAMP, mqMessage.BasicProperties.Timestamp.UnixTime == 0 ? DateTime.Now : DateTime2UnixTime.FromUnixTime(mqMessage.BasicProperties.Timestamp.UnixTime) }, { MessagePropertyConstants.CONTENT_TYPE, string.IsNullOrEmpty(mqMessage.BasicProperties.ContentType) ? "text/json" : mqMessage.BasicProperties.ContentType }, { MessagePropertyConstants.BODY, mqMessage.Body }, { MessagePropertyConstants.ROUTING_KEY, mqMessage.RoutingKey } }); context.OnAck += (sender, e) => Interlocked.Decrement(ref unackCount); OnMessageReceived.Invoke(this, new MessageReceivedEventArgs(context)); } catch { } } }) { IsBackground = false }; channelTuple = new Tuple <IModel, Thread>(channel, consumerThread); consumerThread.Start(new Tuple <IModel, string, int>(channel, topic, queueIndex)); } finally { channels.TryAdd(queueIndex, channelTuple); } } } }
private void ConsumeByPush() { foreach (var topic in _topics.Keys) { var queueCount = _topics[topic]; var subscribeQueues = GetSubscribeQueues(queueCount, _consumerCount, _consumerSequence); var subTopic = GetSubTopic(topic); var consumers = new ConcurrentDictionary <byte, EventingBasicConsumer>(); if (!_globalConsumers.TryAdd(subTopic, consumers)) { throw new Exception($"{subTopic} has subscribed."); } for (byte queueIndex = 0; queueIndex < queueCount; queueIndex++) { if (subscribeQueues == null || !subscribeQueues.Any(a => a == queueIndex)) { continue; } consumers.TryGetValue(queueIndex, out EventingBasicConsumer consumer); if (consumer != null) { continue; } var channel = _amqpConnection.CreateModel(); channel.BasicQos(0, _prefetchCount, false); consumer = new EventingBasicConsumer(channel); try { consumer.Received += (sender, e) => { var currentConsumer = ((EventingBasicConsumer)sender); var currentChannel = currentConsumer.Model; while (!consumers.Any(w => w.Value.Model == currentChannel)) { Thread.Sleep(1000); } if (OnMessageReceived != null) { var currentTopic = e.Exchange.IndexOf("-delayed") > 0 ? e.Exchange.Substring(0, e.Exchange.LastIndexOf("-delayed")) : e.Exchange; var currentQueueIndex = consumers.First(w => w.Value.Model == currentChannel).Key; var context = new MessageHandlingTransportationContext(currentTopic, currentQueueIndex, _groupName, channel, e.DeliveryTag, new Dictionary <string, object> { { MessagePropertyConstants.MESSAGE_ID, e.BasicProperties.MessageId }, { MessagePropertyConstants.MESSAGE_TYPE, e.BasicProperties.Type }, { MessagePropertyConstants.TIMESTAMP, e.BasicProperties.Timestamp.UnixTime == 0 ? DateTime.Now : DateTime2UnixTime.FromUnixTime(e.BasicProperties.Timestamp.UnixTime) }, { MessagePropertyConstants.CONTENT_TYPE, string.IsNullOrEmpty(e.BasicProperties.ContentType) ? "text/json" : e.BasicProperties.ContentType }, { MessagePropertyConstants.BODY, e.Body }, { MessagePropertyConstants.ROUTING_KEY, e.RoutingKey } }); try { OnMessageReceived.Invoke(this, new MessageReceivedEventArgs(context)); } catch { } } }; consumer.Shutdown += (sender, e) => { var currentConsumer = ((EventingBasicConsumer)sender); var currentChannel = currentConsumer.Model; if (e.ReplyCode == RabbitMQConstants.ConnectionForced) { return; } while (e.ReplyCode == RabbitMQConstants.ChannelError && !currentChannel.IsOpen) { Thread.Sleep(1000); } }; channel.BasicConsume(GetQueue(topic, queueIndex), false, $"{_amqpConnection.ClientProvidedName}_consumer{queueIndex}", new Dictionary <string, object>(), consumer); } finally { consumers.TryAdd(queueIndex, consumer); } } } }
/// <summary> /// 发送消息 /// </summary> /// <param name="message">消息</param> public void SendMessage <T>(Envelope <T> message) where T : class { if (message == null || message.Body == null) { throw new ArgumentNullException("message"); } var body = Serializer.Serialize(message.Body); var messageTypeName = MessageTypeAttribute.GetTypeName(message.Body.GetType()); var routingKey = string.Empty; if (_publishToExchange) { uint routingKeyHashCode = 0; if (!uint.TryParse(message.RoutingKey, out routingKeyHashCode)) { routingKeyHashCode = (uint)message.RoutingKey.GetHashCode(); } routingKey = (routingKeyHashCode % _publishToExchangeQueueCount).ToString(); } else { routingKey = message.RoutingKey == null ? string.Empty : message.RoutingKey; } var envelopedMessage = new EnvelopedMessage(message.MessageId, messageTypeName, message.Timestamp, Serializer.GetString(body), routingKey, string.Empty, string.Empty); var context = new MessageSendingTransportationContext(ExchangeName, new Dictionary <string, object> { { MessagePropertyConstants.MESSAGE_ID, string.IsNullOrEmpty(message.MessageId) ? Guid.NewGuid().ToString() : message.MessageId }, { MessagePropertyConstants.MESSAGE_TYPE, messageTypeName }, { MessagePropertyConstants.TIMESTAMP, message.Timestamp }, { MessagePropertyConstants.CONTENT_TYPE, Serializer.ContentType }, { MessagePropertyConstants.PAYLOAD, Serializer.GetString(body) }, { MessagePropertyConstants.ROUTING_KEY, routingKey } }); try { TriggerOnMessageSent(new MessageSentEventArgs(this.GetSenderType(), context)); if (!IsRunning()) { throw new ObjectDisposedException(nameof(PubsubSender)); } IncreaseRetryingMessageCount(); RetryPolicy.Retry(() => { using (var channel = _conn.CreateChannel()) { var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.DeliveryMode = 2; properties.ContentType = Serializer.ContentType; properties.MessageId = message.MessageId; properties.Type = messageTypeName; properties.Timestamp = new AmqpTimestamp(DateTime2UnixTime.ToUnixTime(message.Timestamp)); if (_confirmEnabled) { channel.ConfirmSelect(); } channel.BasicPublish(exchange: ExchangeName, routingKey: routingKey, mandatory: _atLeastMatchOneQueue, basicProperties: properties, body: body); if (_confirmEnabled && !channel.WaitForConfirms()) { throw new Exception("Wait for confirm after sending message failed"); } } }, cancelOnFailed: (retryCount, retryException) => { return(false); }, retryCondition: ex => IOHelper.IsIOError(ex) || RabbitMQExceptionHelper.IsChannelError(ex), retryTimeInterval: 1000, maxRetryCount: _maxRetryCount); TriggerOnMessageSendingSucceeded(new MessageSendingSucceededEventArgs(this.GetSenderType(), context)); } catch (Exception ex) { var realEx = ex is TargetInvocationException ? ex.InnerException : ex; context.LastException = realEx; TriggerOnMessageSendingFailed(new MessageSendingFailedEventArgs(this.GetSenderType(), context)); throw new MessageSendFailedException(envelopedMessage, ExchangeName, realEx); } finally { DecreaseRetryingMessageCount(); } }
/// <summary> /// 发送请求 /// </summary> /// <param name="message">消息</param> /// <param name="methodName">方法名</param> /// <param name="correlationId">关联ID</param> public void SendRequest <T>(Envelope <T> message, string methodName, string correlationId) where T : class { if (message == null || message.Body == null) { throw new ArgumentNullException("message"); } var queueName = _disableQueuePrefix ? methodName : $"rpc.{methodName}"; var callbackQueueName = $"{queueName}.callback"; var body = Serializer.Serialize(message.Body); var messageTypeName = MessageTypeAttribute.GetTypeName(message.Body.GetType()); var routingKey = string.IsNullOrEmpty(ExchangeName) ? queueName : methodName; var envelopedMessage = new EnvelopedMessage(message.MessageId, messageTypeName, message.Timestamp, Serializer.GetString(body), routingKey, correlationId, callbackQueueName); var context = new MessageSendingTransportationContext(ExchangeName, new Dictionary <string, object> { { MessagePropertyConstants.MESSAGE_ID, string.IsNullOrEmpty(message.MessageId) ? Guid.NewGuid().ToString() : message.MessageId }, { MessagePropertyConstants.MESSAGE_TYPE, messageTypeName }, { MessagePropertyConstants.TIMESTAMP, message.Timestamp }, { MessagePropertyConstants.CONTENT_TYPE, Serializer.ContentType }, { MessagePropertyConstants.PAYLOAD, Serializer.GetString(body) }, { MessagePropertyConstants.ROUTING_KEY, routingKey }, { MessagePropertyConstants.REPLY_TO, callbackQueueName }, { MessagePropertyConstants.CORRELATION_ID, correlationId } }); TriggerOnMessageSent(new MessageSentEventArgs(this.GetSenderType(), context)); try { if (!IsRunning()) { throw new ObjectDisposedException(nameof(RpcClient)); } IncreaseRetryingMessageCount(); RetryPolicy.Retry(() => { using (var channel = _conn.CreateChannel()) { var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.DeliveryMode = 2; properties.ContentType = Serializer.ContentType; properties.MessageId = message.MessageId; properties.Type = messageTypeName; properties.Timestamp = new AmqpTimestamp(DateTime2UnixTime.ToUnixTime(message.Timestamp)); properties.ReplyTo = callbackQueueName; properties.CorrelationId = correlationId; if (_confirmEnabled) { channel.ConfirmSelect(); } channel.BasicPublish(exchange: ExchangeName, routingKey: routingKey, mandatory: true, basicProperties: properties, body: body); if (_confirmEnabled && !channel.WaitForConfirms()) { throw new Exception("Wait for confirm after sending message failed"); } } }, cancelOnFailed: (retryCount, retryException) => { return(false); }, retryCondition: ex => IOHelper.IsIOError(ex) || RabbitMQExceptionHelper.IsChannelError(ex), retryTimeInterval: 1000, maxRetryCount: _maxRetryCount); TriggerOnMessageSendingSucceeded(new MessageSendingSucceededEventArgs(this.GetSenderType(), context)); } catch (Exception ex) { var realEx = ex is TargetInvocationException ? ex.InnerException : ex; context.LastException = realEx; TriggerOnMessageSendingFailed(new MessageSendingFailedEventArgs(this.GetSenderType(), context)); throw new MessageSendFailedException(envelopedMessage, ExchangeName, realEx); } finally { DecreaseRetryingMessageCount(); } }