Start / Seminare / Jakarta EE modernisieren
Modul
Messaging und asynchrone Integration
4 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
Messaging und asynchrone Integration
0:00 Nachrichten sind das Rückgrat vieler Enterprise-Anwendungen — und der Bereich, in dem eine Migration am unangenehmsten scheitern kann. Der Grund: Eine Nachricht kommt irgendwann an. Manchmal zweimal. Wer das nicht einplant, plant nur den guten Tag. In diesem Modul analysieren wir zuerst, welche Zusagen das bestehende JMS-System überhaupt gibt, sehen uns dann an, wie Quarkus Messaging denselben Fluss beschreibt, und kümmern uns schließlich um das, was in Bestandsanwendungen fast nie sauber geregelt ist: Fehlerfälle, doppelte Nachrichten und die Koexistenz von altem und neuem Verbraucher.
Messaging und asynchrone Integration
0:34 Wir sind beim vorletzten Baustein des Piloten. Nach Schnittstelle, Beans und Daten fehlt noch die asynchrone Seite — bei Zählwerk der Eingang der Zählerstände aus den Ablesegeräten. Und hier zeigt sich besonders deutlich, was wir in Modul neun gelernt haben: Die Klammer, die der Application Server über Datenbank und Warteschlange gelegt hat, gibt es so nicht mehr.
Bestehendes Messaging analysieren
0:55 Beginnen wir mit der Analyse. Und die erste Frage ist nicht, welcher Broker eingesetzt wird, sondern welche Zusagen heute gelten. Denn der Verbrauchercode ist schnell übertragen. Die Frage ist, ob die Zusagen dahinter erhalten bleiben. Eine JMS-Anwendung besteht aus Zielen, Verbrauchern und Zusagen. Die Zusagen sind der eigentliche Gegenstand: Wird jede Nachricht genau einmal verarbeitet, mindestens einmal oder höchstens einmal?
1:21 Und wird sie in derselben Transaktion bestätigt wie die Datenbankänderung? Denken Sie an ein Einschreiben gegenüber einem normalen Brief — beide kommen an, aber nur bei einem wissen Sie es sicher, und nur bei einem gibt es eine Wiederholung, wenn niemand da war. Genau diese Unterschiede müssen Sie kennen, bevor Sie über Technik reden. Der Verbrauchercode ist der leichtere Teil.
1:44 Die ersten drei Zeilen sind fast mechanisch übersetzbar: Queue wird Kanal, Topic wird Kanal mit Broadcast, Message-Driven Bean wird eine Methode mit Incoming. Die vierte Zeile ist die schwierige — und wie die Fußzeile sagt, fällt dort die selbstverständliche Klammer des Servers weg. Bisher galt: Wenn die Datenbankänderung zurückgerollt wird, gilt die Nachricht als nicht verarbeitet und kommt wieder.
2:06 Diese Selbstverständlichkeit müssen Sie jetzt selbst herstellen — nicht durch eine Transaktion über beide Systeme, sondern durch einen Entwurf, der Wiederholungen verträgt. Vier Gründe, und alle laufen auf dasselbe hinaus: Die Zustellgarantie ist die Anforderung, aus der sich alles andere ergibt. Sie entscheidet, ob doppelte Verarbeitung überhaupt vorkommen darf. Sie bestimmt, ob Idempotenz nötig ist oder ein Luxus wäre.
2:31 Sie legt fest, wann eine Nachricht als verarbeitet gilt — vor oder nach dem Datenbankschreiben. Und ohne sie lässt sich kein Fehlerverhalten sinnvoll entwerfen, weil Sie nicht wissen, ob eine wiederholte Nachricht ein Problem oder die Lösung ist. Klären Sie das mit dem Fachbereich, nicht nur mit dem Betrieb. Punkt eins ist der methodische Fehler: Die bisherige Garantie wird angenommen statt in der Konfiguration nachgesehen.
2:56 Sehen Sie nach — Bestätigungsmodus, Transaktionsverhalten, Wiederholungsverhalten stehen dort. Punkt drei ist eine besonders zähe Annahme: Die Reihenfolge der Nachrichten galt als sicher und war es nie, jedenfalls nicht bei mehreren Verbrauchern. Und Punkt vier ist ein technischer Altlastenklassiker: Nachrichtenformate tragen serialisierte Java-Objekte.
3:16 Damit sind Sender und Empfänger an dieselbe Klassenversion gebunden — und eine schrittweise Migration wird deutlich schwieriger, als sie sein müsste.
Quarkus Messaging
3:26 Sehen wir uns an, wie derselbe Nachrichtenfluss in der neuen Laufzeit beschrieben wird. Der Gedanke dahinter ist erfreulich einfach. Der Gedanke dahinter ist erfreulich einfach: Im Code steht nur ein Kanalname, die Verbindung zum Broker steht in der Konfiguration. Quarkus Messaging beschreibt den Nachrichtenfluss über benannte Kanäle.
3:45 Eine Methode mit Incoming verbraucht, eine mit Outgoing erzeugt, und über Channel lässt sich ein Emitter injizieren, um aus normalem, imperativem Code heraus zu senden. Der Clou ist die Trennung: Im Code steht nur der Kanalname, die Zuordnung zu einem konkreten Broker-Ziel steht in der Konfiguration. Das ist für Migrationen ausgesprochen praktisch, denn Sie können denselben Code gegen einen Testbroker, gegen den alten Broker und später gegen einen anderen betreiben — ohne den Code anzufassen.
4:13 Worauf es hier ankommt, sind die drei Annotationen über der Methode. Incoming benennt den Kanal. Blocking sagt ausdrücklich, dass diese Verarbeitung blockiert und deshalb nicht auf dem Event Loop laufen darf. Und Transactional zieht die Datenbankgrenze. Diese drei Zeilen ersetzen das, was früher die Message-Driven Bean als Bündel mitbrachte — dieselbe Zerlegung wie bei den Session Beans in Modul neun.
4:37 Und die Fußzeile enthält den zweiten Teil: Der Kanal wird über eine Konfigurationszeile mit Connector und Ziel verbunden. Achten Sie darauf, dass der Name in beiden Welten exakt übereinstimmt. Hier steckt ein Unterschied, den Bestandsentwickler oft unterschätzen. Synchroner Code läuft auf Worker-Threads, asynchroner auf dem Event Loop.
4:57 Und blockierende Arbeit auf dem Event Loop bremst nicht nur den eigenen Kanal, sondern alle anderen mit — das ist die klassische Falle beim Umzug von blockierendem Bestandscode. Punkt drei ist die erfreuliche Nachricht: Mit Java 21 lassen sich Virtual Threads als Ausführungsmodell wählen, was blockierenden Code entlastet, ohne ihn umzuschreiben.
5:16 Und Punkt vier ist der Rat für die Praxis: Die Annotation Blocking macht die Entscheidung sichtbar, statt sie dem Zufall zu überlassen. Die Logik dieser Tabelle ist eine Zuordnung von Broker zu Einsatzzweck. Für eine Ablösung von JMS ist meist AMQP der naheliegende Weg, weil die Denkweise mit Queues und Topics erhalten bleibt.
5:36 Kafka ist attraktiv, wenn Sie Ereignisströme und Wiederholbarkeit brauchen — aber es ist auch ein Modellwechsel, kein Protokollwechsel. Und beachten Sie die Fußzeile: JMS selbst bleibt als eigene Erweiterung möglich, ist aber weniger tief eingebunden. Für eine Übergangszeit kann das genau richtig sein; als Zielbild sollte man es bewusst wählen, nicht aus Trägheit.
5:58 Punkt drei ist der gefährlichste auf dieser Liste, und er ist leicht zu übersehen: Die Bestätigung erfolgt vor der Datenbankänderung. Wenn dann das Schreiben scheitert, ist die Nachricht bestätigt und weg — der Zählerstand ist verloren, und niemand bemerkt es, weil kein Fehler übrig bleibt. Klären Sie deshalb genau, wann Ihr Kanal bestätigt.
6:17 Punkt zwei ist der ärgerliche Anfängerfehler: Der Kanalname im Code weicht vom Namen in der Konfiguration ab — dann startet die Anwendung, und es passiert einfach nichts. Und Punkt vier ist die Reihenfolge: erst die Zustellgarantie klären, dann die Erweiterung wählen.
Fehlerfälle und Koexistenz
6:33 Und jetzt der Teil, der in Bestandsanwendungen fast nie ausformuliert ist: Was passiert eigentlich, wenn eine Nachricht nicht verarbeitet werden kann? Und dazu gehört die Frage, was passiert, wenn eine Nachricht nicht verarbeitet werden kann — in Bestandsanwendungen fast nie sauber beantwortet. Für jeden Kanal wird festgelegt, was bei einem Fehler geschieht: den Verbraucher stoppen, die Nachricht überspringen oder sie in eine Fehlerwarteschlange schreiben.
6:59 Beim Kafka-Connector geschieht das über die Fehlerstrategie des Kanals, unter anderem mit dem Wert dead-letter-queue. Das ist eine echte Verbesserung gegenüber vielen Bestandssystemen, in denen die Antwort auf diese Frage lautet: Der Verbraucher versucht es unendlich oft weiter. Denn das bedeutet, dass eine einzige fehlerhafte Nachricht den gesamten Kanal blockiert — und der Ableselauf steht.
7:21 Dieser Ablauf ist bewusst einfach gehalten: Empfang, Fehler, Wiederholung, Fehlerwarteschlange. Entscheidend ist, dass es ein Ende gibt. Wiederholung ist sinnvoll, wenn der Fehler vorübergehend ist — die Datenbank war kurz nicht erreichbar. Wiederholung ist sinnlos, wenn die Nachricht selbst fehlerhaft ist; dann wiederholen Sie den Fehler beliebig oft.
7:42 Deshalb der letzte Kasten: Nach einer begrenzten Zahl von Versuchen wandert die Nachricht aus dem Weg, und der Kanal läuft weiter. Was danach mit ihr geschieht, ist eine organisatorische Frage — und die gehört beantwortet. Hier ist der Kern des Moduls. Bei Zustellung mindestens einmal ist die doppelte Nachricht kein Ausnahmefall, sondern der Normalfall — sie tritt auf, wann immer eine Bestätigung verloren geht.
8:06 Die Lösung ist nicht, das zu verhindern, sondern es folgenlos zu machen: Ein fachlicher Schlüssel je Nachricht, und die Verarbeitung prüft, ob dieser Schlüssel schon verarbeitet wurde. Bei Zählwerk wäre das etwa Zählernummer plus Ablesezeitpunkt. Und Punkt vier ist die technische Bedingung: Diese Prüfung muss in derselben Transaktion laufen wie die Verarbeitung, sonst hilft sie bei gleichzeitiger Zustellung nicht.
8:30 Der rote Faden: entscheiden, absichern, einrichten — und beweisen. Schritt eins verlangt eine ausdrückliche Festlegung der Fehlerstrategie; ein Standardwert ist keine Entscheidung. Schritt zwei und drei bauen die Idempotenz. Schritt vier richtet die Fehlerwarteschlange ein und klärt, wer hineinsieht — das ist der organisatorische Teil, ohne den die Warteschlange nur ein Datengrab ist.
8:53 Und Schritt fünf ist, wie die Fußzeile sagt, der einzige Beleg: eine doppelte und eine fehlerhafte Nachricht einspielen. Alles davor ist Entwurf; erst dieser Schritt macht daraus eine Aussage. Punkt eins ist der organisatorische Klassiker: Die Fehlerwarteschlange existiert, aber niemand sieht hinein. Nach einem halben Jahr liegen dort dreitausend Nachrichten, und keiner weiß, welche davon noch relevant sind.
9:17 Punkt drei ist der, der in Koexistenzphasen konkret passiert: Alte und neue Verarbeitung hören auf dasselbe Ziel und teilen sich die Nachrichten — mal verarbeitet der Bestand, mal der neue Dienst, und niemand kann vorhersagen, welcher. Und Punkt vier ist die Wiederholung aus Modul neun: Eine Transaktion über Datenbank und Broker ist nicht zugesichert. Bauen Sie nicht darauf.
Übung
9:39 In der Übung lösen Sie die Message-Driven Bean von Zählwerk ab — und Sie spielen bewusst kaputte Nachrichten ein. Der Beweis sind zwei bewusst eingespielte Nachrichten: eine doppelte und eine fehlerhafte. Alles andere ist Entwurf. Sie ersetzen den Verbraucher, der Zählerstände aus den Ablesegeräten entgegennimmt. Das Lernziel ist, eine Nachrichtenverarbeitung samt ihrer Zustellgarantien zu übertragen und gegen Wiederholungen abzusichern — nicht nur, dass Nachrichten ankommen, sondern dass sie genau einmal wirken.
10:09 Erfolgreich sind Sie, wenn eine doppelt eingespielte Meldung genau eine Ablesung erzeugt und eine fehlerhafte Meldung nachweislich in der Fehlerwarteschlange landet. Der Kanal heißt ablesung-eingang, die Meldungen tragen Zählernummer, Zeitpunkt und Zählerstand — daraus ergibt sich Ihr fachlicher Schlüssel fast von selbst.
10:27 Fünf Schritte in der Reihenfolge des Moduls: erst die Zusagen des Bestands feststellen, dann übertragen, dann absichern, dann Fehlerverhalten, dann beweisen. Schritt eins ist der, den man überspringen möchte, weil er nach Archäologie aussieht — er entscheidet aber über alles Weitere. Und der Hinweis in der Fußzeile nimmt Ihnen Arbeit ab: Für den Broker im Test genügt die automatisch bereitgestellte Entwicklungs-Infrastruktur; Sie müssen also keinen Broker installieren und keine Zugangsdaten verwalten.
10:55 Das ist einer der Punkte, an denen die kurze Entwicklungsschleife aus Modul sechs im Migrationsalltag wirklich zahlt. Punkt eins ist der feine technische Fehler, der die ganze Absicherung wertlos macht: Die Idempotenzprüfung liegt außerhalb der Transaktion. Dann können zwei gleichzeitig eintreffende Kopien beide feststellen, dass noch nichts verarbeitet wurde — und beide schreiben.
11:17 Punkt zwei ist die bequeme Testlücke: Es werden nur gültige Meldungen eingespielt. Punkt drei ist der Koexistenzfehler, über den wir gesprochen haben. Und Punkt vier ist der, der nach dem Projekt zuschlägt: Die Fehlerwarteschlange bekommt keine Zuständigkeit im Betrieb — und damit wird aus einer Sicherheitsmaßnahme ein blinder Fleck.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →