Scrivi la tua mamma!

La mamma è fraintesa e la mamma non ottiene alcun merito. Potresti averlo sentito prima, ma nell'arena dei sistemi distribuiti è effettivamente vero! Questo perché il middleware orientato ai messaggi (MOM) tradizionalmente non ha goduto dello stesso livello di sofisticazione e supporto delle altre tecnologie utilizzate nei framework di comunicazione distribuita.

Ma i tempi stanno cambiando. Con l'introduzione di offerte di fornitori sofisticate e solide, l'interesse per i sistemi MOM sta crescendo rapidamente. Le buone implementazioni di MOM forniscono un'interfaccia di applicazioni di alto livello, garanzie di qualità del servizio e una serie di servizi come la sicurezza, l'accodamento dei messaggi e il supporto di directory necessari per comunicazioni distribuite "di livello industriale".

Framework di comunicazioni distribuite

Lo scopo di un framework di comunicazione distribuito è fornire un buon modo per comunicare tra le parti di un sistema distribuito. I framework orientati agli oggetti eseguono questo compito fornendo agli oggetti distribuiti un modo per scambiarsi messaggi.

I framework distribuiti orientati agli oggetti che ottengono la massima attenzione sono quelli che modellano la messaggistica come chiamate di metodo. CORBA e RMI sono due ottimi esempi di questo tipo di framework (vedi Risorse). Questi sistemi sono spesso chiamati sistemi RPC (Remote Procedure Call). La magia di questi sistemi è che fanno sembrare che le chiamate di procedura remota (o metodo) siano chiamate di procedura locale (LPC).

Gli RPC sono progettati sul modello client / server. Ad esempio, gli oggetti CORBA che espongono metodi che devono essere chiamati da oggetti remoti sono chiamati (e sono) server.

Presentazione di MOM

A differenza degli RPC, i MOM non modellano i messaggi come chiamate di metodo; invece, li modellano come eventi in un sistema di consegna di eventi. I client inviano e ricevono eventi o "messaggi" tramite le API fornite da MOM. Il MOM può presentare servizi di directory che consentono ai client di cercare un'altra applicazione che funge da server, oppure può presentare "canali" per tutti gli usi che consentono a un gruppo di client di comunicare come peer, oppure può presentare entrambe le opzioni.

Tutte le applicazioni comunicano direttamente tra loro utilizzando il MOM. I messaggi generati dalle applicazioni sono significativi solo per altri client perché lo stesso MOM è solo un router di messaggi (e in alcuni casi anche un sistema di accodamento dei messaggi).

Le mamme sono disponibili in tutte le forme e dimensioni

Tutte le MOM condividono due caratteristiche fondamentali: abilitano il passaggio dei messaggi e il passaggio dei messaggi non è bloccante. Oltre a queste nozioni di base, i fornitori possono implementare un numero qualsiasi di interfacce e servizi diversi.

Molte MOM forniscono un'interfaccia di pubblicazione e sottoscrizione per consentire alle applicazioni di pubblicare e ricevere messaggi a cui sono interessati. Questa interfaccia può assumere la forma di un sistema basato su canali o di un sistema più semplice in cui un client registra i tipi di messaggi è interessato a ricevere.

Le MOM di base forniscono solo messaggistica diretta, senza servizi aggiuntivi. Le MOM avanzate forniscono accodamento dei messaggi e recapito garantito, oltre a sicurezza, marshalling dei dati multipiattaforma, scalabilità e altri vantaggi.

Mamme a colpo d'occhio

Ecco un rapido riferimento per aiutarti a capire cosa sono le MOM.

Vantaggi della mamma

  • Semplice : i clienti pubblicano e si iscrivono

    Pubblica e iscriviti è un'utile astrazione di alto livello per ciò che le app devono fare per comunicare.

  • Facile : nessuna configurazione complicata richiesta

    Le MOM sono facili da installare e utilizzare, a differenza dei complessi sistemi basati su RPC come CORBA.

  • Generico : la stessa MOM può essere utilizzata per più app

    Poiché un determinato sistema MOM è essenzialmente solo un trasporto di messaggi generico, può essere riutilizzato in diverse applicazioni senza alcun lavoro aggiuntivo.

  • Flessibile : qualsiasi tipo di messaggio può essere passato

    Qualsiasi messaggio può essere passato dalla MOM. Poiché la MAMMA non comprende i messaggi, non importa cosa siano.

Svantaggi della mamma

  • Generico : le applicazioni devono comprendere i messaggi

    Fare in modo che le applicazioni utilizzino i messaggi invece delle chiamate ai metodi può essere complicato, soprattutto se l'applicazione si basa sul fatto che le chiamate ai metodi si bloccano.

  • Non familiare : non modella le chiamate ai metodi

    Gli sviluppatori che non hanno familiarità con i messaggi potrebbero avere difficoltà a capire come usarli in modo efficace.

  • Asincrono : i messaggi non sono bloccanti

    I messaggi sono naturalmente non bloccanti. Ciò rende più difficile scrivere app che richiedono il blocco delle chiamate.

  • Troppo semplice : nessun marshalling dei dati

    Anche i sistemi RPC semplici eseguono il marshalling dei dati correttamente. Le semplici MOM possono semplicemente inviare messaggi in cui i byte sono fuori servizio dal punto di vista del destinatario.

  • Non standard : i fornitori sono dappertutto

    Le implementazioni MOM del fornitore fanno tutto ... e niente.

    Caveat Emptor

    è la frase da tenere a mente quando si esaminano le varie offerte dei fornitori.

Quando sono appropriate le mamme?

  • Quando si comunicano le app è necessario utilizzare i messaggi
  • Quando il personale di programmazione non è collegato a sistemi client / server e RPC
  • Quando CORBA / RMI e i sistemi correlati sono troppo complessi
  • Quando i sistemi RPC semplici sono troppo rudimentali

Considerazioni sulla progettazione per la nostra mamma

Ora che lo sfondo è fuori mano, iniziamo a mettere insieme la nostra MOM, il Message Bus . Useremo il MOM per abilitare la comunicazione tra i client della lavagna distribuita. (Vedere Risorse per i collegamenti alle informazioni sull'applicazione della lavagna con cui abbiamo lavorato nelle ultime puntate.)

La considerazione principale per il Message Bus è che fornisce una comoda interfaccia di comunicazione di alto livello agli oggetti dell'applicazione che lo utilizzeranno.

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; } 

Questo metodo di classe restituisce l'istanza del canale corrispondente al nome del canale. Crea il canale e chiama MessageBusper aggiungerlo al sistema se non esiste già. Non appena il canale viene creato, il suo ascoltatore iniziale viene registrato con esso.

// chiamato dai client per registrare ChannelsUpdateListener public static void subscribeChannelsUpdates (ChannelsUpdateListener cul) {channelsUpdateListeners.addElement (cul); }

// chiamato dai client per annullare la registrazione di ChannelsUpdateListener public static void unsubscribeChannelsUpdates (ChannelsUpdateListener cul) {channelsUpdateListeners.removeElement (cul); }