07 Juni 2022

Workflows mit Cadence optimieren!

1. Was ist Cadence?

Cadence Logo

Beim Radfahren ist Kadenz (engl. Cadence) ein anderes Wort für Trittfrequenz, also die Drehzahl, mit der die Tretkurbel betätigt wird. Das Fahren mit hoher Kadenz wird Spinning genannt. Beim Fahren mit niedriger Kadenz, dem sogenannten Grinding oder Mashing, ist man langsamer. Und es ist schlecht für die Kniegelenke.

Die rasante Zunahme von Big-Data-Anwendungsfällen in den letzten zehn Jahren wurde durch beliebte, hochgradig skalierbare Open-Source-Technologien wie Apache Cassandra® für Speicherung, Apache Kafka® für Streaming und OpenSearch® für Suchen noch beschleunigt. Nun begrüßen wir ein neues Mannschaftsmitglied: Cadence zur Workflow-Orchestrierung.

Mein Mitbewohner ist mit seinem Cello immer auf dem Fahrrad zur Musikschule gefahren. Das sah ungefähr so wie auf dem Foto aus. Aber Musik und Fahrradfahren haben noch etwas anderes gemeinsam: Bei beidem arbeitet man mit Kadenzen!

(Quelle: Adobe Stock)

In der Musik ist eine Kadenz eine musikalische Phrase, die den Abschluss eines Stücks oder eines Abschnitts markiert. Beim Radfahren zeigt die Kadenz an, wie hoch die Trittfrequenz des Radfahrers ist, was sich wiederum auf die Effizienz und das Tempo der Fahrt auswirkt.

Als Workflow-Orchestrierung wird der Prozess bezeichnet, mit dem Reihen von Aufgaben festgelegt und ausgeführt werden, sowie die Reihenfolge, in der sie ausgeführt werden. Einige Aufgaben müssen vielleicht nacheinander ausgeführt werden, andere gleichzeitig. Nach diesem Prinzip funktionieren auch Orchesterpartituren, aus denen Dirigent, Musiker und Chor ablesen können, welche Noten wann zu spielen bzw. zu singen sind.

Von Max Reger—IMSLP, lizenzfrei, (https://commons.wikimedia.org/w/index.php?curid=51020706)

In der üblichen Workflow-Terminologie (z. B. der von BPMN) besteht ein Workflow aus Start- und Endereignissen, Activities (Atomic Task oder Composite Task), Gateways (Verzweigungen) und Sequenzen bzw. Flüssen – die Reihenfolge von Activities in einem Workflow.

In der Informatik habe ich einige der früheren Versuche erlebt, Workflows zu implementieren und zu modellieren, u. a. Enterprise Java Beans (Stateful Session Beans wurden für Workflows verwendet), ebXML, BPEL und die Modellierung und Evaluierung von Workflows mithilfe von ereignisorientierter Simulation. In der traditionellen Workflow-Modellierung wird die Ausführungssemantik mit Hilfe von semi- formalen Spezifikation, Endlichen Zustandsautomaten (EA), Markov-Modellen, ereignisorientierter Simulation und Petri-Netzen, genutzt, um Workflows festzulegen, zu visualisieren und zu analysieren.

Die Open-Source-Lösung Cadence wurde von Uber auf der Grundlage von AWS SWF (Simple Workflow Service) entwickelt. Sie nutzt Apache Cassandra (und andere Datenbanken) für Persistenz und ist auf eine hohe Fehlertoleranz und Skalierbarkeit ausgelegt. Das vielleicht überraschendste Merkmal von Cadence im Vergleich zu anderen Workflow-Engine-Ansätzen, die ich bereits kennengelernt habe, ist der Fokus auf die Unterstützung der Entwickler, Workflows hauptsächlich in Java oder Go zu schreiben (weitere Sprachen sind verfügbar). Eine spezielle Visualisierungsnotation oder ein weiteres Tool zum Festlegen des Workflow wird daher nicht benötigt – die Semantik besteht einfach nur aus Code. Mit dem Cadence-Web-Client können Workflows jedoch bei der Ausführung visualisiert werden.

(Quelle: Shutterstock)

Zum Fahrradfahren braucht man eine Menge Ausrüstung: beispielsweise ein Fahrrad (logisch…), einen Helm, Schuhe, Wasserflasche, Brille, Handschuhe, Lampe, Pumpe, Shirt und Hose aus Lycra (optional) usw. Für den erfolgreichen Einsatz von Cadence werden ebenfalls viele Elemente benötigt, unter anderem Workflows, Activities, Domains, Workflow-Clients und Worker. Sehen wir uns diese Elemente nacheinander mal genauer an.

2. Cadence – Workflows

Bei Workflows in Fabrikanlagen muss die Reihenfolge der Aufgaben eindeutig festgelegt werden. (Quelle: Shutterstock)

Beginnen wir mit dem Grundkonzept von Cadence: Workflows. Ich werde den Cadence-Java-Client benutzen, und würde Ihnen empfehlen diesen herunterzuladen und entsprechend zu konfigurieren, damit Sie ihn in der von Ihnen gewählten IDE kompilieren können. (Ich nutze Eclipse und Maven. Zur Vereinfachung habe ich die Importe weggelassen). Es gibt einige Beispiele für Java-Clients, die mich inspiriert haben. Zunächst benötigen wir eine Workflow-Interface und -Implementierung.

Workflow-Interface:

static final String activityName = "ExampleActivity";

    public interface ExampleWorkflow {

        @WorkflowMethod(executionStartToCloseTimeoutSeconds = 120, taskList = activityName)

        void startWorkflow(String name);

    }

Workflow-Implementierung:

public static class ExampleWorkflowImpl implements ExampleWorkflow {

   

     private ExampleActivities activities = null;

    

     public ExampleWorkflowImpl() {

            this.activities = Workflow.newActivityStub(ExampleActivities.class);

        }

    

        @Override

        public void startWorkflow(String name) {

         System.out.println("Started workflow! ID=" + Workflow.getWorkflowInfo().getWorkflowId());

        

         String r1 = activities.task1(name);

           String r2 = activities.task2(r1);

            

         System.out.println("Workflow result = " + r2 + " ID=" + Workflow.getWorkflowInfo().getWorkflowId());

        }

       

    }

In Cadence gibt es immer genau eine Methode mit der @WorkflowMethod-Annotation, die als Startereignis für den Workflow fungiert und den Sequenzfluss und Activities enthält. Ihr Aufruf startet eine stateful Workflow-Instanz, die schließlich endet.

In der Implementierung ruft die Methode startWorkflow() zwei weitere Methoden nacheinander auf (task1, task2), wodurch wir eine Workflow-Logik (einfach sequenziell) und 2 Activities erhalten.

Jede ausgeführte Workflow-Instanz verfügt über eine eindeutige ID, die wir im Beispiel oben verwendet haben. Sie brauchen eine Workflow-ID, da Workflows über mehrere Instanzen verfügen können.

Hier ist es aber noch komplexer. Tatsächlich funktioniert Cadence nicht nur nach dem POJI/POJO-Modell. Es ist eine Plattform, mit der sich stateful Workflows skalierbar und zuverlässig ausführen lassen.

Sie bemerken sicher die Elemente timeout, activities und taskList im vorstehenden Beispiel. Timeouts sind notwendig, weil jeder Workflow, ganz wie ein Musikstück oder eine Fahrradtour, irgendwann enden muss.

3. Cadence – Activity

Einige Leute veranstalten die sonderbarsten Sachen mit ihrem Fahrrad. Das hier wird wahrscheinlich schiefgehen. (Quelle: Shutterstock)

Activities sind ein zentrales Element von Cadence. Workflows sind stateful und fehlertolerant. In einer verteilten Microservices-Architektur möchten sie aber schließlich Remote-APIs aufrufen, um Aufgaben auszuführen. Remote-API-Aufrufe können jedoch fehlerhaft sein und nicht fehlertolerant gestaltet werden, da sie außerhalb des Cadence-Workflows liegen. Cadence nutzt sie, damit jeder Code, einschließlich Remote-Aufrufen, in eine Cadence-Activity eingebunden werden kann, unter dem Vorbehalt, dass Cadence bei Fehlern den Zustand der Activity nicht wiederherstellt und Activities garantiert höchstens einmal ausgeführt werden. (Idempotente Activities werden bei Fehlern automatisch erneut ausgeführt, nicht-idempotente Activities müssen von einer unternehmensspezifischen Logik behandelt werden, einschließlich Kompensations-Activities. Weitere Informationen zu RetryOptions finden Sie hier.)

Activities sind ebenfalls nur ein POJO/POJI-Paar und enthalten die auszuführenden Aufgaben/Methoden. Jede Methode verfügt über eine @ActivityMethod-Annotation mit Optionen.

public interface ExampleActivities

    {

     @ActivityMethod(scheduleToCloseTimeoutSeconds = 60)

     String task1(String name);

     @ActivityMethod(scheduleToCloseTimeoutSeconds = 60)

         String task2(String name);

    }




public static class ExampleActivitiesImpl implements ExampleActivities

    {

         public String task1(String arg) {

         System.out.println("task1 " + arg + " in " + Activity.getWorkflowExecution().getWorkflowId());

         return arg+"task1";

         }




    public String task2(String arg) {

         System.out.println("task2 " + arg + " in " + Activity.getWorkflowExecution().getWorkflowId());

         return arg+"task2";

         }    

    }

Wir haben die Activities bereits im Workflow-Konstrukteur oben registriert. Beachten Sie, dass man etwas anders vorgehen muss, um die Workflow-ID mit einer Activity zu verknüpfen. Brauchen wir also nur einen Workflow und Activities um loszulegen?

4. Cadence – Domains

Königreich: Staatsgebiet, dessen Beherrschung in die Domäne eines Monarchen fällt (falls Sie mal einen Tipp für das Kreuzworträtsel brauchen) (Quelle: Shutterstock)

Cadence nutzt das Konzept der Domains, die im Grunde nur ein Namensraum für die darin enthaltenen Workflows sind. Sie müssen eine Domain erstellen oder eine bestehende Domain wiederverwenden, bevor Sie einen Workflow oder Worker starten können. Hier sehen Sie ein Beispiel für die Registrierung einer neuen Domain (oder für deren Wiederverwendung, falls sie bereits besteht):

public static void registerDomain(String host, String domain)

    {  

     String nameDescription = "a new domain";

    

      IWorkflowService cadenceService = new WorkflowServiceTChannel(ClientOptions.newBuilder().setHost(host).setPort(7933).build());

        RegisterDomainRequest request = new RegisterDomainRequest();

        request.setDescription(nameDescription);

        request.setEmitMetric(false);

        request.setName(domain);

        int retentionPeriodInDays = 1;

        request.setWorkflowExecutionRetentionPeriodInDays(retentionPeriodInDays);

        try {

          cadenceService.RegisterDomain(request);

          System.out.println(

              "Successfully registered domain "

                  + domain

                  + " with retentionDays="

                  + retentionPeriodInDays);

        } catch (DomainAlreadyExistsError e) {

          System.out.println("Domain " + domain + " is already registered")

    }

Und nur für den Fall, dass Sie sich fragen, was IWorkflowService und WorkflowServiceTChannel sind (Ich habe mich das auch schon gefragt, und wir werden sie später auch nochmal benutzen)! Diese Typen sind im ServiceClient-Paket enthalten, das nicht gut dokumentiert zu sein scheint, aber es ist anscheinend die Hauptmethode, um eine Verbindung zum Cadence-Server herzustellen.

„Der ServiceClient ist ein RPC-Service-Client, der eine Verbindung zum Cadence-Service herstellt. Er dient auch als Baustein für die anderen Clients.“

Im Java-Client-Code habe ich folgende Client-Typen gefunden (im Client-Paket und im ServiceClient-Paket): client/ActivityCompletionClient, client/WorkflowClient, serviceclient/IWorkflowService. (Vielleicht gibt es noch mehr.)

5. Cadence – Workflow-Client

Wie kommunizieren Sie mit dem Cadence-Server? (Quelle: Shutterstock)

Nun sind wir so weit, dass wir den Beispielcode ausprobieren können. Hier ist etwas Beispielcode, um eine Domain und eine neue WorkflowClient-Instanz zu erstellen:

String host  = "Cadence Server IP";

    

String domainName = "example-domain";

    

registerDomain(domainName);

    

WorkflowClient workflowClient =

                WorkflowClient.newInstance(

                        new WorkflowServiceTChannel(ClientOptions.newBuilder().setHost(host).setPort(7933).build()),

                        WorkflowClientOptions.newBuilder().setDomain(domainName).build());

6. Cadence – Worker

Ich hätte nie gedacht, dass es „zu viele Fahrräder“ geben kann, bis ich in die Niederlande reiste und ganze Horden von Fahrradfahrern erlebte. Das wichtigste Zubehör ist hier nicht der Helm, sondern die Klingel! (Quelle: Adobe Stock)

Wenn Sie den Beispielcode oben versuchsweise ausführen, werden Sie feststellen, dass keine der Aufgaben ausgeführt wird und für den Workflow schließlich ein Timeout eintritt. Was ist schiefgegangen? Activities werden durch Worker ausgeführt. Bevor Arbeiten ausgeführt werden können, müssen für die Activities Worker erstellt werden. Ähnlich ist es bei den Musikern eines Orchesters. Vielleicht sind Dirigent und Partitur schon da. Aber die Partitur wird erst gespielt, wenn die Musiker mit ihren Instrumenten auf die Bühne gekommen und bereit für die Vorführung sind. Dann erst kann der Dirigent das Konzert beginnen.

Wir haben also jetzt einen WorkflowClient und können einen Worker erstellen. Sie müssen den String activityName festlegen und den Workflow und die ActivitiesImplementations im Workflow registrieren, bevor Sie diesen starten. Wenn Sie es versäumen, die Activities zu registrieren, wird kein Fehler gemeldet. Es geschieht aber auch nichts, weil Cadence darauf wartet, dass ein Worker-Prozess startet, zumindest bis zum Workflow-Timeout:

        WorkerFactory factory = WorkerFactory.newInstance(workflowClient);

        Worker worker = factory.newWorker(activityName);

        worker.registerWorkflowImplementationTypes(ExampleWorkflowImpl.class);




        worker.registerActivitiesImplementations(new ExampleActivitiesImpl());

        

        factory.start();

Beachten Sie, dass die Worker in der Regel in einem vom Workflow getrennten Prozess und aus Gründen der Skalierbarkeit auf (mehreren) Servern mit ausreichenden Ressourcen ausgeführt werden, da hier die eigentliche Workflow-Aufgabe „work“, also die Arbeit, erledigt wird.

Aber wie viele Worker haben wir tatsächlich? Es bestehen standardmäßige Beschränkungen für Gleichzeitigkeit und Rate. Die standardmäßigen Einstellungen von WorkerOptions sind

  • Maximale Anzahl gleichzeitiger Activities = 100
  • Maximale Anzahl gleichzeitiger Workflows = 50
  • Maximale Anzahl gleichzeitiger lokaler Activities = 100
  • Maximale Anzahl von pro Sekunde gestarteten Activities = 0 (unbegrenzt)

Wenn Sie clever sind und Workflows und Activities (jeweils Anzahl und Zeiten) ausreichend überwachen, könnten Sie vermutlich Little’s Law verwenden, das Gleichzeitigkeit, Durchsatz und Zeit verknüpft, um die magischen Zahlen genauer zu schätzen. Hier sehen Sie ein Beispiel für das Ändern der WorkerOptions-Standardeinstellungen, inklusive einer Erklärung, wie sinnvoll vorgegangen werden kann.

7. Cadence – Workflow-Ausführung: synchron oder asynchron

(Quelle: Shutterstock)

Jetzt ist es an der Zeit, wie folgt die Workflow-Instanz auzuführen und zu starten:

ExampleWorkflow exampleWorkflow = workflowClient.newWorkflowStub(ExampleWorkflow.class);

exampleWorkflow.startWorkflow("workflow 1");

Workflows können synchron oder asynchron gestartet werden. Zwei Instanzen starten synchron:

ExampleWorkflow exampleWorkflow = workflowClient.newWorkflowStub(ExampleWorkflow.class);

exampleWorkflow.startWorkflow("workflow 1 sync");




ExampleWorkflow exampleWorkflow2 = workflowClient.newWorkflowStub(ExampleWorkflow.class);

exampleWorkflow2.startWorkflow("workflow 2 sync");

Asynchroner Start:

ExampleWorkflow exampleWorkflow3 = workflowClient.newWorkflowStub(ExampleWorkflow.class);




CompletableFuture r3 = WorkflowClient.execute(exampleWorkflow3::startWorkflow, "workflow async");

CompletableFuture r4 = WorkflowClient.execute(exampleWorkflow3::startWorkflow, "workflow 2 async");




          try {

r3.get();

r4.get();

} catch (InterruptedException e1) {

e1.printStackTrace();

} catch (ExecutionException e1) {

e1.printStackTrace();

}

Denken Sie daran, den Abschluss der asynchronen Workflows abzuwarten (wie in diesem Beispiel mit der Verwendung von get()). Andernfalls wird nicht viel geschehen, da die Worker in diesem Beispiel in demselben Thread sind.

8. Cadence – asynchrone Activities und Blockieren

(Quelle: Shutterstock)

Nicht nur Workflows lassen sich asynchron aufrufen, auch Activities können gleichzeitig ausgeführt werden. Dies ist hilfreich, wenn Sie eine große Anzahl an gleichzeitigen Aufgaben ausführen und dann abwarten möchten, bis alle abgeschlossen sind. Lassen Sie uns beispielsweise die ursprüngliche Methode startWorkflow(String name) mit der neuen Methode startConcurrentWorkflow(int max) ersetzen. Zudem verwenden wir die ursprüngliche Activity task1() erneut, führen sie jedoch gleichzeitig aus:

        public void startConcurentWorkflow(int concurrency) {

            List<Promise> promises = new ArrayList<>();

            List processed = null;

            try {

                       for (int i=0; i<concurrency; i++)

             {

                        Promise x = Async.function(activities::task1, “subpart “ + i);

             promises.add(x);

             }

                

                // allOf converts a list of promises to a single promise that contains a list

                // of each promise value.

                Promise<List> promiseList = Promise.allOf(promises);




                // get() blocks on all tasks completing

                List results = promiseList.get();

                

                int count = 0;

                for (String s: results)

                {

                 System.out.println("Processed " + s);

                 count++;

                }

                System.out.println("Processed “ + count + " concurrent tasks");

            } finally {

                }

            }

        }

Beachten Sie, dass wir die Methode Async.function(activities::activityName, arg) und die Promise-Klasse mit den Methoden add(), allOf() und get() verwenden müssen, um korrekt zu blockieren, bis alle Activities abgeschlossen sind.

9. Cadence – Einschränkungen

Dieses Schild weist vermutlich darauf hin, dass hier keine Traktoren, Pferdekutschen und Fahrräder fahren dürfen. (Quelle: Shutterstock)

Wirklich clever, wie Cadence den Status von Workflows verwaltet! Beim üblichen Ansatz für Persistenz und zuverlässige Workflow-Engines wird der Status jeder stateful Workflow-Instanz in einer Datenbank beibehalten. Cadence gibt jedoch Statusänderungen als Historienereignisse an die Datenbank aus. Bei Fehlern (selbst, wenn der Workflow im Ruhezustand war) startet Cadence den Workflow einfach erneut und wiederholt die Historie, um zu bestimmen, welche Aufgabe als nächste ausgeführt werden muss. Hierbei kommen sog. event sourcing pattern zum Einsatz, die, ähnlich wie Kafka Streams, den Zustand aus den Ereignisströmen oder Ereignisströme aus dem Zustand berechnen können.

Bei dieser Vorgehensweise wird eine große Anzahl an gleichzeitig ausgeführten Workflow-Instanzen unterstützt, wie sie bei Workflows mit langer Laufzeit und hohem Durchsatz wahrscheinlich vorkommen werden. Dies hat allerdings negative Folgen für lange Workflows: Es sind immer größere Historien erforderlich, und viele Ereignisse müssen wiederholt werden, um den korrekten Zustand zu erhalten.

Dieser Ansatz zieht jedoch einige wichtige Beschränkungen für die Workflow-Implementierung nach sich, denen Sie sich bewusst sein müssen – sie müssen im Prinzip deterministisch sein. Die Cadence-Workflow-Klasse verfügt über viele hilfreiche Methoden, die anstelle von unsicheren / nicht-deterministischen / von Nebeneffekten betroffenen standardmäßigen Java-Methoden verwendet werden können, insbesondere für Zeit, Ruhezustand und Zufallszahlen.

Das ist so ziemlich alles, was ich bis jetzt über Cadence weiß. Es gibt jedoch noch viel mehr zu entdecken, u. a. über die Behandlung von Ausnahmen, Neuversuche, lang laufende Transaktionen, verschiedene Arten von Timeouts, Kompensations-Activities, Signale, Abfragen, abgeleitete Workflows, den Aufruf von Remote-APIs und die Integration mit Kafka (z. B. das Senden und Blockieren/Empfangen einer Nachricht als Activity).

Wir unterstützen Sie gerne

Ob Cadence, Debian oder PostgreSQL: mit über 22+ Jahren an Entwicklungs- und Dienstleistungserfahrung im Open Source Bereich, können credativ und Instaclustr Sie mit einem beispiellosen und individuell konfigurierbaren Support professionell begleiten und Sie in allen Fragen bei Ihrer Open Source Infrastruktur voll und ganz unterstützen.

Sie möchten mehr über Cadence lernen und über die Vorteile die es Ihrer Organisation bietet. Dann laden Sie sich unser englischsprachiges Whitepaper runter.

Sollten Sie Fragen zu unserem Artikel haben oder würden sich wünschen, dass unsere Spezialisten sich Ihr System angucken und Ihre Infrastruktur optimieren, dann schauen Sie doch vorbei und melden sich über unser Kontaktformular oder schreiben uns eine E-mail an info@credativ.de.

Über unsere Mutterfirma Instaclustr bieten wir auch eine komplett verwaltete Plattform für Cadence an.

Original englischsprachige Artikel auf Instaclustr.com

Folgen Sie der Reihe auf credativ.de: Drohnen fliegen lassen mit Cadence

Kategorien: HowTos
Tags: apachekafka® cadence

über den Autor

Paul Brebner

zur Person

Paul is the Technology Evangelist at Instaclustr/Spot by NetApp. For the past five years, he has been learning new scalable Big Data technologies, solving realistic problems, building applications, and blogging and talking about Apache Cassandra, Apache Spark, Apache Kafka, Redis, Elasticsearch, PostgreSQL, Cadence, and many more open source technologies. Since learning to program on a VAX 11/780, Paul has extensive R&amp;D, teaching, and consulting experience in distributed systems, technology innovation, software architecture and engineering, software performance and scalability, grid and cloud computing, and data analytics and machine learning. Paul has also worked at Waikato University (NZ), UNSW, CSIRO, UCL (UK), NICTA/ANU, and several tech start-ups. Paul has an MSc in Machine Learning and a BSc (Computer Science and Philosophy).

Beiträge ansehen


Beitrag teilen: