Java Message Service (JMS)

ActiveMQ

JMS kann für das Versenden von Nachrichten über Rechnergrenzen oder zwischen Laufzeitumgebungen (JVM) verwendet werden. Die Art des Nachrichtenkanals bestimmt die Auslieferung der Nachrichten an einen (Queue) oder mehrere (Topic) Teilnehmer. Je nach Problemstellung kann somit die Zustellung von Nachrichten garantiert (Queue) oder einfach als Benachrichtigung für aktuelle Empfänger (Topic) eingesetzt werden.

Apache ActiveMQ ist ein freier Message Broker, der vollständig das Java Message Service 1.1 (JMS) implementiert. Abgesehen von JMS unterstützt er Protokolle wie AMQP 1.0, STOMP und OpenWire.

Ein Broker kennt zwei Arten von Nachrichtenkanälen – Queue und Topic. Sobald der Broker die erste Nachricht für einen Nachrichtenkanal empfangen hat, wird die Art des Kanals automatisch festgestellt.

Queues kennen zwei Arten von Clients – Erzeuger (Producer) und Verbraucher (Consumer). Hat ein Verbraucher eine Nachricht von einem Erzeuger empfangen, wird diese Nachricht aus der Queue gelöscht und steht den anderen Verbrauchern nicht mehr zur Verfügung. Eine Nachricht kann also nur von einem einzelnen Verbraucher empfangen (konsumiert) werden.

image2015-11-4 13_41_50

Topics kennen ebenfalls zwei Arten von Clients – Publisher und Subscriber. In diesem Fall ist es jedoch möglich, dass mehrere Subscriber die gleiche Nachricht empfangen.

 

image2015-11-4 13_42_33

ActiveMQ kann als externer Server oder Embedded-Broker konfiguriert werden. Für den Produktiven Einsatz empfiehlt sich die externe Server-Variante zu verwenden.

Für die erfolgreiche Verbindung zu dem ActiveMQ Server muss in der applications.properties der Eintrag jms.connector.uri angegeben werden. Je nach gewünschtem Kommunikationsprotokoll kann die Verbindungs URI wie folgt angegeben werden.

jms.connector.uri = <PROTOCOL>://<HOST>:<PORT>

Beispiel:

jms.connector.uri= tcp://<HOST>:61616

oder

jms.connector.uri=nio://<HOST>:61615

Die Liste der gültigen Protokolle kann unter http://activemq.apache.org/uri-protocols.html eingesehen werden.

Wichtig ist es, dass die für die Applikation verwendete URI auch in der activemq.xml für Activemq korrekt konfiguriert ist.

Die Ports können je nach System unterschiedlich konfiguriert sein.

Konfiguration

Für die Spring Konfiguration kann eine XML-Konfiguration oder auch eine Konfigurations-Klasse verwendet werden.

Die Annotations @Configuration, @EnableJms, @Component müssen für Spring gesetzt werden.

  • @Configuration signalisiert dem Spring-Container, dass diese Klasse @Bean-Methoden enthält, die zur Initialisierung des Spring-Containers verarbeitet werden müssen, um notwendige Beans zu generieren.
  • @EnableJms aktiviert die als @JMSListener annotierten Endpunkte, welche durch eine JmsListenerContainerFactory erzeugt werden. Ohne diese Annotation werden die erzeugten Listener nicht aktiviert.
  • @Component signalisiert Spring, dass es sich um eine Klasse handelt, die durch Spring verarbeitet werden muss.

In der JMSConfiguration werden die Beans definiert.

Die ConnectionFactory ist verantwortlich für die Verbindungen zu ActiveMQ. Für den ordentlichen Betrieb muss eine Broker-URL mit setBrokerURL() gesetzt werden. Wird die ConnectionFactory für mehr als einen Endpunkt (Consumer oder Producer) verwendet, müssen diese eine eindeutige ID zugewiesen bekommen, dafür kann mit setClientIDPrefix(CLIENT_ID_PREFIX) ein Präfix für automatisch generierte Connections gesetzt werden.

JMSListener laufen in einem Container. Für jeden Listener wird ein Container erstellt. Für die Erstellung ist die JmsListenerContainerFactory verantwortlich. Wichtig ist der Aufruf setPubSubDomain(true). Durch diesen Aufruf wird der Listener zu einem Topic-Listener deklariert. Wird eine parallele Verarbeitung der Nachrichten gewünscht, muss der Aufruf setTaskExecutor() erfolgen.

Über JMS versendete Objekte müssen serialisiert werden. Diese Aufgabe übernimmt der MessageConverter. Es kann als Transport-Format json verwendet werden. Hierfür stellt das Spring-Framework den MappingJackson2MessageConverter zur Verfügung. Wenn kein MessageConverter verwendet werden, wird automatisch DefaultMessageConverter von JMS angewendet.

Zum Versenden von Nachrichten ist ein Sender (Producer) notwendig. Im Spring-Kontext nennt man diesen Sender JmsTemplate. Oben ist ein Beispiel für eine Queue dargestellt. Beim Topic ist es wichtig, dass der Aufruf setPubSubDomain(true) ist. Durch diesen Aufruf wird das durch setDefaultDestination() gesetzte Ziel als Topic deklariert. Die Methode setSessionTransacted() ist für die Performance sehr wichtig. Wenn MessageListener eine Exception auslöst, wird bei setSessionTransacted(true) die Nachricht wieder in die Queue eingestellt. Der Nachteil ist ein Performance-Verlust. Beim Topic kann das zur Performance-Optimierung abgeschaltet werden.

Senden und Empfangen

Producer

Das Senden von Nachrichten übernimmt ein Producer. Eine Beispiel-Implementierung ist unten zu sehen.

In diesem Beispiel wird per Spring das konfigurierte Template injiziert. Das Template sendet durch einen MessageCreator konvertierte Nachricht an die konfigurierte Queue(Nachrichtenkanal). Alternativ kann auch jmsTopicTemplate.convertAndSend(nachricht) verwendet werden, um den konfigurierten MessageConverter zu verwenden.

Consumer

Für den Empfang der Nachrichten muss mindestens ein Consumer definiert werden.

Ein Listener wird durch den Tag @JmsListener deklariert. Dieser sollte entweder durch einen ListenerContainer oder eine ListenerContainerFactory parametrisiert werden. Optional kann ein Message Selektor als Filter angegeben werden. Die Angaben zum Selektor müssen in der Message gesetzt werden.

Frameworks – Brokers

Es gibt auch neben ActiveMQ weitere Messaging Frameworks. Die populärsten sind:

  • RabbitMQ
  • HornetMQ
  • JBoss Messaging
  • OpenJMS
  • QPid
  • ZeroMQ

Alle diese Frameworks haben ihre Vor- und Nachteile. Je nach Problemstellung kann eine Auswahl getroffen werden. Die am besten unterstützten und für die meisten Aufgaben passenden Frameworks sind ActiveMQ und RabbitMQ.

Verfasst von Senay Uzuner am 16. Mai 2017