Démarrez avec Cadence, un moteur de workflow open source

Les applications modernes nécessitent des interactions compliquées entre les processus métier de longue durée, les services internes et les API tierces. Dire que cela a été un défi pour les développeurs est un euphémisme. La gestion de ces processus implique le suivi d’états complexes, la préparation de réponses à des événements asynchrones et la communication avec des dépendances externes souvent peu fiables.
Les développeurs relèvent généralement ces défis complexes avec des solutions tout aussi alambiquées, assemblant des systèmes peu maniables qui exploitent des services sans état, des bases de données, des algorithmes de nouvelle tentative et des files d’attente de planification des tâches. Étant donné que ces systèmes complexes obscurcissent leur propre logique métier, les problèmes de disponibilité sont fréquents, souvent dus à la dépendance de l’application vis-à-vis de composants dispersés et non éprouvés. La productivité des développeurs est régulièrement sacrifiée pour empêcher ces systèmes tentaculaires et troublés de s’effondrer.
Conception d’une application distribuée
Cadence résout ces problèmes en proposant une plate-forme de code hautement évolutive et insensible aux erreurs. Cadence élimine les défis habituels de la mise en œuvre de la tolérance aux pannes et de la durabilité avec son code insensible aux pannes.
Une application Cadence standard comprend un service Cadence, un flux de travail, des agents d’activité et des clients externes. Si nécessaire, il est acceptable de regrouper les rôles des agents de flux de travail, des agents d’activité et des clients externes dans un seul processus d’application.
Service Cadence
(Ben Slater, CC BY-SA 4.0)
Cadence est centrée sur son service multi-tenant et la grande évolutivité qu’il permet. Un fortement typé API gRPC expose toutes les fonctionnalités du service Cadence. Un cluster Cadence peut exécuter plusieurs services sur plusieurs nœuds, notamment :
- L’extrémité avant: Un service sans état qui gère les demandes de travail entrantes, avec des instances soutenues par un équilibreur de charge externe.
- Service d’histoire : Gère la logique de base pour les étapes du flux de travail et l’orchestration des activités.
- Service correspondant : Associe les tâches de flux de travail ou d’activité avec les travailleurs prêts à les terminer.
- Service des travailleurs internes : Répond aux exigences internes (telles que l’archivage) en introduisant des flux de travail et des activités Cadence.
- Ouvriers: Fonctionne comme des applications clientes Cadence qui exécutent le flux de travail et la logique d’activité créés par l’utilisateur.
Par défaut, Cadence prend en charge Apache Cassandra, MySQL, PostgreSQL, CockroachDB et TiDB pour une utilisation en tant que magasins de persistance, ainsi qu’ElasticSearch et OpenSearch pour répertorier les workflows avec des prédicats complexes.
Étant donné que le service Cadence est multi-tenant, un seul service peut servir une ou plusieurs applications. Une instance de service Cadence locale peut être configurée avec docker-compose pour le développement local. Le service Cadence gère les états de flux de travail, les minuteurs durables associés et les files d’attente internes de “liste de tâches” pour envoyer des tâches à des travailleurs externes.
Au-delà du service Cadence lui-même :
-
Travailleurs du flux de travail: héberge le code insensible aux pannes en externe au service Cadence. Le service Cadence envoie à ces travailleurs des “tâches de décision”. Les travailleurs livrent les tâches au code de workflow et communiquent les « décisions » terminées au service Cadence. Le code de workflow peut être implémenté dans n’importe quel langage capable de communiquer avec l’API Cadence : des clients Java et Go prêts pour la production sont actuellement disponibles.
-
Travailleurs de l’activité : héberge des “activités” ou du code qui effectue des actions spécifiques à l’application telles que des appels de service, des mises à jour d’enregistrements de base de données et des téléchargements de fichiers. Les activités comportent un routage des tâches vers des processus spécifiques, des pulsations, des tentatives infinies et un temps d’exécution illimité. Le service Cadence envoie des tâches d’activité à ces travailleurs, qui les terminent et signalent leur achèvement.
-
Clients externes : permettre la création d’instances de workflow, ou “exécutions”. Les clients externes tels que les interfaces utilisateur, les microservices ou les CLI utilisent l’appel d’API du service StartWorkflowExecution Cadence pour mettre en œuvre les exécutions. Les clients externes sont également capables de notifier les flux de travail sur les événements externes asynchrones, les requêtes d’état de flux de travail synchrones, l’attente de l’achèvement du flux de travail synchrone, les redémarrages de flux de travail, l’annulation et la recherche de flux de travail spécifiques avec l’API List.
Premiers pas avec Cadence
Dans cet exemple, nous utiliserons le client Cadence Java. Le client est disponible sur GitHubet La documentation JavaDoc peut être trouvée ici. Vous pouvez également vérifier la dernière version.
Pour commencer, ajoutez cadence-client en tant que dépendance de votre pom.xml fichier comme celui-ci :
<dependency><groupId>com.uber.cadence</groupId>
<artifactId>cadence-client</artifactId>
<version>LATEST.RELEASE.VERSION</version>
</dependency>
Alternativement, vous pouvez utiliser build.gradle:
groupe de compilation : ‘com.uber.cadence’, nom : ‘cadence-client’, version : ‘LATEST.RELEASE.VERSION’
Java Hello World avec Cadence
La meilleure façon d’avoir une idée de ce dont Cadence est capable est de l’essayer, alors voici un exemple simple “Hello World” que vous pouvez essayer. Tout d’abord, ajoutez le Dépendance du client Cadence Java à votre projet Java. En utilisant Gradle, la dépendance ressemble à ceci :
groupe de compilation : ‘com.uber.cadence’, nom : ‘cadence-client’, version : ‘
Ajoutez ces dépendances dont le cadence-client a également besoin :
groupe de compilation : ‘commons-configuration’, nom : ‘commons-configuration’, version : ‘1.9’
groupe de compilation : ‘ch.qos.logback’, nom : ‘logback-classic’, version : ‘1.2.3’
Puis compilez ce code :
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);
}
}
Ces Exemples Java Cadence sont disponibles pour vous aider si vous rencontrez des problèmes avec les fichiers de construction.
Ensuite, placez ce fichier de configuration de logback dans votre 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>
Créez maintenant le workflow Hello World. Ajoutez HelloWorldImpl avec la méthode sayHello, qui enregistre et renvoie “Hello …”:
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 + "!");
}
}
}
Enregistrez la mise en œuvre du flux de travail dans le cadre Cadence avec un travailleur connecté à un service Cadence. Les travailleurs se connecteront à un service Cadence exécuté localement par défaut.
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();
}
Vous êtes maintenant prêt à exécuter le programme de travail. Voici un exemple de journal :
13:35:02.575 [main] INFO cucsWorkflowServiceTChannel – TChannel initialisé pour service cadence-frontend, LibraryVersion : 2.2.0, FeatureVersion : 1.0.0
13:35:02.671 [main] INFO cucadence.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”, domaine=”test-domain”, type=”workflow”‘}, identité=45937@maxim-C02XD0AAJGH6}
13:35:02.673 [main] INFO cucadence.internal.worker.Poller – start() : Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000, maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0, pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S, pollThreadCount=1, pollThreadNamePrefix=’null’ }, identité=81b8d0ac-ff89-47e8-b842-3dd26337feea}
« Hello » ne s’imprime pas, car le travailleur n’héberge que le code du flux de travail. Pour exécuter le workflow, démarrez-le avec la 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
Maintenant, le programme donne cette sortie :
13:35:02.575 [main] INFO cucsWorkflowServiceTChannel – TChannel initialisé pour service cadence-frontend, LibraryVersion : 2.2.0, FeatureVersion : 1.0.0
13:35:02.671 [main] INFO cucadence.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”, domaine=”domaine-test”, type=”workflow”‘}, identité=45937@maxim-C02XD0AAJGH6}
13:35:02.673 [main] INFO cucadence.internal.worker.Poller – start() : Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000, maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0, pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S, pollThreadCount=1, pollThreadNamePrefix=’null’ }, identité=81b8d0ac-ff89-47e8-b842-3dd26337feea}
13:40:28.308 [workflow-root] INFO cucsamples.hello.GettingStarted – Hello World !
Succès! Exécutez maintenant cette exécution de workflow :
$ 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
Vous devriez obtenir cette sortie :
13:35:02.575 [main] INFO cucsWorkflowServiceTChannel – TChannel initialisé pour service cadence-frontend, LibraryVersion : 2.2.0, FeatureVersion : 1.0.0
13:35:02.671 [main] INFO cucadence.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”, domaine=”test-domain”, type=”workflow”‘}, identité=45937@maxim-C02XD0AAJGH6}
13:35:02.673 [main] INFO cucadence.internal.worker.Poller – start() : Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000, maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0, pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S, pollThreadCount=1, pollThreadNamePrefix=’null’ }, identité=81b8d0ac-ff89-47e8-b842-3dd26337feea}
13:40:28.308 [workflow-root] INFO cucsamples.hello.GettingStarted – Hello World !
13:42:34.994 [workflow-root] INFO cucsamples.hello.GettingStarted – Bonjour Cadence !
Enfin, utilisez cette CLI pour répertorier le workflow :
$ docker run --network=host --rm ubercadence/cli:master --do test-domain workflow listWORKFLOW TYPE | WORKFLOW ID | RUN ID | START TIME | EXECUTION TIME | END TIME
HelloWorld::sayHello | d2083532-9c68-49ab-90e1-d960175377a7 | 331bfa04-834b-45a7-861e-bcb9f6ddae3e | 20:42:34 | 20:42:34 | 20:42:35
HelloWorld::sayHello | bcacfabd-9f9a-46ac-9b25-83bcea5d7fd7 | e7c40431-8e23-485b-9649-e8f161219efe | 20:40:28 | 20:40:28 | 20:40:29
Consultez également l’historique d’exécution du workflow :
$ 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}
Il peut s’agir d’un flux de travail simple, mais regarder l’historique est assez instructif. La valeur de l’historique en tant qu’outil de dépannage, d’analyse et de conformité ne fait qu’augmenter avec la complexité du flux de travail. Comme meilleure pratique, archivez automatiquement l’historique dans un magasin d’objets blob à long terme lorsque les workflows sont terminés.
Essayez la cadence
Cadence offre des avantages transformateurs aux organisations et aux équipes de développement d’applications chargées de créer et de gérer des applications distribuées à grande échelle conçues pour une durabilité, une disponibilité et une évolutivité élevées. Cadence est disponible pour tous en tant que logiciel gratuit et open source, ce qui permet aux équipes d’explorer facilement ses capacités et de déterminer si Cadence convient parfaitement à leur organisation.
Utiliser Cadence est aussi simple que de cloner le Référentiel Git pour le serveur Cadence ou la image de conteneur. Pour plus de détails sur la mise en route, visitez : https://cadenceworkflow.io/docs/get-started/.