Napišite svoju MAMU!

MAMA se pogrešno razumije, a MOM ne dobiva kredit. Možda ste ovo već čuli, ali u areni distribuiranih sustava to je zapravo istina! To je zato što srednji softver usmjeren na poruke (MOM) tradicionalno ne uživa istu razinu sofisticiranosti i podrške kao druge tehnologije koje se koriste u distribuiranim komunikacijskim okvirima.

Ali vremena se mijenjaju. Uvođenjem sofisticirane, robusne ponude dobavljača, interes za MOM sustave brzo raste. Dobre implementacije MOM-a pružaju sučelje aplikacija na visokoj razini, jamstvo kvalitete usluge i mnoštvo usluga kao što su sigurnost, čekanje poruka i podrška za direktorij potrebne za distribuiranu komunikaciju "industrijske snage".

Distribuirani komunikacijski okviri

Svrha okvira distribuiranih komunikacija je pružiti dobar način za komunikaciju dijelova distribuiranog sustava. Objektno orijentirani okviri ostvaruju ovaj zadatak pružajući distribuiranim objektima način da međusobno poručuju.

Distribuirani objektno orijentirani okviri koji privlače najviše pozornosti su oni koji oblikuju poruke kao pozive metoda. CORBA i RMI dva su izvrsna primjera ove vrste okvira (vidi Resursi). Ti se sustavi često nazivaju i sustavi za daljinski poziv (RPC). Magija ovih sustava je u tome što čine da se pozivi udaljenih procedura (ili metoda) čine lokalni pozivi procedura (LPC).

RPC-ovi se oblikuju na uzorku klijent / poslužitelj. Na primjer, CORBA objekti koji izlažu metode koje se pozivaju udaljenim objektima nazivaju se (i jesu) poslužiteljima.

Predstavljamo MOMU

Za razliku od RPC-a, MOM-e poruke ne modeliraju kao pozive metoda; umjesto toga, modeliraju ih kao događaje u sustavu isporuke događaja. Klijenti šalju i primaju događaje ili "poruke" putem API-ja koje pruža MOM. MOM može predstaviti usluge direktorija koje omogućavaju klijentima da potraže drugu aplikaciju koja djeluje kao poslužitelj, ili može predstaviti višenamjenske "kanale" koji omogućavaju skupini klijenata da komuniciraju kao vršnjaci ili može predstaviti obje opcije.

Sve aplikacije izravno međusobno komuniciraju pomoću MOM-a. Poruke koje generiraju aplikacije smislene su samo za druge klijente, jer je sam MOM samo usmjerivač poruka (a u nekim slučajevima i sustav čekanja poruka).

Mame dolaze u svim oblicima i veličinama

Sve MOM-e dijele dvije temeljne karakteristike: omogućuju prosljeđivanje poruka, a prosljeđivanje poruka ne blokira. Osim ovih osnova, dobavljači mogu implementirati bilo koji broj različitih sučelja i usluga.

Mnoge MOM-ove pružaju sučelje za objavljivanje i pretplatu kako bi omogućili aplikacijama da objavljuju i primaju poruke koje ih zanimaju. Ovo sučelje može biti u obliku sustava temeljenog na kanalima ili jednostavnijeg sustava u kojem klijent registrira vrste poruka zainteresiran je za primanje.

Osnovne MOM-ove pružaju samo izravne poruke, bez dodatnih usluga. Napredne MOM-ove pružaju čekanje poruka i zajamčenu isporuku, zajedno sa sigurnošću, razmještanjem podataka na više platformi, skalabilnošću i drugim prednostima.

Mame na prvi pogled

Evo kratke reference koja će vam pomoći da se snađete u tome što su MOM-e.

MOM prednosti

  • Jednostavno : klijenti objavljuju i pretplaćuju se

    Objavi i pretplati se korisna je apstrakcija na visokoj razini za ono što aplikacije trebaju učiniti za komunikaciju.

  • Jednostavno : nije potrebno složeno postavljanje

    MOM-ove je jednostavno instalirati i koristiti, za razliku od složenih RPC-utemeljenih sustava poput CORBA.

  • Općenito : isti MOM može se koristiti za više aplikacija

    Budući da je bilo koji MOM sustav u osnovi samo generički prijevoz poruka, može se ponovno upotrijebiti u različitim aplikacijama bez ikakvog dodatnog rada.

  • Fleksibilno : može se proslijediti svaka i svaka vrsta poruke

    MOM može proslijediti bilo koju poruku. Budući da MAMA poruke ne razumije, nije važno kakve su.

MAMA nedostaci

  • Općenito : Aplikacije moraju razumjeti poruke

    Navođenje aplikacija da koriste poruke umjesto poziva metode može biti nezgodno, pogotovo ako se aplikacija oslanja na činjenicu da pozivi metode blokiraju.

  • Nepoznato : Ne modelira pozive metode

    Programeri koji nisu upoznati s porukama mogu imati problema s odgovorom kako ih učinkovito koristiti.

  • Asinkrono : poruke ne blokiraju

    Poruke prirodno ne blokiraju. To otežava pisanje aplikacija kojima treba blokirati pozive.

  • Prejednostavno : nema razvrstavanja podataka

    Čak i jednostavni RPC sustavi ispravno raspoređuju podatke. Jednostavne MOM-e mogu samo slati poruke u kojima bajtovi nisu u redu s gledišta prijemnika.

  • Nestandardno : dobavljači su posvuda

    Implementacije dobavljača MOM rade sve ... i ništa.

    Caveat Emptor

    fraza je koju morate imati na umu prilikom pregledavanja različitih ponuda dobavljača.

Kada su MOM-e prikladne?

  • Prilikom komuniciranja aplikacije trebaju koristiti poruke
  • Kada osoblje za programiranje nije povezano s klijentom / poslužiteljem i RPC sustavima
  • Kad su CORBA / RMI i srodni sustavi previše složeni
  • Kad su jednostavni RPC sustavi previše osnovni

Razmatranja dizajna za našu MOM

Sad kad se pozadina ne stavlja s puta, krenimo slagati našu MOM, sabirnicu poruka . Pomoću MOM-a omogućit ćemo komunikaciju između klijenata distribuirane ploče. (Pogledajte Resurse za poveznice na informacije o aplikaciji bijele ploče s kojom smo radili u proteklih nekoliko rata.)

Potaknuće razmatranja sabirnice poruka je da pruža prikladno komunikacijsko sučelje visoke razine objektima aplikacije koji će ga koristiti.

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

Ova metoda klase vraća instancu kanala koja odgovara nazivu kanala. Stvara kanal i poziva MessageBusda ga doda u sustav ako već ne postoji. Čim se kanal stvori, na njemu se registrira njegov početni slušatelj.

// pozvani od strane klijenata da registriraju ChannelsUpdateListener javnu statičku void pretplatuChannelsUpdates (ChannelsUpdateListener cul) {channelsUpdateListeners.addElement (cul); }

// pozvani od strane klijenata da ponište registraciju ChannelsUpdateListener public static void unsubscribeChannelsUpdates (ChannelsUpdateListener cul) {channelsUpdateListeners.removeElement (cul); }