/// <summary> /// 通知Mq消息处理结果 /// </summary> /// <param name="ea"></param> /// <param name="currentConsumerLoopChannel"></param> /// <param name="consumeHandleResult"></param> /// <param name="storedException"></param> private void AcknowledgeMessage(BasicDeliverEventArgs ea, IModel currentConsumerLoopChannel, ConsumeHandleResult consumeHandleResult, object storedException = null) { try { switch (consumeHandleResult) { case ConsumeHandleResult.Act: currentConsumerLoopChannel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); //重试消息处理逻辑 if (ea.BasicProperties.MessageId.IsNotNullOrWhitespace()) { var messageId = ea.BasicProperties.MessageId; var consumerErrorMessageRepository = ServiceProvider.GetService <IConsumerErrorMessageStore>(); consumerErrorMessageRepository.DeleteAsync(messageId.SafeToObjectId()).Wait(); } break; case ConsumeHandleResult.ActAndMoveToErrorQueue: currentConsumerLoopChannel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); //重试消息处理逻辑 if (ea.BasicProperties.MessageId.IsNotNullOrWhitespace()) { var messageId = ea.BasicProperties.MessageId; var consumerErrorMessageRepository = ServiceProvider.GetService <IConsumerErrorMessageStore>(); var message = consumerErrorMessageRepository.GetByIdAsync(messageId.SafeToObjectId()).Result; message.ProcessTimes += 1; //已重试次数+1 var originalBodyMessage = Encoding.UTF8.GetString(ea.Body); var orignalBodyMessageObject = BsonSerializer.Deserialize <BsonDocument>(originalBodyMessage); var update = Builders <ConsumerErrorMessage> .Update .Set(x => x.ProcessTimes, message.ProcessTimes) .Set(x => x.RetryTime, DateTime.Now) .Set(x => x.RetryNow, 0) .Set(x => x.RetryLock, null) .Set(x => x.Exception, storedException.ToBsonDocument()) .Set(x => x.LastedOriginalMessage, orignalBodyMessageObject) .Set(x => x.NextRetryTime, DateTime.Now.AddMinutes(Math.Pow(_retryTimeSpanBaseSeconds, message.ProcessTimes)));//计算下次重试时间 consumerErrorMessageRepository.UpdateAsync(Builders <ConsumerErrorMessage> .Filter.Eq(x => x.Sysid, messageId.SafeToObjectId()), update).Wait(); } else { //如当前Consumer开启MoveToErrorQueue则推送到错误队列 if (MoveToErrorQueueWhenErrorOccured) { var originalBodyMessage = Encoding.UTF8.GetString(ea.Body); var orignalBodyMessageObject = JObject.Parse(originalBodyMessage); var errorMessageModel = new { RoutingKey = ea.RoutingKey, Exchange = ea.Exchange, Logged = DateTime.Now, OrignalMessage = orignalBodyMessageObject, Exception = storedException, CanRetry = MessageCanRetryWhenErrorOccured ? 1 : 0, ProcessTimes = 1, NextRetryTime = DateTime.Now.AddMinutes(Math.Pow(_retryTimeSpanBaseSeconds, 1)) //计算第二次的重试时间 }; var uncompressedMessage = MessageHelper.GetMessage(JsonConvert.SerializeObject(errorMessageModel, new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }), Encoding.UTF8); var sendMessage = MessageHelper.CompressMessage(uncompressedMessage, CompressionTypes.None); RecordConsumerError(ea.RoutingKey, sendMessage); } } break; case ConsumeHandleResult.Reject: currentConsumerLoopChannel.BasicReject(ea.DeliveryTag, true); //拒绝消息, 重新插入队列 break; default: break; } } catch (Exception ex) { _logger.LogError($"MessageConsume消息消费发生异常之后, 在处理异常的过程中再次发生错误, EXType:{ex.GetType().FullName} 错误详细信息: {ex.FullMessage()}"); } }
/// <summary> /// 消息的接收事件处理 /// </summary> /// <param name="sender"></param> /// <param name="ea"></param> private void ConsumerReceiveHandler(object sender, BasicDeliverEventArgs ea) { StopwatchAction((stopwatch) => { //If the channel is closed, directly return no need to handle; var currentConsumerLoopChannel = (sender as IBasicConsumer)?.Model; if (currentConsumerLoopChannel == null || currentConsumerLoopChannel.IsClosed) { return; } if (CancellationToken.IsCancellationRequested) { StopConsume();//释放本地对象 return; } ConsumeHandleResult consumeHandleResult = ConsumeHandleResult.Act; try { var bodyMessage = Encoding.UTF8.GetString(ea.Body); var cts = new CancellationTokenSource(HostingEnvironment?.IsDevelopment() ?? false ? TimeSpan.FromMinutes(30) : TimeSpan.FromMinutes(30)); using (var serviceScope = ServiceProvider.CreateScope()) { ConsumerAsync(ea, bodyMessage, serviceScope.ServiceProvider).Wait(cts.Token); } consumeHandleResult = ConsumeHandleResult.Act; AcknowledgeMessage(ea, currentConsumerLoopChannel, consumeHandleResult); } catch (AggregateException ex) { _logger.LogError(ex, $"消息消费时出现异常,将推送到消费错误队列进行人工处理, 详细错误:{ex.FullMessage()}"); consumeHandleResult = ConsumeHandleResult.ActAndMoveToErrorQueue; var storedException = ex.InnerExceptions.Select(x => new { Message = x.FullMessage(), StackTrace = x.FullStacktrace(), ExType = x.FullExType() }).ToList(); AcknowledgeMessage(ea, currentConsumerLoopChannel, consumeHandleResult, new { Errors = storedException }); } catch (TaskCanceledException ex) { consumeHandleResult = ConsumeHandleResult.ActAndMoveToErrorQueue; _logger.LogError(ex, $"消息消费出现[TaskCanceledException]异常,并将推送到消费错误队列进行人工处理, 详细错误:{ex.FullMessage()}"); var storedException = new { Message = ex.FullMessage(), StackTrace = ex.FullStacktrace(), ExType = ex.FullExType() }; AcknowledgeMessage(ea, currentConsumerLoopChannel, consumeHandleResult, storedException); } catch (OperationCanceledException ex) { consumeHandleResult = ConsumeHandleResult.ActAndMoveToErrorQueue; _logger.LogError(ex, $"消息消费超时OperationCanceledException(超时时间120秒), 并将推送到消费错误队列进行人工核查处理, 详细错误:{ex.FullMessage()}"); var storedException = new { Message = ex.FullMessage(), StackTrace = ex.FullStacktrace(), ExType = ex.FullExType() }; AcknowledgeMessage(ea, currentConsumerLoopChannel, consumeHandleResult, storedException); } catch (Exception ex) { consumeHandleResult = ConsumeHandleResult.ActAndMoveToErrorQueue; _logger.LogError(ex, $"消息消费出现异常[{ex.GetType().FullName}],并将推送到消费错误队列进行人工处理, 详细错误:{ex.FullMessage()}"); var storedException = new { Message = ex.FullMessage(), StackTrace = ex.FullStacktrace(), ExType = ex.FullExType() }; AcknowledgeMessage(ea, currentConsumerLoopChannel, consumeHandleResult, storedException); //only no particular exception have the retry } }, SessionName + " consumer message used:{0} ms", sw => { //todo: 耗时分析 }); }