DSL 7 - Manejo Timeouts

Para Comenzar

El paso del tutorial utiliza el add_mapping_function solución del proyecto como su punto de partida.

Si en algún momento desea ver la solución a este paso, puede encontrarla en el handling_timeouts solución.

Action Timeouts

En esta sección, examinaremos cómo manejamos los tiempos de espera basados en acciones. Suponga que tenemos un flujo simple que realiza una llamada a un dominio externo, la llamada se realiza con éxito, pero luego nunca recibimos la respuesta asignada. En ese escenario, puede que deseemos realizar algún tipo de acción compensatoria.

Para ilustrar esto, vamos a utilizar nuestro sistema de Fraude y a examinar cómo hacemos frente a la falta de respuesta del dominio descendente.

Configuración de un Tiempo de Espera de Acción (en el DSL)

Configurar los tiempos de espera de acción en el DSL es simple, todo lo que debe hacer es agregar un tipo especial de Event Línea de comportamiento para indicar al flujo qué enfoque compensatorio adoptar.

Comencemos, por lo tanto, abriendo MPS y yendo a nuestro flujo. Luego, añadamos un nuevo Event Comportamiento ("Agregar Event Comportamiento").

Agreguemos los fundamentos de nuestro comportamiento de evento diciendo que está en el estado de "Verificación de Fraude".- ese es el estado en el que estaríamos si hubiéramos realizado con éxito nuestra llamada al sistema de fraude.

Ahora desde el "Para Event ", elegimos un nuevo tipo que no hemos utilizado antes" - el "Tiempo de Espera de Acción"

timeout 1

Una vez seleccionado, debemos elegir la acción que corresponde:

timeout 2

Lo primero que debe señalar aquí es el valor predeterminado que se ha aplicado "Cualquier acción" - esto significa que un tiempo de espera de "cualquier acción" en el estado de "verificación de fraude" invocará el comportamiento. En nuestro caso, solo invocamos la acción de "Verificar Fraude" al verificar el fraude, por lo que no hace demasiada diferencia, pero puede haber ocasiones en las que estemos ejecutando múltiples acciones y deseemos manejar el resultado de que cualquiera de ellas se agote.

Para nuestro escenario, puede dejar el valor predeterminado como "Cualquier acción" (ya que solo hay una acción) o simplemente seleccionar "Verificar Fraude" (ambas opciones funcionarán aquí).

Ahora necesitamos ingresar el "Mover a State " y "Realizar Acciones" según el comportamiento de cualquier otro evento. Aquí podríamos hacer cualquier cosa que normalmente desearíamos, como llamar a otras acciones, decisiones, etc. Sin embargo, para simplificar en este tutorial, simplemente moveremos nuestro flujo a un estado de "Tiempo Agotado".

Así que añadamos un nuevo estado llamado "Tiempo Agotado", lo marcaremos como terminal para tratarlo como el final del flujo y para informar a nuestro flujo de iniciación sobre lo que está sucediendo.

timeout 3

y establezca eso en nuestro estado de movimiento. No realizaremos más acciones.

El comportamiento de nuestro evento se verá como:

timeout 4

Eso es todo nuestro trabajo de DSL, ahora revisemos nuestro gráfico para nuestro flujo.

Abra el flujo yendo a Herramientas  Abrir Visor de Flujos. Esto abrirá el flujo y a primera vista, esto no parece diferente. Sin embargo, en la parte superior del panel veremos:

timeout 5

Así que ahora tenemos una nueva opción "Mostrar Timeouts ". Haga clic en esa casilla y "Aplique" las actualizaciones (nota-si ya tiene Flo Viewer abierto, puede que deba cerrarlo y volver a abrirlo para ver "Mostrar Timeouts ").

El nuevo gráfico mostrará:

timeout 6

Aquí podemos ver que en el tiempo de espera del chequeo de fraude estamos dirigiendo al nuevo estado de Tiempo Agotado.

Tenga en cuenta también que el gráfico muestra que esta transición ocurrirá en el evento "CheckFraudActionTimeoutEvent". Este evento es generado por la aplicación cuando la llamada de fraude excede el tiempo de espera.

Java Implementación

Como es habitual, comencemos nuestra implementación regenerando nuestra base de código.

mvn clean install

Comencemos por observar un poco cómo funciona el código.

Comenzará abriendo la interfaz "SchedulerPort" (parte del IPF Core):

public interface SchedulerPort {

    void schedule(ScheduleItem var1, Function<ScheduleItem, CompletableFuture<Void>> var2);

    void schedule(ScheduleItem var1, Function<ScheduleItem, CompletableFuture<Void>> var2, Duration var3);

    void cancel(ScheduleItem var1);

    void cancelAll(String var1);

    int getRetries(ScheduleItem var1);

}

Son estas funciones las que el código generado invocará cada vez que necesite establecer un horario. En nuestro caso, cada vez que se llame a una acción, invocará el método de programación y proporcionará un Elemento de Programación que contiene los detalles de la acción y un tipo de "TIMEOUT".

Así que si usted especifica a la scheduler que desea un tiempo de espera de 10s en esta acción, entonces devolverá un fallo después de 10s si el programa sigue activo. Sin embargo, si en ese tiempo se llama a una cancelación, entonces esto cerrará el scheduler.

El IPF application necesita una implementación del scheduler el puerto debe ser proporcionado como parte del dominio. Sin embargo, hasta ahora no hemos tenido que especificar uno. ¿Por qué es esto? Es porque, por defecto, una operación nula scheduler se proporciona. Lo que necesitamos hacer ahora es proporcionar una implementación adecuada para nuestro caso.

El Akka Scheduler

Usted podría aquí utilizar cualquier scheduler que se ajusta a la definición de interfaz anterior, para este tutorial utilizaremos otro proporcionado por el IPF framework, el AkkaScheduler.

Comencemos añadiendo una dependencia en el pom de nuestra aplicación ipf-tutorial-app (ipf-tutorial-app/pom.xml):

<dependency>
    <groupId>com.iconsolutions.ipf.core.platform</groupId>
    <artifactId>ipf-flo-scheduler-akka</artifactId>
</dependency>

(nota-puede que necesite recargar Maven para descargar la dependencia)

Al agregar la dependencia, Spring inyectará automáticamente una instancia del scheduler en nuestra aplicación. Por lo tanto, todo lo que necesitamos hacer es configurar nuestro dominio para utilizarlo, para esto simplemente necesitamos actualizar la declaración del dominio en IpfTutorialConfig.java para especificar el scheduler adaptador:

@Bean
public IpftutorialmodelDomain init(ActorSystem actorSystem, SchedulerPort schedulerAdapter) { (1)
    // All adapters should be added to the domain model
    return new IpftutorialmodelDomain. Builder(actorSystem)
            .withTutorialDomainFunctionLibraryAdapter(input -> CompletableFuture.completedStage(new DuplicateCheckResponseInput. Builder(input.getId(), AcceptOrRejectCodes. Accepted).build()))
            .withAccountingSystemActionAdapter(new SampleAccountingSystemActionAdapter())
            .withFraudSystemActionAdapter(new FraudSystemActionAdapter())
            .withDecisionLibraryAdapter(input ->
                    input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getIntrBkSttlmAmt().getValue().compareTo(BigDecimal. TEN)>0?
                            RunFraudCheckDecisionOutcomes. FRAUDREQUIRED: RunFraudCheckDecisionOutcomes. SKIPFRAUD)
            .withIpftutorialflowAggregateFunctionAdapter(input -> new ExtractCreditorAccountForFlowIpftutorialflowAggregateFunctionOutput(input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getCdtrAcct()))
            .withSchedulerAdapter(schedulerAdapter) (2)
            .build();
}
1 Tenga en cuenta la adición de SchedulerPort a la firma de inicialización.
2 Agregando el Adaptador de Programador al dominio

Eso es todo desde la perspectiva del código.

Configuración

Nuestro trabajo final es configurar nuestro scheduler. La configuración se realiza utilizando propiedades, la cadena de propiedades que requerimos tiene el formato:

ipf.flow.<FLOW_NAME>.<STATE_NAME>.<ACTION_NAME>.timeout-duration=<DURATION>

Lo primero que debe tener en cuenta aquí es que necesitamos proporcionar los valores de los tres parámetros opcionales.- nuestro nombre de flujo, nombre de estado, nombre de acción-y proporcione el valor para la duración.

En nuestro caso, entonces, nuestra propiedad debería verse así:

ipf.flow.Ipftutorialflow.CheckingFraud.CheckFraud.timeout-duration=2s

Lo primero que debe tener en cuenta al definir esta propiedad es que, donde tengamos espacios en cualquiera de los componentes, simplemente ignoramos el espacio. Así que, por ejemplo, nuestro nombre de acción es en realidad Check Fraud pero simplemente utilizamos CheckFraud. También estamos especificando una duración aquí de 2 segundos.

Agreguemos esta propiedad a la configuración de la aplicación. Para hacer esto, abra el archivo docker/config/ipf-tutorial-app/application.conf y añada la línea anterior.

Habilitando una Configuración de Prueba

Estamos casi listos para probar nuestro tiempo de espera; lo único que queda es hacer que la llamada de verificación de fraude tenga la capacidad de expirar. Actualicemos la definición de la verificación de fraude que proporcionamos anteriormente para no utilizar el adaptador de fraude de muestra, sino para permitir también un tiempo de espera opcional. Creamos un nuevo paquete en nuestro proyecto ipf-tutorial-app para las implementaciones de adaptadores.-com.iconsolutions.ipf.tutorial.app.adapters.

Luego, en nuestro nuevo paquete, añadiremos una implementación de FraudActionPort.

Utilizará la idea de "valores mágicos" para indicar que si se recibe un pago de valor >= 50 USD por la llamada de fraude, se producirá un tiempo de espera; de lo contrario, se devolverá exitosamente. Intente implementar esta lógica usted mismo (Sugerencia - Consulte el ejemplo de clase generado Sample Fraud System Action Adapter en domain-root/sampleapp como punto de partida. Cuando esté listo, compare su intento con la solución proporcionada a continuación:

@Slf4j
public class FraudSystemActionAdapter implements FraudSystemActionPort {

    @Override
    public CompletionStage<Void> execute(final CheckFraudAction action) {
        log.debug("Received an action of type {} for id {}", action.getActionName(), action.getId());
        if (action.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getIntrBkSttlmAmt().getValue().compareTo(new BigDecimal("50")) >= 0) {
            return CompletableFuture.runAsync(() -> log.debug("Pretending to timeout the fraud call for aggregate {}", action.getProcessingContext().getAssociationId()));
        } else {
            return IpftutorialmodelDomain.fraudSystem().handle(new FraudCheckResponseInput. Builder(action.getId(), AcceptOrRejectCodes. Accepted).build())
                    .thenAccept(done -> log.debug("Sent input of type {} for id {} with result {}", done.getCommandName(), action.getId(), done.getResult().name()));
        }
    }
}
  • El CompletionStage tipo es Void porque lo único que el llamador de un adaptador desea saber es si la acción se ha completado o no. Cualquier información que deba ser devuelta se pasa directamente a través del objeto de dominio del modelo (por ejemplo,IpftutorialmodelDomain en nuestro caso)

  • Si utilizó el Sample Fraud System Action Adapter como punto de partida, puede haber notado que la solución anterior no contiene un duration variable de clase o referencias a un CompletableFuture.delayedExecutor(..) método. Estos se incluyen en las definiciones de adaptador de muestra proporcionadas por sample-app para simular el execute(..) el método no devuelve una respuesta de inmediato, como sucedería en una implementación "real" del adaptador. Dado que existen solo para simular un retraso, podemos omitirlos de nuestra implementación del adaptador (no de muestra) (¡lo que la hace mucho más ordenada!).

  • Puede tener una implementación ligeramente diferente para el FraudCheck (construido al final de la sección 'DSL 4') y su solución puede utilizar diferentes clases (es decir, si no comenzó desde un clon nuevo al inicio de esta sección). Por ejemplo, lo anterior espera AcceptOrRejectCodes, para el FraudCheckResponseInput, pero puede haber implementado códigos de respuesta separados para el fraude.

Finalmente, necesitamos agregar nuestro nuevo adaptador a nuestra configuración como de costumbre (estamos cambiando el.with Fraud System Action Adapter para utilizar nuestro adaptador recién creado).FraudSystemActionAdapter):

@Bean
public IpftutorialmodelDomain init(ActorSystem actorSystem, SchedulerPort schedulerAdapter) {
    // All adapters should be added to the domain model
    return new IpftutorialmodelDomain. Builder(actorSystem)
            .withDomainFunctionAdapter(input -> CompletableFuture.completedStage(new DuplicateCheckResponseInput. Builder(input.getId(), AcceptOrRejectCodes. Accepted).build()))
            .withAccountingSystemActionAdapter(new SampleAccountingSystemActionAdapter())
            .withFraudSystemActionAdapter(new FraudSystemActionAdapter())
            .withDecisionAdapter(input ->
                    input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getIntrBkSttlmAmt().getValue().compareTo(BigDecimal. TEN)>0?
                            RunFraudCheckDecisionOutcomes. FRAUDREQUIRED: RunFraudCheckDecisionOutcomes. SKIPFRAUD)
            .withIpftutorialflowAggregateFunctionAdapter(input -> new ExtractCreditorAccountForFlowIpftutorialflowAggregateFunctionOutput(input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getCdtrAcct()))
            .withSchedulerAdapter(schedulerAdapter)
            .build();
}

Eso es todo completo, es hora de construir y activar el entorno del contenedor para verificar que todo funcione.

Verificando nuestra Solución

Como es habitual, ahora verifiquemos que la solución funcione. Inicie la aplicación como se indicó anteriormente (las instrucciones están disponibles en Revisando la solicitud inicial si necesita un repaso!)

Para los pagos, necesitamos reconsiderar la lógica que hemos construido:

  • Si un pago es superior a $50 (pero superior a 10 para asegurarse de que se requiere la verificación de fraude), entonces se agota el tiempo; si no, procedemos como antes.

Así que intentemos ambos escenarios comenzando con un pago superior a $50:

curl -X POST localhost:8080/submit -H 'Content-Type: application/json' -d '{"value": "150"}' | jq

Hablemos del pago en el Developer GUI y mencione el domain events ver (buscar por id de unidad de trabajo, haga clic en ver, haga clic domain events):

timeout 7

Aquí podemos ver que esta vez hemos recibido con éxito el evento de tiempo de espera. Para confirmarlo, si ahora repetimos el proceso con un valor de, digamos, $25, veremos que la verificación de fraude se ha procesado con éxito y no ha expirado, por lo que nuestro flujo ha continuado hasta su finalización.

Persistent Scheduling

IPF proporciona otro scheduling interfaz por defecto. Este es el persistent scheduler. Está respaldado por cuarzo y la principal diferencia es que está respaldado por una capa de persistencia y, como tal, si la aplicación falla, los horarios estarán disponibles después del apagado de la jvm.

Para aplicar el persistent scheduler simplemente necesitamos reemplazar el akka scheduler dependencia con la de nuestro persistent scheduler:

<dependency>
    <groupId>com.iconsolutions.ipf.core.platform</groupId>
    <artifactId>ipf-flo-scheduler-persistent</artifactId>
</dependency>

Si lo desea, adelante y pruebe este cambio, puede repetir las pruebas que realizamos anteriormente para demostrar que nuestra nueva scheduler está funcionando.

Conclusiones

En esta sección hemos aprendido cómo configurar tiempos de espera en las acciones y trabajar con cuándo se invocan.