02 August 2022

Drohnen fliegen lassen mit Cadence – Integrationsmuster und neue Cadence-Funktionen

In diesem Blog-Beitrag fahren wir mit unserer Anwendung der Cadence Drohnenlieferung fort, inklusive einer Zusammenfassung des vollständigen Workflows und der Cadence+Kafka Integrationsmuster. Außerdem werden wir uns einige neue Cadence-Funktionen ansehen, die wir verwendet haben (Retry, Continue, Abfragen, Nebeneffekte), und wir werden eine beispielhafte Verfolgung einer Drohnenlieferung enthüllen.

1. Bewegung und Lieferung

(Quelle: Shutterstock)

Die Details der Drohnenbewegung wurden im vorherigen Blog-Beitrag erläutert, doch zusammenfassend lässt sich Folgendes festhalten: Der Drohnen-Workflow ist für die „Bewegung“ vom Stützpunkt zum Bestellstandort, für die Abholung der Bestellung, für das Fliegen zum Lieferstandort, für das Ablegen der Bestellung und das Zurückkehren zum Stützpunkt verantwortlich. Dies geschieht mit der Activity nextLeg() und wenn die Bestellung abgeholt, aber noch nicht geliefert wurde, werden Signale der Standortstatusänderung an den Bestellungs-Workflow (6) gesendet. Eine interessante Erweiterung könnte die Aktualisierung einer ETA im Bestellungs-Workflow sein. Nachdem die Drohne zum Stützpunkt zurückgekehrt ist, markiert sie die Lieferung als „abgehakt“ und signalisiert dem Bestellungs-Workflow, dass er abgeschlossen ist. Das führt dazu, dass der Bestellungs-Workflow abgeschlossen wird (7) und der Drohnen-Workflow eine neue Instanz von sich selbst startet (8).

2. Zusammenfassung der Cadence+Kafka Integrationsmuster

Nun haben wir einige Beispiel für verschiedene Möglichkeiten gesehen, wie Cadence und Kafka integriert werden können, einige davon in unserem früheren Blog-Beitrag. Wir fassen sie hier für ein leichteres Verständnis in einer Tabelle zusammen.

Musternummer Mustername Richtung Zweck Merkmale Beispiele
1 Senden einer Nachricht an Kafka Cadence → Kafka Senden einer einzelnen Nachricht an Kafka-Thema, simple Notification (no response) Umschließt einen Kafka-Producer in einer Cadence-Activity Blog 2, Blog 4
2 Anfrage/    Antwort von Cadence an Kafka Cadence → Kafka → Cadence Wiederverwenden des Kafka-Microservice von Cadence, Anfragen und Antworten an Kafka Umschließt einen Kafka-Producer in einer Cadence-Activity, um eine Nachricht und Header-Metadaten als Antwort zu senden; Kafka-Consumer verwendet Metadaten, um dem Cadence-Workflow oder der Activity mit dem Ergebnis zu signalisieren, dass fortgefahren werden kann Blog 2
3 Starten eines neuen Cadence-Workflows von Kafka Kafka → Cadence Starten des Cadence-Workflows vom Kafka-Consumer als Reaktion auf das Empfangen eines neuen Datensatzes Kafka-Consumer startet das Ausführen der Cadence-Workflow-Instanz, eine Instanz pro empfangenem Datensatz Blog 4
4 Erhalten des nächsten Jobs aus einer Warteschlange Cadence → Kafka → Cadence Jede Workflow-Instanz erhält einen einzelnen Job von einem Kafka-Thema zum Verarbeiten Die Cadence-Activity umschließt einen Kafka-Consumer, der das Kafka-Thema kontinuierlich abruft, nur einen einzelnen Datensatz zurückgibt, transient für die Dauer der Activity Blog 4

3. Neue verwendete Cadence-Merkmale

Wir haben einige neue Funktionen in die Demo-Anwendung der Drohnenlieferung eingebaut, die ich in diesem Blog-Beitrag näher beleuchten möchte.

3.1 Cadence-Retry

(Quelle: Shutterstock)

Cadence-Activities und -Workflows können aus unterschiedlichen Gründen fehlschlagen, unter anderem wegen Zeitüberschreitungen. Retries können manuell verarbeitet werden, oder Sie lassen den Cadence-Server den Retry handhaben, was wesentlich einfacher ist. Sie finden die Cadence-Dokumentation zu Retries hier und hier. Für meine Drohnenlieferung-Anwendung habe ich festgelegt, dass Retries für meine Activities verwendet werden sollen. Der Grund dafür ist, dass sie das Potenzial haben, „lange“ zu laufen, und wenn sie fehlschlagen, können sie wieder aufgenommen und dort fortgesetzt werden, wo sie abgebrochen wurden. Die waitForOrder()-Activity ist eine blockierende Activity, die erst dann zurückkehrt, wenn sie einen Auftrag erhalten hat, der geliefert werden muss, sodass sie potenziell eine unbestimmte Zeit dauern kann. Außerdem ist sie unabhängig, was bedeutet, dass sie problemlos mehrmals aufgerufen werden kann (wie wir oben gesehen haben, handelt es sich tatsächlich um einen Kafka-Consumer, der ein Thema abruft). Die nextLeg()-Activity ist dafür verantwortlich, den Weg der Drohne von einem Ort zum anderen aufzuzeichnen, was viele Minuten in Anspruch nehmen kann. Wenn sie fehlschlägt, wollen wir, dass sie wiederaufgenommen wird, aber nicht vom aktuellen Standort (die neueste Version verwendet jetzt eine Abfragemethode, um den aktuellen Drohnenstandort zu Beginn der Activity abzurufen). Die einfachste Möglichkeit, die Optionen für einen Retry festzulegen, ist eine @MethodRetry-Annotation im Activity-Interface wie in diesem Beispiel. Die Einstellungen einschließlich Zeiten richtig abzurufen, kann jedoch eine Herausforderung sein, und ich habe diese nur „geraten“:

@MethodRetry(maximumAttempts = 3, initialIntervalSeconds = 1, expirationSeconds = 300, maximumIntervalSeconds = 30)
    	void nextLeg(LatLon start, LatLon end, boolean updateOrderLocation, String orderID);
  • maximumAttempts ist die maximal zulässige Anzahl an Retries, bevor die Activity fehlschlägt
  • initialIntervalSeconds ist die Verzögerung bis zum ersten Retry.
  • expirationSeconds ist die maximale Anzahl an Sekunden für alle Retries.
    • Retries stoppen, wenn entweder maximumAttempts oder expirationSeconds erreicht ist
  • maximumIntervalSeconds ist die Obergrenze für das Intervall zwischen den Wiederholungsversuchen. Es wird eine exponentielle Backoff-Strategie verwendet, die das Intervall begrenzt, anstatt es zu stark ansteigen zu lassen.

3.2 Cadence „Continue“

Sie könnten versucht sein, einen Cadence-Workflow ewig laufen zu lassen – schließlich sind sie für lang laufende Prozesse konzipiert. Dies wird jedoch im Allgemeinen als „schlechte Praxis“ angesehen, da die Größe des Workflow-Status immer weiter ansteigt, möglicherweise die maximale Statusgröße überschreitet und die Neuberechnung des aktuellen Status von Workflows aus dem Statusverlauf verlangsamt. Ich habe folgende einfache Lösung verwendet: Am Ende des vorhandenen Workflows wird mit Workflow.continueAsNew() ein neuer Drohnen-Workflow gestartet. Dadurch beginnt eine neue Workflow-Instanz mit derselben Workflow-ID, aber nicht mit demselben Status wie der ursprüngliche Workflow. Das bedeutet, dass Drohnen-Workflows am Stützpunkt beginnen, voll aufgeladen sind und noch keine Bestellung zum Abholen haben.

3.3 Cadence-Abfragen

Im 2. Cadence-Blog-Beitrag haben wir gezeigt, dass Workflows eine Schnittstellenklasse definieren müssen und dass die Schnittstellenmethoden Annotationen haben können, einschließlich @WorkflowMethod (der Einstiegspunkt in einen Workflow, exakt eine Methode muss diese Annotation haben) und @SignalMethod (eine Methode, die auf externe Signale reagiert, null oder mehr Methoden können diese Annotation haben).

Es gibt jedoch eine weitere Annotation, die wir nicht verwendet haben: @QueryMethod. Diese Annotation zeigt eine Methode an, die auf synchrone Abfrageanfragen reagiert, und von denen kann es viele geben. Sie dienen dazu, anderen Workflows usw. den Status anzuzeigen, sodass es sich im Grunde um eine „Getter“-Methode handelt.  Als Beispiel ist hier eine vollständige Liste von Methoden, einschließlich einiger Abfragemethoden für den OrderWorkflow:

public interface OrderWorkflow {
@WorkflowMethod(executionStartToCloseTimeoutSeconds = (int)maxFlightTime, taskList = orderActivityName)
String startWorkflow(String name);
@SignalMethod
void updateGPSLocation(LatLon l);
@SignalMethod
void signalOrder(String msg);
@SignalMethod
void updateLocation(String loc);
@QueryMethod
String getState();
@QueryMethod
LatLon getOrderLocation();
@QueryMethod
LatLon getDeliveryLocation();
    }

Abfragen müssen schreibgeschützt und nicht blockierend sein. Ein wesentlicher Unterschied zwischen Signalen und Abfragen, der mir aufgefallen ist, besteht darin, dass Signale nur bei nicht abgeschlossenen Workflows funktionieren, während Abfragen sowohl bei nicht abgeschlossenen als auch bei abgeschlossenen Workflows funktionieren. Bei abgeschlossenen Workflows werden die Workflows automatisch neu gestartet, sodass ihr Endzustand wiederhergestellt werden kann. Das ist clever!

3.4 Cadence-Nebeneffekte

Der Cadence-Workflow-Code muss im Allgemeinen deterministisch sein (Quelle: Shutterstock)

Ein letzter Trick, den ich bei dieser Anwendung genutzt habe, ist die Verwendung von Zufallszahlen zur Berechnung der Bestell- und Lieferorte in der Funktion newDestination(). Um diese im Order Activity-Workflow korrekt zu verwenden, musste ich Cadence mit der Methode Workflow.sideEffect()
sagen, dass er Nebeneffekte hat. So können Workflows die bereitgestellte Funktion einmal ausführen und das Ergebnis im Workflow-Verlauf speichern. Das Ergebnis des aufgezeichneten Verlaufs wird zurückgegeben, ohne dass die angegebene Funktion während der Wiedergabe ausgeführt wird. Dies garantiert die deterministische Anforderung für Workflows, da bei der Wiedergabe genau das gleiche Ergebnis zurückgegeben wird. Hier ist mein Beispiel aus der startWorkflow()-Methode des Bestellungs-Workflows (oben):

startLocation = Workflow.sideEffect(LatLon.class, () -> DroneMaths.newDestination(baseLocation, 0.1, maxLegDistance));

deliveryLocation = Workflow.sideEffect(LatLon.class, () -> DroneMaths.newDestination(startLocation, 0.1, maxLegDistance));

Der Code ist in unserem Github-Repository verfügbar. Im nächsten Blog-Beitrag in dieser Reihe werden wir die Skalierbarkeit von Cadence näher betrachten und herausfinden, wie viele Drohnen gleichzeitig fliegen können.

Anhang: Beispielhafte Verfolgung

Wenn alles nach Plan verläuft, sieht eine typische (gekürzte) Drohnen- und Bestellungs-Workflow-Verfolgung so aus:

Order WF readyForDelivery activity order_0 id 939328c5-eaad-4f52-9aaf-08100fde1e84
waitForOrder got an order! topic = orderjobs2, partition = 0, offset = 17, key = , value = 939328c5-eaad-4f52-9aaf-08100fde1e84
Drone Drone_0 got an order from Kafka + 939328c5-eaad-4f52-9aaf-08100fde1e84
Drone Drone_0 has got order 939328c5-eaad-4f52-9aaf-08100fde1e84
Drone Drone_0: new state = gotOrder, location = base
order order_0 got signal = droneHasOrder
Drone Drone_0 has generated a flight plan based on Order and Delivery locations
Start lat -35.20586, lon 149.09462
Order lat -35.189085007724415, lon 149.09190627324634
Delivery lat -35.1849320141879, lon 149.08653974053584
End lat -35.20586, lon 149.09462
Drone Drone_0 flight plan total distance (km) = 4.9930887486783835
Drone Drone_0 estimated total flight time (h) = 0.24965443743391919
Drone Drone_0 distance to order (km) = 1.881431511294941
Drone Drone_0 estimated time until order pickup (h) = 0.09407157556474705
Drone Drone_0 distance to delivery (km) = 2.5530367293280447
Drone Drone_0 estimated time until delivery (h) = 0.12765183646640224
Drone Drone_0: new state = flightPlanGenerated, location = base
Drone + Drone_0 flying to pickup Order
Drone Drone_0: new state = flyingToOrder, location = betweenBaseAndOrder
order order_0 got signal = droneOnWayForPickup
Drone WF gpsLocation = lat -35.20586, lon 149.09462
start loc = lat -35.20586, lon 149.09462
Drone flew to new location = lat -35.20536523834301, lon 149.09453994515312
Distance to destination = 1.825940473393331 km
Drone Drone_0 gps location update lat -35.20536523834301, lon 149.09453994515312
Drone Drone_0 charge now = 99.44444444444444%, last used = 0.5555555555555556
Drone WF gpsLocation = lat -35.20536523834301, lon 149.09453994515312
start loc = lat -35.20586, lon 149.09462
Drone flew to new location = lat -35.20487047663333, lon 149.09445989128173
Distance to destination = 1.770449431350198 km
Drone Drone_0 gps location update lat -35.20487047663333, lon 149.09445989128173
Drone flew to new location = lat -35.204375714870956, lon 149.0943798383858
Distance to destination = 1.714958384759105 km
Drone Drone_0 charge now = 98.88888888888889%, last used = 0.5555555555555556
...
Drone flew to new location = lat -35.189085007724415, lon 149.09190627324634
Distance to destination = 0.0 km
Drone Drone_0 gps location update lat -35.189085007724415, lon 149.09190627324634
Drone arrived at destination.
Drone Drone_0 charge now = 81.11111111111106%, last used = 0.5555555555555556
Drone Drone_0 picking up Order
Drone Drone_0: new state = pickingUpOrder, location = orderLocation
Drone Drone_0 picking up Order!
Drone Drone_0 picked up Order!
order order_0 got signal = pickedUpByDrone
Drone Drone_0 charge now = 77.77777777777773%, last used = 3.3333333333333335
Drone Drone_0 delivering Order...
Drone Drone_0: new state = startedDelivery, location = betweenOrderAndDelivery
Order new location = orderLocation
order order_0 got signal = outForDelivery
Order new location = onWay
Drone WF gpsLocation = lat -35.189085007724415, lon 149.09190627324634
start loc = lat -35.189085007724415, lon 149.09190627324634
Drone flew to new location = lat -35.188741877698526, lon 149.09146284539662
Distance to destination = 0.6161141689892213 km
Drone Drone_0 gps location update lat -35.188741877698526, lon 149.09146284539662
Drone Drone_0 charge now = 77.22222222222217%, last used = 0.5555555555555556
Drone flew to new location = lat -35.18839874605641, lon 149.09101942129195
Distance to destination = 0.5606231205354245 km
Order  GPS Location lat -35.188741877698526, lon 149.09146284539662
...
Drone flew to new location = lat -35.1849320141879, lon 149.08653974053584
Distance to destination = 0.0 km
Drone Drone_0 gps location update lat -35.1849320141879, lon 149.08653974053584
Drone Drone_0 charge now = 70.55555555555549%, last used = 0.5555555555555556
Drone arrived at destination.
Order GPS Location lat -35.1849320141879, lon 149.08653974053584
Drone + Drone_0 dropping Order!
Drone Drone_0: new state = droppingOrder, location = deliveryLocation
Drone + Drone_0 dropped Order!
Order  new location = deliveryLocation
Drone Drone_0 charge now = 67.22222222222216%, last used = 3.3333333333333335
Drone Drone_0 returning to Base
Drone Drone_0: new state = returningToBase, location = betweenDeliveryAndBase
order order_0 got signal = delivered
Drone WF gpsLocation = lat -35.1849320141879, lon 149.08653974053584
start loc = lat -35.1849320141879, lon 149.08653974053584
Drone flew to new location = lat -35.1854079590788, lon 149.08672345348626
Distance to destination = 2.384560977495208 km
Drone Drone_0 gps location update lat -35.1854079590788, lon 149.08672345348626
Drone flew to new location = lat -35.18588390369231, lon 149.08690716858857
Distance to destination = 2.3290699362110967 km
Drone Drone_0 charge now = 66.6666666666666%, last used = 0.5555555555555556
...
Drone flew to new location = lat -35.20586, lon 149.09462
Distance to destination = 0.0 km
Drone Drone_0 charge now = 43.3333333333332%, last used = 0.5555555555555556
Drone Drone_0 gps location update lat -35.20586, lon 149.09462
Drone arrived at destination.
Drone Drone_0 charge now = 42.777777777777644%, last used = 0.5555555555555556
Drone + Drone_0 returned to Base!
Drone Drone_0: new state = backAtBase, location = base
Drone Drone_0: new state = checkOrder, location = base
Drone Drone_0: new state = droneDeliveryCompleted, location = base
Drone Drone_0: new state = charging, location = base
Drone Drone_0 charging! charging time = 515s
Drone Drone_0: new state = charged, location = base
order order_0 got signal = orderComplete
Order WF exiting!

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&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: