/// <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> public void Subscribe() { var exceptionList = new List <Exception>(); foreach (var channel in _channels) { if (_consumers.Exists(e => e.Model == channel)) { continue; } var consumer = new EventingBasicConsumer(channel); try { consumer.Received += (sender, e) => { var currentConsumer = ((EventingBasicConsumer)sender); var currentChannel = currentConsumer.Model; HandleMessage(currentConsumer, e.BasicProperties, e.Exchange, QueueName, e.RoutingKey, e.Body, e.DeliveryTag, e.Redelivered, false); }; consumer.Shutdown += (sender, e) => { var currentConsumer = ((EventingBasicConsumer)sender); var currentChannel = currentConsumer.Model; _logger.Warn($"PubsubReceiver.Consumer.Shutdown: QueueName={QueueName}, ChannelIsOpen={currentChannel.IsOpen}, Initiator={e.Initiator}, ClassId={e.ClassId}, MethodId={e.MethodId}, ReplyCode={e.ReplyCode}, ReplyText={e.ReplyText}"); while (RabbitMQExceptionHelper.IsChannelError(e.ReplyCode) && !currentChannel.IsOpen) { Thread.Sleep(1000); } }; LockerExecuter.Execute(channel, () => { try { RetryPolicy.Retry(() => channel.BasicConsume(QueueName, false, consumer), retryCondition: ex => IOHelper.IsIOError(ex) || RabbitMQExceptionHelper.IsChannelError(ex), maxRetryCount: 1, retryTimeInterval: 1000); } catch (Exception ex) { var realEx = ex is TargetInvocationException ? ex.InnerException : ex; _logger.Error(realEx.Message, realEx); throw new TargetException(realEx.Message, realEx); } }); } catch (Exception ex) { exceptionList.Add(ex); } finally { _consumers.Add(consumer); } } if (exceptionList.Count > 0) { throw new AggregateException(exceptionList); } }
/// <summary> /// 接收请求 /// </summary> public void ReceiveRequest() { var exceptionList = new List <Exception>(); foreach (var methodName in MethodNameList) { var queueName = _disableQueuePrefix ? methodName : $"rpc.{methodName}"; var callbackQueueName = $"{queueName}.callback"; var channelList = _channels[methodName]; _consumers.TryGetValue(queueName, out List <EventingBasicConsumer> consumers); if (consumers == null) { consumers = new List <EventingBasicConsumer>(); } foreach (var channel in channelList) { if (consumers.Exists(e => e.Model == channel)) { continue; } var consumer = new EventingBasicConsumer(channel); try { consumer.Received += (sender, e) => { var currentConsumer = ((EventingBasicConsumer)sender); var currentChannel = currentConsumer.Model; while (!_channels.Any(w => w.Value.Contains(currentChannel))) { Thread.Sleep(1000); } var currentMethodName = _channels.First(w => w.Value.Contains(currentChannel)).Key; var currentQueueName = _disableQueuePrefix ? currentMethodName : $"rpc.{currentMethodName}"; HandleMessage(currentConsumer, e.BasicProperties, e.Exchange, currentQueueName, e.RoutingKey, e.Body, e.DeliveryTag, e.Redelivered, false); }; consumer.Shutdown += (sender, e) => { var currentConsumer = ((EventingBasicConsumer)sender); var currentChannel = currentConsumer.Model; while (!_channels.Any(w => w.Value.Contains(currentChannel))) { Thread.Sleep(1000); } var currentMethodName = _channels.First(w => w.Value.Contains(currentChannel)).Key; var currentQueueName = _disableQueuePrefix ? currentMethodName : $"rpc.{currentMethodName}"; _logger.Warn($"RpcServer.Consumer.Shutdown: QueueName={currentQueueName}, ChannelIsOpen={currentChannel.IsOpen}, Initiator={e.Initiator}, ClassId={e.ClassId}, MethodId={e.MethodId}, ReplyCode={e.ReplyCode}, ReplyText={e.ReplyText}"); while (RabbitMQExceptionHelper.IsChannelError(e.ReplyCode) && !currentChannel.IsOpen) { Thread.Sleep(1000); } }; LockerExecuter.Execute(channel, () => { try { RetryPolicy.Retry(() => channel.BasicConsume(queueName, false, consumer), retryCondition: ex => IOHelper.IsIOError(ex) || RabbitMQExceptionHelper.IsChannelError(ex), maxRetryCount: 1, retryTimeInterval: 1000); } catch (Exception ex) { var realEx = ex is TargetInvocationException ? ex.InnerException : ex; _logger.Error(realEx.Message, realEx); throw new TargetException(realEx.Message, realEx); } }); } catch (Exception ex) { exceptionList.Add(ex); } finally { consumers.Add(consumer); } } _consumers.TryAdd(queueName, consumers); } if (exceptionList.Count > 0) { throw new AggregateException(exceptionList); } }
/// <summary> /// 订阅指定队列 /// </summary> /// <param name="queueIndex">队列索引</param> public void Subscribe(byte queueIndex) { if (_isDisposing) { throw new ObjectDisposedException(nameof(TopicReceiver)); } if (queueIndex >= QueueCount) { throw new ArgumentOutOfRangeException("queueIndex", $"must less than {QueueCount}"); } var dlxQueueName = $"{ExchangeName}-dlx"; string queueName = $"{ExchangeName}-{queueIndex}"; using (var channelForConfig = _conn.CreateChannel()) { _conn.DeclareQueue(channelForConfig, queueName, new Dictionary <string, object> { { "x-dead-letter-exchange", "" }, { "x-dead-letter-routing-key", dlxQueueName }, }); channelForConfig.Close(); } _consumers.TryGetValue(queueIndex, out EventingBasicConsumer consumer); if (consumer != null) { return; } _channels.TryGetValue(queueIndex, out IModel channel); if (channel == null) { channel = _conn.CreateChannel(); channel.BasicQos(0, 1, false); } consumer = new EventingBasicConsumer(channel); try { consumer.Received += (sender, e) => { var currentConsumer = ((EventingBasicConsumer)sender); var currentChannel = currentConsumer.Model; while (!_channels.Any(w => w.Value == currentChannel)) { Thread.Sleep(1000); } var currentQueueIndex = _channels.First(w => w.Value == currentChannel).Key; var currentQueueName = $"{ExchangeName}-{currentQueueIndex}"; HandleMessage(currentConsumer, e.BasicProperties, e.Exchange, currentQueueName, e.RoutingKey, e.Body, e.DeliveryTag, e.Redelivered, true); }; consumer.Shutdown += (sender, e) => { var currentConsumer = ((EventingBasicConsumer)sender); var currentChannel = currentConsumer.Model; while (!_channels.Any(w => w.Value == currentChannel)) { Thread.Sleep(1000); } var currentQueueIndex = _channels.First(w => w.Value == currentChannel).Key; var currentQueueName = $"{ExchangeName}-{currentQueueIndex}"; _logger.Warn($"TopicReceiver.Consumer.Shutdown: ExchangeName={ExchangeName}, QueueName={currentQueueName}, ChannelIsOpen={currentChannel.IsOpen}, Initiator={e.Initiator}, ClassId={e.ClassId}, MethodId={e.MethodId}, ReplyCode={e.ReplyCode}, ReplyText={e.ReplyText}"); while (RabbitMQExceptionHelper.IsChannelError(e.ReplyCode) && !currentChannel.IsOpen) { Thread.Sleep(1000); } }; LockerExecuter.Execute(channel, () => { try { RetryPolicy.Retry(() => channel.BasicConsume(queueName, false, consumer), retryCondition: ex => IOHelper.IsIOError(ex) || RabbitMQExceptionHelper.IsChannelError(ex), maxRetryCount: 1, retryTimeInterval: 1000); } catch (Exception ex) { var realEx = ex is TargetInvocationException ? ex.InnerException : ex; _logger.Error(realEx.Message, realEx); throw new TargetException(realEx.Message, realEx); } }); } finally { _channels.TryAdd(queueIndex, channel); _consumers.TryAdd(queueIndex, consumer); } }