Как обрабатывать команды, отправленные из саги в рамках аксона - PullRequest
1 голос
/ 03 ноября 2019

Используя сагу, учитывая событие EventA, сага запускается, она отправляет команду (или многие). Как мы можем убедиться, что команда успешно отправлена, тогда фактическая логика в другом микро-сервисе не сработала и т. Д.

Давайте рассмотрим пример саги по электронной почте: когда пользователь регистрируется, мы создаем агрегат пользователя, которыйпубликует UserRegisteredEvent, будет создана сага, и эта сага отвечает за то, чтобы убедиться, что регистрационное электронное письмо отправлено пользователю (электронное письмо может содержать ключ подтверждения, приветственное сообщение и т. д.).

Если мы используем:

  1. commandGateway.sendAndWait с попыткой / уловом -> масштабируется?

  2. commandGateway.send и использует крайний срок и использует какое-то "событие сбоя"«подобно SendEmailFailedEvent -> требует связать« токен »для команд, чтобы можно было связать« associationProperty »с правильной сагой, отправившей SendRegistrationEmailCommand

  3. commandGateway.send(...).handle(...) -> в дескрипторе. eventGateway / commandGateway, которые были в MyEmailSaga? Если ошибка, мы отправляем событие? Или мы можем изменить / вызвать метод из экземпляра саги, который у нас был. Если нет ошибок, то другие службы отправили событие, подобное «RegistrationEmailSentEvent», поэтому сага будет завершена.

  4. использовать крайний срок, потому что мы просто используем «отправить» и не обрабатываем возможную ошибку командыкоторые, возможно, не были отправлены (другие службы не работают и т. д.)

  5. что-то еще?

  6. Или комбинация всех?

Как обрабатывать ошибки ниже? (используйте крайний срок или .handle (...) или другое)

Возможны следующие ошибки:

  • команда не имеет обработчиков (нет обслуживания и т. д.)

  • команда была обработана, но возникла исключительная ситуация в другой службе, и никакое событие не отправлено (без попытки / перехвата в другой службе)

  • команда была обработана, исключениеПоднятый и перехваченный, другой сервис публикует событие, чтобы уведомить о том, что ему не удалось отправить электронное письмо (сага получит событие и предпримет соответствующие действия в зависимости от типа события и предоставленных данных -> возможно, электронное письмо неверно или не существует, поэтому не нужно повторять попытку)

  • другие ошибки, которые я пропустил?

@Saga
public class MyEmailSaga {

    @Autowired
    transient CommandGateway commandGateway;


    @Autowired
    transient EventGateway eventGateway;

    @Autowired
    transient SomeService someService;

    String id;
    SomeData state;
    /** count retry times we send email so can apply logic on it */
    int sendRetryCount;

    @StartSaga
    @SagaEventHandler(associationProperty = "id")
    public void on(UserRegisteredEvent event) {
        id = event.getApplicationId();
        //state = event........
        // what are the possibilities here? 
        // Can we use sendAndWait but it does not scale very well, right?
        commandGateway.send(new SendRegistrationEmailCommand(...));
        // Is deadline good since we do not handle the "send" of the command
    }

    // Use a @DeadlineHandler to retry ?

    @DeadlineHandler(deadlineName = "retry_send_registration_email")
    fun on() {
         // resend command and re-schedule a deadline, etc
    }

    @EndSaga
    @SagaEventHandler(associationProperty = "id")
    public void on(RegistrationEmailSentEvent event) {

    }

}

РЕДАКТИРОВАТЬ (после принятого ответа):

В основном два варианта (извините, но код котлина ниже):

Первый вариант

commandGateway.send(SendRegistrationEmailCommand(...))
    .handle({ t, result ->
    if (t != null) {
       // send event (could be caught be the same saga eventually) or send command or both
    }else{
       // send event (could be caught be the same saga eventually) or send command or both
    }
    })
// If not use handle(...) then you can use thenApply as well
    .thenApply { eventGateway.publish(SomeSuccessfulEvent(...)) }
    .thenApply { commandGateway.send(SomeSuccessfulSendOnSuccessCommand) }

2-й вариант: Используйте крайний срок, чтобы убедиться, что сагасделать что-то, если SendRegistrationEmailCommand не удалось, и вы не получили никаких событий в случае сбоя (когда вы не обрабатываетекоманда отправлена).

Конечно, можно использовать крайний срок для других целей.

Когда команда SendRegistrationEmailCommand была успешно получена, получатель опубликует событие, поэтому сага будет уведомлена и будет действовать в соответствии с ним. Может быть RegistrationEmailSentEvent или RegistrationEmailSendFailedEvent.

Сводка:

Кажется, что лучше использовать handle() только в том случае, если команда не была отправлена ​​или получатель получилнеожиданное исключение, если так, то опубликуйте событие для саги, чтобы воздействовать на него. В случае успеха получатель должен опубликовать событие, сага его прослушает (и в конечном итоге зарегистрирует крайний срок на всякий случай);Получатель может также отправить событие, чтобы уведомить об ошибке и не бросать, сага также будет слушать это событие.

1 Ответ

2 голосов
/ 07 ноября 2019

в идеале вы должны использовать асинхронные опции для устранения ошибок. Это будет либо commandGateway.send(command), либо commandGateway.send(command).thenApply(). Если сбой связан с бизнес-логикой, то может иметь смысл отправлять события об этих сбоях. Простое gateway.send(command) тогда имеет смысл;Сага может реагировать на события, возвращенные в результате. В противном случае вам придется иметь дело с результатом команды.

То, нужно ли вам использовать sendAndWait или просто send().then..., зависит от действия, которое необходимо выполнить в случае сбоя. К сожалению, когда вы работаете с результатами асинхронно, вы больше не можете безопасно изменять состояние саги. Аксон, возможно, уже сохранил состояние саги, что привело к потере этих изменений. sendAndWait решает это. Масштабируемость не часто является проблемой, потому что разные Sagas могут выполняться параллельно, в зависимости от конфигурации вашего процессора.

В настоящее время команда Axon рассматривает возможные API-интерфейсы, которые позволили бы обеспечить безопасное асинхронное выполнение логики в Sagas, в то время каксохраняя гарантии безопасности потоков и сохранения состояния.

...