¡Escribe tu propia MAMÁ!

MOM es incomprendida y MOM no recibe crédito. Es posible que haya escuchado esto antes, pero en el campo de los sistemas distribuidos es realmente cierto. Esto se debe a que el middleware orientado a mensajes (MOM) tradicionalmente no ha disfrutado del mismo nivel de sofisticación y soporte que otras tecnologías utilizadas en marcos de comunicaciones distribuidas.

Pero los tiempos están cambiando. Con la introducción de ofertas de proveedores robustas y sofisticadas, el interés en los sistemas MOM está creciendo rápidamente. Las buenas implementaciones de MOM proporcionan una interfaz de aplicaciones de alto nivel, garantías de calidad de servicio y una gran cantidad de servicios como seguridad, cola de mensajes y soporte de directorio que son necesarios para las comunicaciones distribuidas de "potencia industrial".

Marcos de comunicaciones distribuidas

El propósito de un marco de comunicaciones distribuidas es proporcionar una buena forma para que las partes de un sistema distribuido se comuniquen. Los marcos orientados a objetos realizan esta tarea proporcionando a los objetos distribuidos una forma de enviarse mensajes entre sí.

Los marcos distribuidos orientados a objetos que reciben la mayor atención son los que modelan la mensajería como llamadas a métodos. CORBA y RMI son dos excelentes ejemplos de este tipo de marco (ver Recursos). Estos sistemas a menudo se denominan sistemas de llamada a procedimiento remoto (RPC). La magia de estos sistemas es que hacen que las llamadas a procedimientos (o métodos) remotos parezcan llamadas a procedimientos locales (LPC).

Las RPC se diseñan según el patrón cliente / servidor. Por ejemplo, los objetos CORBA que exponen métodos para ser llamados por objetos remotos se denominan (y son) servidores.

Presentando a MOM

A diferencia de los RPC, los MOM no modelan los mensajes como llamadas a métodos; en cambio, los modelan como eventos en un sistema de entrega de eventos. Los clientes envían y reciben eventos o "mensajes" a través de las API que proporciona MOM. El MOM puede presentar servicios de directorio que permiten a los clientes buscar otra aplicación que actúa como servidor, o puede presentar "canales" de uso múltiple que permiten que un grupo de clientes se comunique como pares, o puede presentar ambas opciones.

Todas las aplicaciones se comunican directamente entre sí mediante MOM. Los mensajes generados por las aplicaciones son significativos solo para otros clientes porque el MOM en sí es solo un enrutador de mensajes (y en algunos casos también un sistema de cola de mensajes).

Las mamás vienen en todas las formas y tamaños

Todas las MOM comparten dos características fundamentales: permiten el paso de mensajes y el paso de mensajes no es bloqueante. Más allá de estos conceptos básicos, los proveedores pueden implementar cualquier cantidad de interfaces y servicios diferentes.

Muchas MOM proporcionan una interfaz de publicación y suscripción para permitir que las aplicaciones publiquen y reciban los mensajes que les interesan. Esta interfaz puede adoptar la forma de un sistema basado en canales o un sistema más simple en el que un cliente registra los tipos de mensajes le interesa recibir.

Los MOM básicos solo brindan mensajería directa, sin servicios adicionales. Los MOM avanzados brindan cola de mensajes y entrega garantizada, junto con seguridad, clasificación de datos multiplataforma, escalabilidad y otros beneficios.

MOMs de un vistazo

Aquí hay una referencia rápida para ayudarlo a comprender de qué se tratan las mamás.

Ventajas de MOM

  • Simple : los clientes publican y se suscriben

    publicar y suscribirse es una abstracción útil de alto nivel de lo que las aplicaciones deben hacer para comunicarse.

  • Fácil : no se requiere una configuración complicada

    Los MOM son fáciles de instalar y usar, a diferencia de los sistemas complejos basados ​​en RPC como CORBA.

  • Genérico : el mismo MOM se puede utilizar para varias aplicaciones

    Dado que cualquier sistema MOM determinado es esencialmente un transporte de mensajes genérico, se puede reutilizar en diferentes aplicaciones sin ningún trabajo adicional.

  • Flexible : se puede transmitir cualquier tipo de mensaje

    La MOM puede transmitir cualquier mensaje. Debido a que MOM no comprende los mensajes, no importa cuáles sean.

Desventajas de mamá

  • Genérico : las aplicaciones deben comprender los mensajes

    Hacer que las aplicaciones usen mensajes en lugar de llamadas a métodos puede ser complicado, especialmente si la aplicación se basa en el hecho de que las llamadas a métodos se bloquean.

  • No familiar : no modela llamadas a métodos

    Los desarrolladores que no estén familiarizados con los mensajes pueden tener problemas para descubrir cómo usarlos de manera efectiva.

  • Asincrónico : los mensajes no se bloquean

    Los mensajes son naturalmente no bloqueantes. Esto hace que sea más difícil escribir aplicaciones que necesiten bloquear llamadas.

  • Demasiado simple : sin clasificación de datos

    Incluso los sistemas RPC simples recopilan los datos correctamente. Los MOM simples pueden simplemente enviar mensajes en los que los bytes están desordenados desde el punto de vista del receptor.

  • No estándar : los proveedores están en todos los ámbitos

    Las implementaciones del proveedor MOM hacen todo ... y nada.

    Caveat Emptor

    es la frase a tener en cuenta al revisar las distintas ofertas de los proveedores.

¿Cuándo son apropiadas las mamás?

  • Cuando las aplicaciones de comunicación necesitan usar mensajes
  • Cuando el personal de programación no está vinculado a los sistemas cliente / servidor y RPC
  • Cuando CORBA / RMI y los sistemas relacionados son demasiado complejos
  • Cuando los sistemas RPC simples son demasiado rudimentarios

Consideraciones de diseño para nuestra mamá

Ahora que el trasfondo está fuera del camino, comencemos a armar nuestro MOM, el Bus de mensajes . Usaremos MOM para habilitar la comunicación entre clientes de pizarra distribuida. (Consulte Recursos para obtener enlaces a información sobre la aplicación de pizarra con la que hemos estado trabajando en las últimas entregas).

La consideración principal para el bus de mensajes es que proporciona una conveniente interfaz de comunicaciones de alto nivel para los objetos de aplicación que lo utilizarán.

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

Este método de clase devuelve la instancia del canal correspondiente al nombre del canal. Crea el canal y llama MessageBuspara agregarlo al sistema si aún no existe. Tan pronto como se crea el canal, su oyente inicial se registra en él.

// llamado por los clientes para registrar ChannelsUpdateListener public static void subscribeChannelsUpdates (ChannelsUpdateListener cul) {channelsUpdateListeners.addElement (cul); }

// llamado por los clientes para dar de baja ChannelsUpdateListener public static void unsubscribeChannelsUpdates (ChannelsUpdateListener cul) {channelsUpdateListeners.removeElement (cul); }