Tulis IBU Anda sendiri!

IBU disalahpahami, dan IBU tidak mendapat pujian. Anda mungkin pernah mendengar yang ini sebelumnya, tetapi di arena sistem terdistribusi itu sebenarnya benar! Ini karena middleware berorientasi pesan (MOM) secara tradisional tidak menikmati tingkat kecanggihan dan dukungan yang sama seperti teknologi lain yang digunakan dalam kerangka komunikasi terdistribusi.

Tapi waktu sedang berubah. Dengan diperkenalkannya penawaran vendor yang canggih dan kuat, minat pada sistem MOM berkembang pesat. Implementasi MOM yang baik menyediakan antarmuka aplikasi tingkat tinggi, jaminan kualitas layanan, dan sejumlah layanan seperti keamanan, antrian pesan, dan dukungan direktori yang diperlukan untuk komunikasi terdistribusi "kekuatan industri".

Kerangka komunikasi terdistribusi

Tujuan dari kerangka komunikasi terdistribusi adalah untuk menyediakan cara yang baik bagi bagian-bagian sistem terdistribusi untuk berkomunikasi. Kerangka kerja berorientasi objek menyelesaikan tugas ini dengan menyediakan objek terdistribusi dengan cara untuk saling mengirim pesan.

Kerangka kerja berorientasi objek terdistribusi yang mendapatkan perhatian paling banyak adalah yang memodelkan perpesanan sebagai panggilan metode. CORBA dan RMI adalah dua contoh bagus dari jenis kerangka kerja ini (lihat Sumberdaya). Sistem ini sering disebut sistem remote procedure call (RPC). Keajaiban dari sistem ini adalah mereka membuat panggilan prosedur (atau metode) jarak jauh tampak seperti panggilan prosedur lokal (LPC).

RPCs dirancang dengan pola klien / server. Misalnya, objek CORBA yang mengekspos metode untuk dipanggil oleh objek jarak jauh disebut (dan adalah) server.

Memperkenalkan MOM

Berbeda dengan RPC, MOM tidak memodelkan pesan sebagai panggilan metode; sebagai gantinya, mereka memodelkannya sebagai peristiwa dalam sistem pengiriman peristiwa. Klien mengirim dan menerima acara, atau "pesan," melalui API yang disediakan oleh MOM. MOM dapat menyajikan layanan direktori yang memungkinkan klien mencari aplikasi lain yang bertindak sebagai server, atau mungkin menyajikan "saluran" serba guna yang memungkinkan sekelompok klien berkomunikasi sebagai rekan, atau mungkin menampilkan kedua opsi.

Semua aplikasi berkomunikasi langsung satu sama lain menggunakan MOM. Pesan yang dihasilkan oleh aplikasi hanya berarti bagi klien lain karena MOM itu sendiri hanya merupakan router pesan (dan dalam beberapa kasus sistem antrian pesan juga).

MOM datang dalam berbagai bentuk dan ukuran

Semua MOM berbagi dua karakteristik dasar: mereka memungkinkan penyampaian pesan dan penyampaian pesan tidak memblokir. Di luar dasar-dasar ini, vendor dapat mengimplementasikan sejumlah antarmuka dan layanan yang berbeda.

Banyak MOM menyediakan antarmuka terbitkan dan langganan untuk memungkinkan aplikasi menerbitkan dan menerima pesan yang mereka minati. Antarmuka ini dapat berbentuk sistem berbasis saluran atau sistem yang lebih sederhana di mana klien mendaftarkan jenis pesan itu tertarik untuk menerima.

Basic MOMs hanya menyediakan pesan langsung, tidak ada layanan tambahan. MOM tingkat lanjut menyediakan antrian pesan dan pengiriman yang terjamin, bersama dengan keamanan, marshalling data lintas platform, skalabilitas, dan manfaat lainnya.

Sekilas tentang IBU

Berikut adalah referensi cepat untuk membantu Anda memahami apa itu MOM.

Keuntungan MOM

  • Sederhana : Klien menerbitkan dan berlangganan

    publish-and-subscribe adalah abstraksi tingkat tinggi yang berguna untuk apa yang perlu dilakukan aplikasi untuk berkomunikasi.

  • Mudah : Tidak perlu penyiapan yang rumit

    MOM mudah dipasang dan digunakan, tidak seperti sistem berbasis RPC yang kompleks seperti CORBA.

  • Generik : MOM yang sama dapat digunakan untuk beberapa aplikasi

    Karena sistem MOM tertentu pada dasarnya hanyalah pengangkutan pesan umum, ia dapat digunakan kembali dalam aplikasi yang berbeda tanpa pekerjaan tambahan apa pun.

  • Fleksibel : Setiap jenis pesan dapat diteruskan

    Pesan apa pun bisa dikirim oleh IBU. Karena IBU tidak memahami pesan-pesan itu, tidak peduli apa itu pesan.

Kerugian MOM

  • Generik : Aplikasi harus memahami pesan

    Membuat aplikasi menggunakan pesan alih-alih panggilan metode mungkin rumit, terutama jika aplikasi mengandalkan fakta bahwa panggilan metode diblokir.

  • Tidak dikenal : Tidak memodelkan panggilan metode

    Pengembang yang tidak terbiasa dengan pesan mungkin kesulitan mencari tahu cara menggunakannya secara efektif.

  • Asynchronous : Pesan tidak memblokir

    Pesan secara alami tidak memblokir. Ini membuat lebih sulit untuk menulis aplikasi yang membutuhkan pemblokiran panggilan.

  • Terlalu sederhana : Tidak ada data marshalling

    Bahkan sistem RPC sederhana menyusun data dengan benar. Simple MOMs mungkin hanya mengirim pesan di mana byte rusak dari sudut pandang penerima.

  • Non-standar : Vendor ada di mana-mana

    Penerapan Vendor MOM melakukan segalanya ... dan tidak ada apa-apa.

    Caveat Emptor

    is the phrase to keep in mind when reviewing the various vendor offerings.

When are MOMs appropriate?

  • When communicating apps need to use messages
  • When programming staff is not wedded to client/server and RPC systems
  • When CORBA/RMI and related systems are too complex
  • When simple RPC systems are too rudimentary

Design considerations for our MOM

Now that the background is out of the way, let's start putting together our MOM, the Message Bus. We'll be using the MOM to enable communication between distributed whiteboard clients. (See Resources for links to information on the whiteboard application we've been working with in the past few installments.)

The driving consideration for the Message Bus is that it provide a convenient high-level communications interface to the application objects that will use it.

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

Metode kelas ini mengembalikan contoh saluran yang sesuai dengan nama saluran. Ini membuat saluran dan panggilan MessageBusuntuk menambahkannya ke sistem jika belum ada. Segera setelah saluran dibuat, pendengar awalnya terdaftar dengannya.

// dipanggil oleh klien untuk mendaftarkan ChannelsUpdateListener public static void subscribeChannelsUpdates (ChannelsUpdateListener cul) {channelsUpdateListeners.addElement (cul); }

// dipanggil oleh klien untuk membatalkan pendaftaran ChannelsUpdateListener public static void unsubscribeChannelsUpdates (ChannelsUpdateListener cul) {channelsUpdateListeners.removeElement (cul); }