protected Task ConsumeReply(IMessageContext reply) { try { Logger.LogInformation("Handle reply:{0} content:{1}", reply.MessageId, reply.ToJson()); var sagaInfo = reply.SagaInfo; var correlationId = sagaInfo?.SagaId ?? reply.CorrelationId; var messageState = _commandStateQueues.TryGetValue(correlationId); if (messageState != null) { _commandStateQueues.TryRemove(correlationId); if (reply.Message is Exception exception) { messageState.ReplyTaskCompletionSource.TrySetException(exception); } else { messageState.ReplyTaskCompletionSource.TrySetResult(reply.Message); } } } catch (Exception e) { Logger.LogError(e); } finally { _internalConsumer.CommitOffset(reply); } return(Task.CompletedTask); }
protected async Task ConsumeMessage(IMessageContext eventContext) { try { Logger.LogDebug($"start handle event {ConsumerId} {eventContext.Message.ToJson()}"); var message = eventContext.Message; var sagaInfo = eventContext.SagaInfo; var messageHandlerTypes = HandlerProvider.GetHandlerTypes(message.GetType()); if (messageHandlerTypes.Count == 0) { Logger.LogDebug($"event has no handlerTypes, messageType:{message.GetType()} message:{message.ToJson()}"); InternalConsumer.CommitOffset(eventContext); return; } //messageHandlerTypes.ForEach(messageHandlerType => foreach (var messageHandlerType in messageHandlerTypes) { using (var scope = ObjectProviderFactory.Instance .ObjectProvider .CreateScope(builder => builder.RegisterInstance(typeof(IMessageContext), eventContext))) { var messageStore = scope.GetService <IMessageStore>(); var subscriptionName = $"{SubscriptionName}.{messageHandlerType.Type.FullName}"; using (Logger.BeginScope(new { eventContext.Topic, eventContext.MessageId, eventContext.Key, subscriptionName })) { if (!messageStore.HasEventHandled(eventContext.MessageId, subscriptionName)) { var eventMessageStates = new List <MessageState>(); var commandMessageStates = new List <MessageState>(); var eventBus = scope.GetService <IEventBus>(); try { var messageHandler = scope.GetRequiredService(messageHandlerType.Type); using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled)) { if (messageHandlerType.IsAsync) { await((dynamic)messageHandler).Handle((dynamic)message) .ConfigureAwait(false); } else { await Task.Run(() => { ((dynamic)messageHandler).Handle((dynamic)message); }) .ConfigureAwait(false); } //get commands to be sent eventBus.GetCommands() .ForEach(cmd => commandMessageStates.Add(new MessageState(CommandBus?.WrapCommand(cmd, sagaInfo: sagaInfo, producer: Producer))) ); //get events to be published eventBus.GetEvents() .ForEach(msg => { var topic = msg.GetFormatTopic(); eventMessageStates.Add(new MessageState(MessageQueueClient.WrapMessage(msg, topic: topic, key: msg.Key, sagaInfo: sagaInfo, producer: Producer))); }); eventBus.GetToPublishAnywayMessages() .ForEach(msg => { var topic = msg.GetFormatTopic(); eventMessageStates.Add(new MessageState(MessageQueueClient.WrapMessage(msg, topic: topic, key: msg.Key, sagaInfo: sagaInfo, producer: Producer))); }); eventMessageStates.AddRange(GetSagaReplyMessageStates(sagaInfo, eventBus)); messageStore.HandleEvent(eventContext, subscriptionName, commandMessageStates.Select(s => s.MessageContext), eventMessageStates.Select(s => s.MessageContext)); transactionScope.Complete(); } if (commandMessageStates.Count > 0) { CommandBus?.SendMessageStates(commandMessageStates); } if (eventMessageStates.Count > 0) { MessagePublisher?.SendAsync(CancellationToken.None, eventMessageStates.ToArray()); } } catch (Exception e) { eventMessageStates.Clear(); messageStore.Rollback(); if (e is DomainException exception) { var domainExceptionEvent = exception.DomainExceptionEvent; if (domainExceptionEvent != null) { var topic = domainExceptionEvent.GetFormatTopic(); var exceptionMessage = MessageQueueClient.WrapMessage(domainExceptionEvent, eventContext.MessageId, topic, producer: Producer); eventMessageStates.Add(new MessageState(exceptionMessage)); } Logger.LogWarning(e, message.ToJson()); } else { //IO error or sytem Crash //if we meet with unknown exception, we interrupt saga if (sagaInfo != null) { eventBus.FinishSaga(e); } Logger.LogError(e, message.ToJson()); } eventBus.GetToPublishAnywayMessages() .ForEach(msg => { var topic = msg.GetFormatTopic(); eventMessageStates.Add(new MessageState(MessageQueueClient.WrapMessage(msg, topic: topic, key: msg.Key, sagaInfo: sagaInfo, producer: Producer))); }); eventMessageStates.AddRange(GetSagaReplyMessageStates(sagaInfo, eventBus)); messageStore.SaveFailHandledEvent(eventContext, subscriptionName, e, eventMessageStates.Select(s => s.MessageContext).ToArray()); if (eventMessageStates.Count > 0) { MessagePublisher?.SendAsync(CancellationToken.None, eventMessageStates.ToArray()); } } } } } } } catch (Exception e) { Logger.LogCritical(e, $"Handle event failed event: {eventContext.ToJson()}"); } InternalConsumer.CommitOffset(eventContext); }
protected virtual async Task ConsumeMessage(IMessageContext commandContext) { Stopwatch watch = Stopwatch.StartNew(); try { var command = commandContext.Message as ICommand; var needReply = !string.IsNullOrEmpty(commandContext.ReplyToEndPoint); var sagaInfo = commandContext.SagaInfo; if (command == null) { InternalConsumer.CommitOffset(commandContext); return; } var needRetry = command.NeedRetry; using (var scope = ObjectProviderFactory.Instance .ObjectProvider .CreateScope(builder => builder.RegisterInstance(typeof(IMessageContext), commandContext))) { using (Logger.BeginScope(new { commandContext.Topic, commandContext.MessageId, commandContext.Key })) { var messageStore = scope.GetService <IMessageStore>(); var eventMessageStates = new List <MessageState>(); var commandHandledInfo = await messageStore.GetCommandHandledInfoAsync(commandContext.MessageId) .ConfigureAwait(false); IMessageContext messageReply = null; if (commandHandledInfo != null) { if (needReply) { messageReply = MessageQueueClient.WrapMessage(commandHandledInfo.Result, commandContext.MessageId, commandContext.ReplyToEndPoint, producer: Producer, messageId: ObjectId.GenerateNewId().ToString()); eventMessageStates.Add(new MessageState(messageReply)); } } else { var eventBus = scope.GetService <IEventBus>(); var messageHandlerType = HandlerProvider.GetHandlerTypes(command.GetType()).FirstOrDefault(); Logger.LogInformation("Handle command, commandID:{0}", commandContext.MessageId); if (messageHandlerType == null) { Logger.LogDebug($"command has no handlerTypes, message:{command.ToJson()}"); if (needReply) { messageReply = MessageQueueClient.WrapMessage(new NoHandlerExists(), commandContext.MessageId, commandContext.ReplyToEndPoint, producer: Producer); eventMessageStates.Add(new MessageState(messageReply)); } } else { object messageHandler = null; do { try { if (messageHandler == null) { messageHandler = scope.GetRequiredService(messageHandlerType.Type); } using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled)) { if (messageHandlerType.IsAsync) { await((dynamic)messageHandler).Handle((dynamic)command) .ConfigureAwait(false); } else { var handler = messageHandler; await Task.Run(() => { ((dynamic)handler).Handle((dynamic)command); }).ConfigureAwait(false); } if (needReply) { messageReply = MessageQueueClient.WrapMessage(commandContext.Reply, commandContext.MessageId, commandContext.ReplyToEndPoint, producer: Producer); eventMessageStates.Add(new MessageState(messageReply)); } eventBus.GetEvents() .ForEach(@event => { var topic = @event.GetFormatTopic(); var eventContext = MessageQueueClient.WrapMessage(@event, commandContext.MessageId, topic, @event.Key, sagaInfo: sagaInfo, producer: Producer); eventMessageStates.Add(new MessageState(eventContext)); }); eventBus.GetToPublishAnywayMessages() .ForEach(@event => { var topic = @event.GetFormatTopic(); var eventContext = MessageQueueClient.WrapMessage(@event, commandContext.MessageId, topic, @event.Key, sagaInfo: sagaInfo, producer: Producer); eventMessageStates.Add(new MessageState(eventContext)); }); eventMessageStates.AddRange(GetSagaReplyMessageStates(sagaInfo, eventBus)); await messageStore.SaveCommandAsync(commandContext, commandContext.Reply, eventMessageStates.Select(s => s.MessageContext).ToArray()) .ConfigureAwait(false); transactionScope.Complete(); } needRetry = false; } catch (Exception e) { eventMessageStates.Clear(); messageStore.Rollback(); if (e is DBConcurrencyException && needRetry) { eventBus.ClearMessages(); } else { if (needReply) { messageReply = MessageQueueClient.WrapMessage(e, commandContext.MessageId, commandContext.ReplyToEndPoint, producer: Producer, messageId: ObjectId.GenerateNewId().ToString()); eventMessageStates.Add(new MessageState(messageReply)); } eventBus.GetToPublishAnywayMessages() .ForEach(@event => { var topic = @event.GetFormatTopic(); var eventContext = MessageQueueClient.WrapMessage(@event, commandContext.MessageId, topic, @event.Key, sagaInfo: sagaInfo, producer: Producer); eventMessageStates.Add(new MessageState(eventContext)); }); if (e is DomainException exception) { var domainExceptionEvent = exception.DomainExceptionEvent; if (domainExceptionEvent != null) { var topic = domainExceptionEvent.GetFormatTopic(); var exceptionMessage = MessageQueueClient.WrapMessage(domainExceptionEvent, commandContext.MessageId, topic, producer: Producer); eventMessageStates.Add(new MessageState(exceptionMessage)); } Logger.LogWarning(e, command.ToJson()); } else { Logger.LogError(e, command.ToJson()); //if we meet with unknown exception, we interrupt saga if (sagaInfo != null) { eventBus.FinishSaga(e); } } eventMessageStates.AddRange(GetSagaReplyMessageStates(sagaInfo, eventBus)); await messageStore.SaveFailedCommandAsync(commandContext, e, eventMessageStates.Select(s => s.MessageContext) .ToArray()) .ConfigureAwait(false); needRetry = false; } } } while (needRetry); } } if (eventMessageStates.Count > 0) { var sendTask = MessagePublisher.SendAsync(CancellationToken.None, eventMessageStates.ToArray()); // we don't need to wait the send task complete here. } } } } catch (Exception ex) { Logger.LogCritical(ex, $"{ConsumerId} CommandProcessor consume command failed"); } finally { watch.Stop(); Logger.LogDebug($"{commandContext.ToJson()} consumed cost:{watch.ElapsedMilliseconds}ms"); } InternalConsumer.CommitOffset(commandContext); }