Schreiben Sie Ihre eigene Mutter!

MOM wird missverstanden und MOM bekommt keine Gutschrift. Sie haben das vielleicht schon einmal gehört, aber in der Arena der verteilten Systeme ist es tatsächlich wahr! Dies liegt daran, dass die nachrichtenorientierte Middleware (MOM) traditionell nicht den gleichen Grad an Raffinesse und Unterstützung aufweist wie andere Technologien, die in Frameworks für verteilte Kommunikation verwendet werden.

Aber die Zeiten ändern sich. Mit der Einführung anspruchsvoller, robuster Anbieterangebote wächst das Interesse an MOM-Systemen rasant. Gute MOM-Implementierungen bieten eine Anwendungsschnittstelle auf hoher Ebene, Garantien für die Dienstqualität und eine Vielzahl von Diensten wie Sicherheit, Nachrichtenwarteschlange und Verzeichnisunterstützung, die für verteilte Kommunikation mit "industrieller Stärke" erforderlich sind.

Verteilte Kommunikations-Frameworks

Der Zweck eines Frameworks für verteilte Kommunikation besteht darin, den Teilen eines verteilten Systems eine gute Möglichkeit zur Kommunikation zu bieten. Objektorientierte Frameworks erfüllen diese Aufgabe, indem sie verteilten Objekten eine Möglichkeit bieten, sich gegenseitig Nachrichten zu senden.

Die verteilten objektorientierten Frameworks, die die größte Aufmerksamkeit erhalten, sind diejenigen, die Messaging als Methodenaufrufe modellieren. CORBA und RMI sind zwei hervorragende Beispiele für diese Art von Framework (siehe Ressourcen). Diese Systeme werden häufig als RPC-Systeme (Remote Procedure Call) bezeichnet. Die Magie dieser Systeme besteht darin, dass sie Remoteprozedur- (oder Methoden-) Aufrufe als lokale Prozeduraufrufe (LPCs) erscheinen lassen.

RPCs werden nach dem Client / Server-Muster erstellt. Beispielsweise werden CORBA-Objekte, die Methoden verfügbar machen, die von Remote-Objekten aufgerufen werden sollen, als Server bezeichnet (und sind).

Wir stellen vor: MOM

Im Gegensatz zu RPCs modellieren MOMs Nachrichten nicht als Methodenaufrufe. Stattdessen modellieren sie sie als Ereignisse in einem Ereignisübermittlungssystem. Clients senden und empfangen Ereignisse oder "Nachrichten" über APIs, die das MOM bereitstellt. Das MOM kann Verzeichnisdienste präsentieren, mit denen Clients eine andere Anwendung nachschlagen können, die als Server fungiert, oder es kann Allzweck- "Kanäle" präsentieren, mit denen eine Gruppe von Clients als Peers kommunizieren kann, oder es können beide Optionen angezeigt werden.

Alle Anwendungen kommunizieren über die MOM direkt miteinander. Von Anwendungen generierte Nachrichten sind nur für andere Clients von Bedeutung, da die MOM selbst nur ein Nachrichtenrouter (und in einigen Fällen auch ein Nachrichtenwarteschlangensystem) ist.

MOMs gibt es in allen Formen und Größen

Alle MOMs haben zwei grundlegende Merkmale gemeinsam: Sie ermöglichen das Weiterleiten von Nachrichten und das Weiterleiten von Nachrichten ist nicht blockierend. Über diese Grundlagen hinaus können Anbieter eine beliebige Anzahl verschiedener Schnittstellen und Dienste implementieren.

Viele MOMs bieten eine Publish-and-Subscribe-Schnittstelle, über die Anwendungen Nachrichten veröffentlichen und empfangen können, an denen sie interessiert sind. Diese Schnittstelle kann in Form eines kanalbasierten Systems oder eines einfacheren Systems vorliegen, in dem ein Client die Nachrichtentypen registriert es ist am Empfangen interessiert.

Grundlegende MOMs bieten nur Direktnachrichten, keine zusätzlichen Dienste. Erweiterte MOMs bieten Nachrichtenwarteschlangen und garantierte Zustellung sowie Sicherheit, plattformübergreifendes Daten-Marshalling, Skalierbarkeit und andere Vorteile.

MOMs auf einen Blick

Hier ist eine Kurzreferenz, die Ihnen hilft, einen Überblick darüber zu bekommen, worum es bei MOMs geht.

MOM Vorteile

  • Einfach : Clients veröffentlichen und abonnieren

    Publish-and-Subscribe ist eine nützliche Abstraktion auf hoher Ebene für das, was Apps für die Kommunikation tun müssen.

  • Einfach : Keine komplizierte Einrichtung erforderlich

    MOMs sind im Gegensatz zu komplexen RPC-basierten Systemen wie CORBA einfach zu installieren und zu verwenden.

  • Allgemein : Dieselbe MOM kann für mehrere Apps verwendet werden

    Da jedes MOM-System im Wesentlichen nur ein generischer Nachrichtentransport ist, kann es ohne zusätzlichen Aufwand in verschiedenen Anwendungen wiederverwendet werden.

  • Flexibel : Jede Art von Nachricht kann weitergeleitet werden

    Jede Nachricht kann von der MOM weitergeleitet werden. Da die MOM die Nachrichten nicht versteht, spielt es keine Rolle, was sie sind.

MOM Nachteile

  • Allgemein : Anwendungen müssen Nachrichten verstehen

    Es kann schwierig sein, Anwendungen dazu zu bringen, Nachrichten anstelle von Methodenaufrufen zu verwenden, insbesondere wenn die Anwendung auf der Tatsache beruht, dass Methodenaufrufe blockieren.

  • Unbekannt : Modelliert keine Methodenaufrufe

    Entwickler, die mit Nachrichten nicht vertraut sind, haben möglicherweise Probleme, herauszufinden, wie sie effektiv verwendet werden können.

  • Asynchron : Nachrichten sind nicht blockierend

    Nachrichten sind natürlich nicht blockierend. Dies macht es schwieriger, Apps zu schreiben, die Anrufe blockieren müssen.

  • Zu einfach : Kein Daten-Marshalling

    Selbst einfache RPC-Systeme stellen Daten korrekt bereit. Einfache MOMs senden möglicherweise nur Nachrichten, bei denen die Bytes aus Sicht des Empfängers nicht in Ordnung sind.

  • Nicht standardisiert : Anbieter sind auf der ganzen Linie

    Vendor MOM-Implementierungen machen alles ... und nichts.

    Vorbehalt Emptor

    ist der Satz, den Sie bei der Überprüfung der verschiedenen Anbieterangebote berücksichtigen sollten.

Wann sind MOMs angemessen?

  • Bei der Kommunikation müssen Apps Nachrichten verwenden
  • Wenn das Programmierpersonal nicht mit Client / Server- und RPC-Systemen verbunden ist
  • Wenn CORBA / RMI und verwandte Systeme zu komplex sind
  • Wenn einfache RPC-Systeme zu rudimentär sind

Designüberlegungen für unsere MOM

Nachdem der Hintergrund nicht mehr im Weg ist, können wir unsere MOM, den Message Bus, zusammenstellen . Wir werden die MOM verwenden, um die Kommunikation zwischen verteilten Whiteboard-Clients zu ermöglichen. (Unter Ressourcen finden Sie Links zu Informationen über die Whiteboard-Anwendung, mit der wir in den letzten Abschnitten gearbeitet haben.)

Die treibende Überlegung für den Nachrichtenbus ist, dass er eine bequeme Kommunikationsschnittstelle auf hoher Ebene zu den Anwendungsobjekten bereitstellt, die ihn verwenden werden.

Because a channel makes sense as the central service that the Message Bus should provide, the interface to the Message Bus is the Channel class. The client uses the Channel class to access every high-level function of the Message Bus, from subscribing and publishing to listing available channels in the system.

The Channel class exposes class methods that affect the Message Bus as a whole, or pertain to all channels. Each channel instance represents a single channel in the system and exposes channel-specific methods.

Two interfaces, ChannelListener and ChannelsUpdateListener, are provided for the purposes of subscribing to receive messages on a channel and receiving notification of channel addition, respectively.

The image below illustrates the Message Bus system architecture.

Under the hood

Under the hood, the Message Bus application uses class methods and data structures of

Channel

to keep track of channels. Listeners to a channel implement the

ChannelListener

interface, and objects that want to receive updates about channel adds implement the

ChannelsUpdateListener

interface. Registered listener objects are called back by

Channel

whenever anything interesting happens. All communication with the outside world is done with a transport-specific implementation of the

MessageBus

interface, such as

MessageBusSocketImpl

.

Each MessageBus implementation passes messages by talking to a corresponding message-passing server, called a broker, over a shared network transport such as sockets or URL/servlets. The broker routes messages among MessageBus instances, each of which corresponds to a Channel class.

Because these transport-specific implementations all implement the MessageBus interface, they are interchangeable. For example, a servlet-based MessageBus and broker can be used by Channel in place of the sockets-based MessageBus and broker.

Our Message Bus is a simple peer-to-peer system based on channels, making it suitable for use in a peer-to-peer application such as a collaborative system.

Using the Message Bus in a client application

These steps allow a client to use the Message Bus:

  1. Set up an instance of MessageBus.

     Channel.setMessageBus (new MessageBusSocketImpl (BROKER_NAME, BROKER_PORT)); 

    In this call, a new MessageBus implementation is created, with the broker identified by the arguments to the constructor call.

  2. Subscribe to a channel.

     Channel textChannel = Channel.subscribe ("text_channel", this); 

    This call returns an instance of the channel corresponding to the channel name argument. If the channel does not exist, it is created in the system.

    Passing this as an argument means that that caller is itself a ChannelListener. The caller can subscribe not just itself but any ChannelListener to the channel, or any number of listeners to a single channel.

  3. Publish a message to the channel.

     textChannel.publish (new String (myID + " says Hello!")); 

    Publishing a message is easy and entails nothing more than calling publish() on the chosen channel instance. Note that the message can be any type of object, as long as other clients on the channel can understand it, and the server has access to the message class file(s) (as detailed in the Using the Message Bus section)

Additional optional steps include:

  • Unsubscribe a listener from a channel.

     textChannel.unsubscribe (ChannelListener); 

    This method unsubscribes the named ChannelListener from the channel, which means that the listener will receive no new messages. Listeners should be unsubscribed in this manner when they are no longer needed.

  • Get a listing of channel names.

     Enumeration Channel.getChannelNames (); 

    This method returns the names of all channels available on the Message Bus.

  • Subscribe to receive newly added channels.

     Channel.subscribeChannelsUpdate (ChannelsUpdateListener); 

    A ChannelsUpdateListener can subscribe to get updates when channels are added to the Message Bus.

  • Stop receiving newly added channels.

     Channel.unsubscribeChannelsUpdate (ChannelsUpdateListener); 

    A ChannelsUpdateListener can be unsubscribed from channel addition updates. Listeners should be unsubscribed in this manner when they are no longer needed.

  • Add more listeners to a channel.

     textChannel.subscribe (ChannelListener); 

    This method allows the caller to subscribe additional listeners to a channel.

     String textChannel.getName (); 

    This method returns the name of this channel instance.

Interface ChannelListener

The ChannelListener interface must be implemented by any object that wants to be updated when a message comes in on a particular channel.

public interface ChannelListener { public void messageReceived (Channel channel, Object message); } 

In most cases, a client that asks for a Channel instance will subscribe itself to the channel and implement this interface itself, but it isn't necessary. In keeping with JDK 1.1 event adapters, a client can subscribe another object to a channel so that it will consume messages generated by the channel.

In fact, a single listener object can subscribe to multiple channels, which will call the listener's messageReceived() every time a message comes in on any of the channels. The messageReceived () method call provides access to the channel where the message appeared, allowing messageReceived () to separate messages by originating channel.

Interface ChannelsUpdateListener

ChannelsUpdateListener must be implemented by any object that wants to be updated when a channel is added.

public interface ChannelsUpdateListener { public void channelAdded (String name); } 

Class Channel

The Channel class serves two purposes:

  • It provides a simple abstraction as an interface to the client using the Message Bus
  • It maintains global state about available channels and passes messages from channels to the MessageBus implementation and receives updates from the MessageBus implementation

Channel instances are created and stored by Channel's static code. References to them are passed out by Channel.subscribe () as requested by the client. Each Channel instance is unique within the JVM process.

public class Channel {

protected static boolean busSet = false; protected static MessageBus bus; protected static Hashtable channels = new Hashtable (); protected static Vector channelsUpdateListeners = new Vector ();

public static synchronized void setMessageBus (MessageBus mb) throws IOException { if (!busSet) { bus = mb; bus.initBroker (); busSet = true; } else System.out.println ("Can't set MessageBus more than once per runtime!"); }

public static String getBrokerName () { return bus.getBrokerName (); }

public static Enumeration getChannelNames () { return channels.keys (); }

These class methods allow the MessageBus instance to be set once for each runtime, and return information about the bus and channel names, respectively.

 public static synchronized Channel subscribe (String name, ChannelListener cl) throws IOException { Channel ch; if (channels.containsKey (name)) ch = (Channel) channels.get (name); else { bus.addChannel (name); ch = new Channel (name); channels.put (name, ch); } ch.subscribe (cl); return ch; } 

Diese Klassenmethode gibt die Kanalinstanz zurück, die dem Kanalnamen entspricht. Es erstellt den Kanal und ruft MessageBusauf, um ihn dem System hinzuzufügen, falls er noch nicht vorhanden ist. Sobald der Kanal erstellt wurde, wird sein erster Listener bei ihm registriert.

// von Clients aufgerufen, um ChannelsUpdateListener zu registrieren public static void subscribeChannelsUpdates (ChannelsUpdateListener cul) {channelUpdateListeners.addElement (cul); }}

// von Clients aufgerufen, um die Registrierung von ChannelsUpdateListener aufzuheben public static void unsubscribeChannelsUpdates (ChannelsUpdateListener cul) {channelUpdateListeners.removeElement (cul); }}