Transaktion und erneute Lieferung in JMS

Das Architekturen und Entwerfen von Anwendungen mit dem Java Message Service (JMS) erfordert nicht nur Kenntnisse über die Verwendung der JMS-API, sondern auch eine solide Grundlage für deren Konzepte. Dieser Artikel konzentriert sich auf zwei so leistungsstarke Konzepte: Transaktion und erneute Lieferung . In JMS organisiert eine Transaktion eine Nachricht oder Nachrichtengruppe in einer atomaren Verarbeitungseinheit. Wenn eine Nachricht nicht zugestellt wird, kann dies zur erneuten Zustellung dieser Nachricht oder Nachrichtengruppe führen.

In diesem Artikel helfe ich Ihnen dabei, ein gründliches Verständnis Ihrer Transaktionsoptionen zu entwickeln und zu zeigen, wie Sie deren Auswirkungen auf die erneute Zustellung von Nachrichten bewerten können. Ich gehe davon aus, dass Sie mit der JMS-API sowie mit Message-Driven Beans (MDBs) vertraut sind.

Übersicht über die Transaktionsoptionen

Für eine Anwendung stehen unzählige Transaktionsoptionen zur Verfügung, einschließlich der Frage, ob sie an Transaktionen teilnehmen möchte oder nicht. Wenn Ihre Anwendung keine Transaktionen verwendet, kann sie einen der folgenden Bestätigungsmodi verwenden: Auto, Duplikate in Ordnung und Client. Sie geben die Bestätigungsmodi beim Erstellen einer JMS-Sitzung an. Wenn Ihre Anwendung Transaktionen verwendet, kann sie aus folgenden Transaktionsoptionen auswählen: Transaktionssitzung, MDB mit Container-verwalteter Transaktionsabgrenzung (CMTD) und MDB mit Bean-verwalteter Transaktionsabgrenzung (BMTD). In den folgenden Listen werden diese Bestätigungsmodi und Transaktionsoptionen kurz beschrieben.

Bestätigungsoptionen:

  • Automatischer Modus: Wenn eine Sitzung den automatischen Modus verwendet, werden die von der Sitzung gesendeten oder empfangenen Nachrichten automatisch bestätigt. Dies ist der einfachste Modus und drückt die Leistungsfähigkeit von JMS aus, indem die Garantie für die Zustellung von Nachrichten nur einmal aktiviert wird.

  • OK-Modus für Duplikate: Wenn eine Sitzung den OK-Modus für Duplikate verwendet, werden die von der Sitzung gesendeten oder empfangenen Nachrichten automatisch wie im Auto-Modus bestätigt, wenn auch träge. In seltenen Fällen werden die Nachrichten möglicherweise mehrmals zugestellt. Dieser Modus ermöglicht eine mindestens einmalige Garantie für die Zustellung von Nachrichten.

  • Client-Modus: Wenn eine Sitzung den Client-Modus verwendet, werden die von der Sitzung gesendeten oder empfangenen Nachrichten nicht automatisch bestätigt. Die Anwendung muss den Nachrichtenempfang bestätigen. In diesem Modus hat die Anwendung (und nicht der JMS-Anbieter) die vollständige Kontrolle über die Nachrichtenbestätigung auf Kosten einer erhöhten Codekomplexität.

Andere Arten von Bestätigungsmodi sind möglich. Diese Bestätigungsmodi sind jedoch JMS-Anbieterspezifisch und beeinträchtigen daher die Portabilität von JMS-Anwendungen.

Transaktionsoptionen:

  • Transaktionssitzung: Eine Anwendung kann an einer Transaktion teilnehmen, indem sie eine Transaktionssitzung (oder eine lokale Transaktion) erstellt. Die Anwendung steuert die Nachrichtenübermittlung vollständig, indem sie die Sitzung festschreibt oder zurücksetzt.

  • Nachrichtengesteuerte Beans mit CMTD: Eine MDB kann an einer Containertransaktion teilnehmen, indem sie CMTD im XML-Bereitstellungsdeskriptor angibt. Die Transaktion wird nach erfolgreicher Nachrichtenverarbeitung festgeschrieben, oder die Anwendung kann sie explizit zurücksetzen.

  • Nachrichtengesteuerte Beans mit BMTD: Eine MDB kann sich dafür entscheiden, nicht an einer Containertransaktion teilzunehmen, indem sie BMTD im XML-Bereitstellungsdeskriptor angibt. Der MDB-Programmierer muss programmatische Transaktionen entwerfen und codieren.

Abbildung 1 zeigt einen Entscheidungsbaum der zuvor genannten Transaktionsoptionen.

Bevor wir die Transaktionsoptionen im Detail untersuchen, werden wir den Prozess der Nachrichtenübermittlung untersuchen.

Phasen der Nachrichtenübermittlung

Gegen Ende der Zustellung durchläuft die Nachricht konzeptionell die folgenden Phasen: Nachricht mit JMS-Anbieter und Nachricht in der Anwendungsverarbeitung.

Nachricht mit JMS-Anbieter

In dieser Phase verbleibt die Nachricht beim JMS-Anbieter, kurz bevor der Anbieter sie an die Anwendung übermittelt. Stellen Sie sich eine katastrophale Situation vor, in der der JMS-Anbieter ausfällt. Was passiert mit den Nachrichten, die der Anbieter noch nicht an den Client übermittelt hat? Gehen die Nachrichten verloren?

Das Schicksal der Nachrichten hängt nicht von den zuvor beschriebenen Transaktionsoptionen ab, sondern vom Zustellungsmodus. Es gibt zwei Übermittlungsmodi: nicht persistent und persistent . Nachrichten mit nicht persistenten Übermittlungsmodi gehen möglicherweise verloren, wenn der JMS-Anbieter ausfällt. Nachrichten mit dauerhaften Zustellungsmodi werden protokolliert und in einem stabilen Speicher gespeichert. Der JMS-Anbieter speichert diese Nachrichten in einem stabilen Speicher, z. B. einer Datenbank oder einem Dateisystem, und übermittelt sie schließlich zur Verarbeitung an die Anwendung.

Nachricht in der Antragsbearbeitung

In dieser Phase empfängt die Anwendung die Nachricht vom JMS-Anbieter und verarbeitet sie. Betrachten Sie einen Fehler, der während der Nachrichtenverarbeitung auftritt. Was passiert mit der Nachricht? Geht die Nachricht verloren oder wird sie für eine spätere erfolgreiche Verarbeitung erneut zugestellt? Die Antworten auf diese Fragen hängen von den von Ihnen ausgewählten Transaktionsoptionen ab.

Abbildung 2 zeigt die beiden Verarbeitungsstufen. Das Diagramm zeigt, dass eine Nachricht vom JMS-Anbieter zur Anwendungsverarbeitung verschoben wird.

Im weiteren Verlauf des Artikels verwende ich die in Abbildung 3 gezeigte Aktionslegende, um die verschiedenen Transaktionsoptionen zu veranschaulichen. Wie Abbildung 3 zeigt, zeigt ein ausgefüllter Pfeil eine vom JMS-Anbieter durchgeführte Aktion, während ein umrissener Pfeil eine von der Anwendung ausgeführte Aktion darstellt.

Die Einrichtung

Um die Auswirkungen verschiedener Transaktionsoptionen sowie die erneute Zustellung zu demonstrieren, werde ich einen Absender verwenden. Der Absender sendet einfache Ganzzahlen als Objektnachrichten an eine Warteschlange. Jede Transaktionsoption hat einen anderen Empfänger. Jeder Empfänger demonstriert die Auswirkungen der Auswahl einer bestimmten Transaktionsoption und hebt die Auswirkungen auf die erneute Zustellung von Nachrichten hervor. Der Sender und die Empfänger verwenden gemeinsam verwaltete Objekte: Verbindungsfactory und Warteschlange. Die Verbindungsfactory ist unter jms/QueueConnectionFactoryVerwendung des jms/QueueJNDI-Namens (Java Naming and Directory Interface) verfügbar, während die Warteschlange unter Verwendung des JNDI-Namens verfügbar ist .

Listing 1 zeigt den Code für den Absender:

Listing 1. Absender

package com.malani.examples.jms.transactions; import javax.naming.InitialContext; import javax.jms.*; public class Sender { public static void main(String[] args) { System.out.println("Starting..."); QueueConnectionFactory aQCF = null; QueueConnection aQC = null; QueueSession aQS = null; QueueSender aSender = null; try { InitialContext aIC = new InitialContext(Resource.getResources()); aQCF = (QueueConnectionFactory) aIC.lookup( iConstants.FACTORY_NAME ); aQC = aQCF.createQueueConnection(); aQS = aQC.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); Queue aQueue = (Queue) aIC.lookup(iConstants.QUEUE_NAME); aSender = aQS.createSender(aQueue); aQC.start(); for (int i = 0; i < 10; i++) { aSender.send(aQS.createObjectMessage(new Integer(i))); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (aSender != null) { aSender.close(); } if (aQS != null) { aQS.close(); } if (aQC != null) { aQC.stop(); aQC.close(); } } catch (JMSException e) { e.printStackTrace(); } } System.out.println("Ending..."); } } 

The following sections describe each acknowledgement mode in detail. A receiver demonstrates each acknowledgement mode. Each case uses the sender above to demonstrate the impact and implications of implementing a specific transaction option.

Auto acknowledgement

To implement the auto acknowledgement mode, when you create the receiver's session, specify false as the first argument and Session.AUTO_ACKNOWLEDGE as the second argument of the createSession() factory method. Specifying false creates a nontransacted session. The second parameter creates a session that automatically acknowledges messages. A message is automatically acknowledged when it successfully returns from the receive() method. If the receiver uses the MessageListener interface, the message is automatically acknowledged when it successfully returns from the onMessage() method. If a failure occurs while executing the receive() method or the onMessage() method, the message is automatically redelivered. The JMS provider carefully manages message redelivery and guarantees once-only delivery semantics.

Listing 2 describes the Receiver class. The Receiver is the AutoReceiver class's superclass. The Receiver superclass does most of the heavy lifting. It receives the object messages sent by the Sender class. In the processMessage() method, the receiver prints the message:

Listing 2. Receiver

package com.malani.examples.jms.transactions; import javax.jms.*; import javax.naming.InitialContext; import java.io.InputStreamReader; public abstract class Receiver { protected void doAll() { QueueConnectionFactory aQCF = null; QueueConnection aQC = null; QueueSession aQS = null; QueueReceiver aQR = null; try { InitialContext aIC = new InitialContext(Resource.getResources()); aQCF = (QueueConnectionFactory) aIC.lookup( iConstants.FACTORY_NAME ); aQC = aQCF.createQueueConnection(); aQS = createQueueSession(aQC); final QueueSession aQS1 = aQS; Queue aQueue = (Queue) aIC.lookup(iConstants.QUEUE_NAME); aQR = aQS.createReceiver(aQueue); MessageListener aML = new MessageListener() { public void onMessage(Message aMessage) { try { processMessage(aMessage, aQS1); } catch (JMSException e) { e.printStackTrace(); } } }; aQR.setMessageListener(aML); aQC.start(); InputStreamReader aISR = new InputStreamReader(System.in); char aAnswer = ' '; do { aAnswer = (char) aISR.read(); if ((aAnswer == 'r') || (aAnswer == 'R')) { aQS.recover(); } } while ((aAnswer != 'q') && (aAnswer != 'Q')); } catch (Exception e) { e.printStackTrace(); } finally { try { if (aQR != null) { aQR.close(); } if (aQS != null) { aQS.close(); } if (aQC != null) { aQC.stop(); aQC.close(); } } catch (JMSException e) { e.printStackTrace(); } } } protected void processMessage(Message aMessage, QueueSession aQS) throws JMSException { if (aMessage instanceof ObjectMessage) { ObjectMessage aOM = (ObjectMessage) aMessage; System.out.print(aOM.getObject() + " "); } } protected abstract QueueSession createQueueSession( QueueConnection aQC ) throws JMSException; } 

Listing 3 describes the AutoReceiver class. As shown, the AutoReceiver creates a nontransacted session that automatically acknowledges messages in the createQueueSession() method:

Listing 3. AutoReceiver

package com.malani.examples.jms.transactions; import javax.naming.InitialContext; import javax.jms.*; import java.io.InputStreamReader; public class AutoReceiver extends Receiver { public static void main(String[] args) { System.out.println("Starting..."); new AutoReceiver().doAll(); System.out.println("Ending..."); } protected QueueSession createQueueSession( QueueConnection aQC ) throws JMSException { return aQC.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); } } 

Executing Listing 3 produces the following output; type character q and press Return to end the program:

Starting... Java (TM) Message Service 1.0.2 Reference Implementation (build b14) 0 1 2 3 4 5 6 7 8 9 q Ending... 

In Figure 4, a message is automatically acknowledged after the application successfully processes it, which is after the message returns from the onMessage() method.

Duplicates okay acknowledgement

The duplicates okay acknowledgement mode closely resembles the auto acknowledgement mode. However, rather than pass Session.AUTO_ACKNOWLEDGE, you specify Session.DUPS_OK_ACKNOWLEDGE as the acknowledgement mode of createSession()'s second argument. With less overhead than auto mode, in duplicates okay mode, the JMS provider guarantees at-least-once message delivery. During failure recovery, certain messages are probably delivered more than once.

Listing 4 beschreibt die DuplicatesOkayReceiverKlasse, die die ReceiverOberklasse erweitert. Wie gezeigt, DuplicatesOkayReceiverwird eine nicht abgewickelte Sitzung mit Duplikaten erstellt. OK Bestätigungsmodus in der createQueueSession()Methode:

Listing 4. DuplicatesOkayReceiver