Comience con Cadence, un motor de flujo de trabajo de código abierto
Cadence simplifica la complejidad de los sistemas distribuidos para que los desarrolladores puedan centrarse en crear aplicaciones diseñadas para ofrecer una alta durabilidad, disponibilidad y escalabilidad.
Las aplicaciones modernas requieren interacciones complicadas entre procesos comerciales de larga duración, servicios internos y API de terceros. Decir que ha sido un desafío para los desarrolladores es decirlo suavemente. Gestionar estos procesos significa rastrear estados complejos, preparar respuestas a eventos asincrónicos y comunicarse con dependencias externas a menudo poco confiables.
Los desarrolladores suelen afrontar estos complejos desafíos con soluciones igualmente complicadas, que ensamblan sistemas difíciles de manejar que aprovechan servicios sin estado, bases de datos, algoritmos de reintento y colas de programación de trabajos. Debido a que estos sistemas complejos oscurecen su propia lógica de negocios, los problemas de disponibilidad son comunes, a menudo derivados de la dependencia de la aplicación de componentes dispersos y no probados. La productividad de los desarrolladores se sacrifica regularmente para evitar que estos sistemas problemáticos y en expansión colapsen.
Diseñar una aplicación distribuida
Cadence resuelve estos problemas ofreciendo una plataforma de código altamente escalable y ajena a los fallos. Cadence elimina los desafíos habituales de implementar tolerancia a fallas y durabilidad con su código ajeno a fallas.
Una aplicación Cadence estándar incluye un servicio Cadence, flujo de trabajo, trabajadores de actividad y clientes externos. Si es necesario, es aceptable ubicar conjuntamente los roles de los trabajadores del flujo de trabajo, los trabajadores de la actividad y los clientes externos en un único proceso de solicitud.
Servicio de cadencia
(Ben Slater, CC BY-SA 4.0)
Cadence se centra en su servicio multiinquilino y la alta escalabilidad que permite. Una API gRPC fuertemente tipada expone todas las funciones del servicio Cadence. Un clúster de Cadence puede ejecutar múltiples servicios en múltiples nodos, incluidos:
- Frontal: un servicio sin estado que maneja las solicitudes entrantes de los trabajadores, con instancias respaldadas por un equilibrador de carga externo.
- Servicio de historial: maneja la lógica central para los pasos del flujo de trabajo y la orquestación de actividades.
- Servicio de emparejamiento: empareja el flujo de trabajo o las tareas de actividad con los trabajadores listos para completarlas.
- Servicio de trabajador interno: cumple con los requisitos internos (como el archivado) al introducir flujos de trabajo y actividades de Cadence.
- Trabajadores: funcionan como aplicaciones cliente de Cadence que ejecutan el flujo de trabajo y la lógica de actividad creados por el usuario.
De forma predeterminada, Cadence admite Apache Cassandra, MySQL, PostgreSQL, CockroachDB y TiDB para su uso como almacenes de persistencia, así como ElasticSearch y OpenSearch para enumerar flujos de trabajo con predicados complejos.
Debido a que el servicio Cadence es multiinquilino, un solo servicio puede atender una o varias aplicaciones. Se puede configurar una instancia de servicio Cadence local con Docker-compose para el desarrollo local. El servicio Cadence mantiene estados de flujo de trabajo, temporizadores duraderos asociados y colas internas de "listas de tareas" para enviar tareas a trabajadores externos.
Más allá del propio servicio Cadence:
Trabajadores de flujo de trabajo: aloja código que no detecta fallas externamente al servicio Cadence. El servicio Cadence envía a estos trabajadores "tareas de decisión". Los trabajadores entregan las tareas al código de flujo de trabajo y comunican las "decisiones" completadas al servicio Cadence. El código de flujo de trabajo se puede implementar en cualquier lenguaje capaz de comunicarse con Cadence API: actualmente hay disponibles clientes Java y Go listos para producción.
Trabajadores de actividades: aloja "actividades" o código que realiza acciones específicas de la aplicación, como llamadas de servicio, actualizaciones de registros de bases de datos y descargas de archivos. Las actividades incluyen enrutamiento de tareas a procesos específicos, latidos, reintentos infinitos y tiempo de ejecución ilimitado. El servicio Cadence envía tareas de actividad a estos trabajadores, quienes las completan e informan su finalización.
Clientes externos: permiten la creación de instancias de flujo de trabajo o "ejecuciones". Los clientes externos, como UI, microservicios o CLI, utilizan la llamada API del servicio StartWorkflowExecution Cadence para implementar ejecuciones. Los clientes externos también son capaces de notificar a los flujos de trabajo sobre eventos externos asincrónicos, consultas de estado del flujo de trabajo sincrónico, espera de finalización del flujo de trabajo sincrónico, reinicios del flujo de trabajo, cancelación y búsqueda de flujos de trabajo específicos con List API.
Empezando con Cadencia
En este ejemplo usaremos el cliente Java Cadence. El cliente está disponible en GitHub y la documentación de JavaDoc se puede encontrar aquí. También puede buscar la última versión de lanzamiento.
Para comenzar, agregue cadence-client como una dependencia a su archivo pom.xml de esta manera:
<dependency>
<groupId>com.uber.cadence</groupId>
<artifactId>cadence-client</artifactId>
<version>LATEST.RELEASE.VERSION</version>
</dependency>
Alternativamente, puedes usar build.gradle
:
compile group: 'com.uber.cadence', name: 'cadence-client', version: 'LATEST.RELEASE.VERSION'
Java Hola Mundo con Cadencia
La mejor manera de tener una idea de lo que Cadence es capaz de hacer es probarlo, así que aquí tienes un ejemplo sencillo de "Hola mundo" que puedes probar. Primero, agregue la dependencia del cliente Cadence Java a su proyecto Java. Usando Gradle, la dependencia se ve así:
compile group: 'com.uber.cadence', name: 'cadence-client', version: '<latest_version>'
Agregue también estas dependencias que el cliente de cadencia requiere:
compile group: 'commons-configuration', name: 'commons-configuration', version: '1.9'
compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
Luego compila este código:
import com.uber.cadence.workflow.Workflow;
import com.uber.cadence.workflow.WorkflowMethod;
import org.slf4j.Logger;
public class GettingStarted {
private static Logger logger = Workflow.getLogger(GettingStarted.class);
public interface HelloWorld {
@WorkflowMethod
void sayHello(String name);
}
}
Estos ejemplos de Cadence Java están disponibles para ayudarle si tiene problemas con los archivos de compilación.
A continuación, coloque este archivo de configuración de inicio de sesión en su classpath:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="io.netty" level="INFO"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Ahora cree el flujo de trabajo Hola mundo. Agregue HelloWorldImpl con el método sayHello, que registra y devuelve "Hola...":
import com.uber.cadence.worker.Worker;
import com.uber.cadence.workflow.Workflow;
import com.uber.cadence.workflow.WorkflowMethod;
import org.slf4j.Logger;
public class GettingStarted {
private static Logger logger = Workflow.getLogger(GettingStarted.class);
public interface HelloWorld {
@WorkflowMethod
void sayHello(String name);
}
public static class HelloWorldImpl implements HelloWorld {
@Override
public void sayHello(String name) {
logger.info("Hello " + name + "!");
}
}
}
Registre la implementación del flujo de trabajo en el marco de Cadence con un trabajador conectado a un servicio de Cadence. Los trabajadores se conectarán a un servicio Cadence que se ejecuta localmente de forma predeterminada.
public static void main(String[] args) {
WorkflowClient workflowClient =
WorkflowClient.newInstance(
new WorkflowServiceTChannel(ClientOptions.defaultInstance()),
WorkflowClientOptions.newBuilder().setDomain(DOMAIN).build());
// Get worker to poll the task list.
WorkerFactory factory = WorkerFactory.newInstance(workflowClient);
Worker worker = factory.newWorker(TASK_LIST);
worker.registerWorkflowImplementationTypes(HelloWorldImpl.class);
factory.start();
}
Ahora está listo para ejecutar el programa de trabajo. Aquí hay un registro de ejemplo:
13:35:02.575 [main] INFO c.u.c.s.WorkflowServiceTChannel -
Initialized TChannel for service cadence-frontend, LibraryVersion:
2.2.0, FeatureVersion: 1.0.0
13:35:02.671 [main] INFO c.u.cadence.internal.worker.Poller - start():
Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000,
maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0,
pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S,
pollThreadCount=1,
pollThreadNamePrefix=‘Workflow Poller taskList="HelloWorldTaskList",
domain="test-domain", type="workflow"'}, identity=45937@maxim-C02XD0AAJGH6}
13:35:02.673 [main] INFO c.u.cadence.internal.worker.Poller - start():
Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000,
maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0,
pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S,
pollThreadCount=1, pollThreadNamePrefix=‘null'},
identity=81b8d0ac-ff89-47e8-b842-3dd26337feea}
"Hola" no se imprime porque el trabajador solo aloja el código del flujo de trabajo. Para ejecutar el flujo de trabajo, inícielo con Cadence CLI:
$ docker run --network=host --rm ubercadence/cli:master \
--do test-domain workflow start --tasklist HelloWorldTaskList \
--workflow_type HelloWorld::sayHello --execution_timeout 3600 \
--input "World"
Started Workflow Id: bcacfabd-9f9a-46ac-9b25-83bcea5d7fd7,
run Id: e7c40431-8e23-485b-9649-e8f161219efe
Ahora el programa da este resultado:
13:35:02.575 [main] INFO c.u.c.s.WorkflowServiceTChannel -
Initialized TChannel for service cadence-frontend,
LibraryVersion: 2.2.0, FeatureVersion: 1.0.0
[...]
13:40:28.308 [workflow-root] INFO c.u.c.samples.hello.GettingStarted
Hello World!
¡Éxito! Ahora ejecute esta ejecución de flujo de trabajo:
$ docker run --network=host --rm ubercadence/cli:master \
--do test-domain workflow start --tasklist HelloWorldTaskList \
--workflow_type HelloWorld::sayHello --execution_timeout 3600 \
--input "Cadence"
Started Workflow Id: d2083532-9c68-49ab-90e1-d960175377a7,
run Id: 331bfa04-834b-45a7-861e-bcb9f6ddae3e
Obtienes este resultado:
13:35:02.575 [main] INFO c.u.c.s.WorkflowServiceTChannel
Initialized TChannel for service cadence-frontend,
LibraryVersion: 2.2.0, FeatureVersion: 1.0.0
[...]
13:40:28.308 [workflow-root] INFO c.u.c.samples.hello.GettingStarted - Hello World!
13:42:34.994 [workflow-root] INFO c.u.c.samples.hello.GettingStarted - Hello Cadence!
Por último, utilice esta CLI para enumerar el flujo de trabajo:
$ docker run --network=host --rm ubercadence/cli:master \
--do test-domain workflow list
TYPE | WORKFLOW ID | START | EXEC | END TIME
HelloWorld::sayHello | d2083532... | 20:42 | 20:42| 20:42:35
HelloWorld::sayHello | bcacfabd... | 20:40 | 20:41| 20:41:29
Revise también el historial de ejecución del flujo de trabajo:
$ docker run --network=host --rm ubercadence/cli:master \
--do test-domain workflow showid 1965109f-607f-4b14-a5f2-24399a7b8fa7
1 WorkflowExecutionStarted {WorkflowType:{Name:HelloWorld::sayHello},
TaskList:{Name:HelloWorldTaskList},
Input:["World"],
ExecutionStartToCloseTimeoutSeconds:3600,
TaskStartToCloseTimeoutSeconds:10,
ContinuedFailureDetails:[],
LastCompletionResult:[],
Identity:cadence-cli@linuxkit-025000000001,
Attempt:0,
FirstDecisionTaskBackoffSeconds:0}
2 DecisionTaskScheduled {TaskList:{Name:HelloWorldTaskList},
StartToCloseTimeoutSeconds:10,
Attempt:0}
3 DecisionTaskStarted {ScheduledEventId:2,
Identity:45937@maxim-C02XD0AAJGH6,
RequestId:481a14e5-67a4-436e-9a23-7f7fb7f87ef3}
4 DecisionTaskCompleted {ExecutionContext:[],
ScheduledEventId:2,
StartedEventId:3,
Identity:45937@maxim-C02XD0AAJGH6}
5 WorkflowExecutionCompleted {Result:[],
DecisionTaskCompletedEventId:4}
Puede que sea un flujo de trabajo sencillo, pero mirar el historial es bastante informativo. El valor del historial como herramienta de resolución de problemas, análisis y cumplimiento solo aumenta con la complejidad del flujo de trabajo. Como práctica recomendada, archive automáticamente el historial en un almacén de blobs a largo plazo cuando se completen los flujos de trabajo.
Prueba cadencia
Cadence ofrece ventajas transformadoras para organizaciones y equipos de desarrollo de aplicaciones encargados de crear y administrar aplicaciones distribuidas de alta escala diseñadas para una alta durabilidad, disponibilidad y escalabilidad. Cadence está disponible para todos como software gratuito y de código abierto, lo que facilita que los equipos exploren sus capacidades y determinen si Cadence es una buena opción para sus organizaciones.
Usar Cadence es tan simple como clonar el repositorio Git para el servidor Cadence o la imagen del contenedor. Para obtener más detalles sobre cómo comenzar, visite: https://cadenceworkflow.io/docs/get-started/.