军中绿花歌词伴奏:Enterprise Integration Patterns - JMS Publish/Subscribe Example

来源:百度文库 编辑:中财网 时间:2024/05/06 11:35:16

JMS Publish/Subscribe Example

Pattern Catalog

 Previous   Next 

Site Home • Patterns Home • Table of Contents

This is a simple example that shows the power ofpublish/subscribe messaging, and explores the alternative designsavailable. It shows how multiple subscriber applications can all beinformed of a single event by publishing the event just once, andconsiders alternative stratagies for how to communicate details of thatevent to the subscribers.


Publish/Subscribe using a JMS Topic

To understand how helpful a simple Publish-Subscribe Channelreally is, we first need to consider what it is like to implement theObserver pattern in a distributed fashion, amongst multipleapplications. Before we get to that, let's review the basics ofObserver.

The Observer Pattern

The Observer pattern [GoF]documents a design through which an object can notify its dependents ofa change, while keeping that object decoupled from its dependents sothat the object works just fine no matter how many dependents it has,even none at all. Its participants are a Subject—the object announcingchanges in its state—and Observers—objects interested in receivingnotification of changes in the Subject. When a subject's state changes,it sends itself Notify(), whose implementation knows the list of observers and sends Update()to each of them. Some observers may not be interested in this statechange, but those who are can find out what the new state is by sendingGetState() to the subject. The subject must also implement Attach(Observer) and Detach(Observer) methods that the observers use to register interest.

Observer provides two ways to get the new state from the subject to the observer: the push model and the pull model. With the push model, the Update call to each observer contains the new state as a parameter. Thus interested observers can avoid having to call GetState(), but effort is wasted passing data to uninterested observers. The opposite approach is the pull model,where the subject sends basic notification and each observer requeststhe new state from the subject. Thus each observer can request theexact details it wants, even none at all, but the subject often has toserve multiple requests for the same data. The push model requires asingle, one-way communication—the Subject pushes the data to anObserver as part of the update. The pull model requires three one-waycommunications—the Subject notifies an Observer, the Observer requeststhe current state from the Subject, and the Subject sends the currentstate to the Observer. As we'll see, the number of one-waycommunications affects both the design-time complexity and the runtimeperformance of the notification.

The easiest way to implement a Subject's Notify()method is with a single thread, but that can have undesirableperformance implications. A single thread will update each observerone-at-a-time, in sequence; so those at the end of a long list ofobservers may need to wait a long time for updates. And a subjectspending a long time updating all of its observers isn't accomplishinganything else. Even worse, an observer may well use its update threadto recact to the update, querying the subject for state and processingthe new data; such observer work in the update thread makes the updateprocess take even longer.

Thus the more sophisticated way to implement a Subject's Notify() method is to run each Update()call in its own thread. Then all observers can be updated concurrently,and whatever work each may do in its update thread does not delay theother observers or the subject. The downside is that implementingmultithreading and handling thread-management issues is more complex.

Distributed Observer

The Observer pattern tends to assume that theSubject and its Observers all run in the same application. Thepattern's design supports distribution, where the Observers run in aseperate memory space from the Subject and perhaps from each other, butthe distribution takes work. The Update() and GetState() methods, as well as the Attach and Detach methods, must be made remotely accessible (see Remote Procedure Invocation).Because the Subject must be able to call each Observer and vise versa,each object must be running in some type of ORB environment that allowsthe objects it contains to be invoked remotely. And because the updatedetails and state data will be passed between memory spaces, theapplications must be able to serialize (e.g., marshall) the objectsthey are passing.

Thus implementing Observer in a distributedenvironment can get rather complex. Not only is a multi-threadedObserver somewhat difficult to implement, but then making methodsremotely accessible—and invoking them remotely—adds more difficulty.The can be a lot of work just to notify some dependents of statechanges.

Another problem is that a Remote Procedure Invocationonly works when the source of the call, the target, and the networkconnecting them are all working properly. If a Subject announces achange and a remote Observer is not ready to process the notificationor is disconnected from the network, the Observer looses thenotification. While the Observer may work fine without the notificationin some cases, in other cases the lost notification may cause theObserver to become out of sync with the Subject—the whole problem theObserver pattern is designed to prevent.

Distribution also favors the push model over thepull model. As discussed earlier, push requires a single one-waycommunication whereas pull requires three. When the distribution isimplemented via RPC's (remote procedure calls), push requires one call (Update()) whereas pull requires at least two calls (Update() and GetState()).RPC's have more overhead than non-distributed method invocations, sothe extra calls required by the push approach can quickly hurtperformance.

Publish-Subscribe

A Publish-Subscribe Channelimplements the Observer pattern, making the pattern much easier to useamongst distributed applications. The pattern is implemented in threesteps:

  1. The messaging system administrator creates a Publish-Subscribe Channel. (This will be represented in Java applications as a JMS Topic.)
  2. The application acting as the Subject creates a TopicPublisher (a type of MessageProducer) to send messages on the channel.
  3. Each of the applications acting as an Observer (e.g., a dependent) creates a TopicSubscriber (a type of MessageConsumer) to receive messages on the channel. (This is analogous to calling the Attach(Observer) method in the Observer pattern.)

This establishes a connection between the subjectand the observers through the channel. Now, whenever the subject has achange to announce, it does so by sending a message. The channel willensure that each of the observers receives a copy of this message.

Here is a simple example of the code needed to announce the change:

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.NamingException;

public class SubjectGateway {

public static final String UPDATE_TOPIC_NAME = "jms/Update";
private Connection connection;
private Session session;
private MessageProducer updateProducer;

protected SubjectGateway() {
super();
}

public static SubjectGateway newGateway() throws JMSException, NamingException {
SubjectGateway gateway = new SubjectGateway();
gateway.initialize();
return gateway;
}

protected void initialize() throws JMSException, NamingException {
ConnectionFactory connectionFactory = JndiUtil.getQueueConnectionFactory();
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination updateTopic = JndiUtil.getDestination(UPDATE_TOPIC_NAME);
updateProducer = session.createProducer(updateTopic);

connection.start();
}

public void notify(String state) throws JMSException {
TextMessage message = session.createTextMessage(state);
updateProducer.send(message);
}

public void release() throws JMSException {
if (connection != null) {
connection.stop();
connection.close();
}
}
}

SubjectGateway is a Messaging Gatewaybetween the Subject (not shown) and the messaging system. The subjectcreates the gateway and then uses it to broadcast notifications.Essentially, the subject's Notify() method is implemented to call SubjectGateway.notify(String). The gateway then announces the change by sending a message on the update channel.

And here is an example of the code needed to receive the change notification:

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.NamingException;

public class ObserverGateway implements MessageListener {

public static final String UPDATE_TOPIC_NAME = "jms/Update";
private Observer observer;
private Connection connection;
private MessageConsumer updateConsumer;

protected ObserverGateway() {
super();
}

public static ObserverGateway newGateway(Observer observer)
throws JMSException, NamingException {
ObserverGateway gateway = new ObserverGateway();
gateway.initialize(observer);
return gateway;
}

protected void initialize(Observer observer) throws JMSException, NamingException {
this.observer = observer;

ConnectionFactory connectionFactory = JndiUtil.getQueueConnectionFactory();
connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination updateTopic = JndiUtil.getDestination(UPDATE_TOPIC_NAME);
updateConsumer = session.createConsumer(updateTopic);
updateConsumer.setMessageListener(this);
}

public void onMessage(Message message) {
try {
TextMessage textMsg = (TextMessage) message; // assume cast always works
String newState = textMsg.getText();
update(newState);
} catch (JMSException e) {
e.printStackTrace();
}
}

public void attach() throws JMSException {
connection.start();
}

public void detach() throws JMSException {
if (connection != null) {
connection.stop();
connection.close();
}
}

private void update(String newState) throws JMSException {
observer.update(newState);
}
}

ObserverGateway is another Messaging Gateway, this time between the Observer (not shown) and the messaging system. The observer creates the gateway, then uses attach() to start the Connection (which is analogous to calling the Attach(Observer) method in the Observer pattern). The gateway is an Event-Driven Consumer, so it implements the MessageListener interface, which requires the onMessage method. In this way, when an update is received, the gateway processes the message to get the new state, and calls is own update(String) method which calls the corresponding message in the observer.

These two classes implement the push model version of Observer. With the notification message sent by SubjectGateway.notify(String),the existance of the message tells the Observer that a change hasoccurred, but it is the contents of the message that tell the Observerwhat the Subject's new state is. The new state is being pushed from theSubject to the Observer. As we'll see later, there's another way toimplement all this using the pull model.

Comparisons

For distributed notification between applications,the Publish-Subscribe (e.g., messaging) approach has several advantagesover the traditional, synchronous (e.g., RPC) approach of implementingObserver:

  • Simplifies Notification — The Subject's implementation of Notify() becomes incredibly simple; the code just has to send a message on a channel. Likewise, Observer.Update() just has to receive a message.
  • Simplifies Attach/Detach — Rather than attach to and detach from the Subject, an Observer needs to subscribe to and unsubscribe from the channel. The Subject does not need to implement Attach(Observer) or Detach(Observer) (although the Observer may implement these methods to encapsulate the subscribe and unsubscribe behavior).
  • Simplifies Concurrent Threading — The Subject only needs one thread to update all Observers concurrently—the channel delivers the notification message to the Observers concurrently—and each Observer handles the update in its own thread. This simplifies the Subject's implementation, and because each Observer uses its own thread, what one does in its update thread does not affect the others.
  • Simplifies Remote Access — Niether the Subject nor the Observers have to implement any remote methods, nor do they need to run in an ORB. They just need to access the messaging system and it handles the distribution.
  • Increases Reliability — Because the channel uses messaging, notifications will be queued until the Observer can process them, which also enables the Observer to throttle the notifications. If an Observer wants to receive notifications that are sent while that Observer is disconnected, it should make itself a Durable Subscriber.

One issue that the Publish-Subscribe approach does not change is serialization.Whether Observer is implemented through RPC or messaging, state data isbeing distributed from the Subject's memory space to each Observer'smemory space, so the data has to be serialized (e.g., marshalled). Thisbehavior has to be implemented for either approach.

If the Publish-Subscribe approach has a downside, it's that the approach requires messaging,which means that the Subject and Observer applications must have accessto a shared messaging system and must be implemented as clients of thatmessaging system. Still, making applications into messaging clients isno more difficult, and probably easier, than using the RPC approach.

Push and Pull Models

Another potential downside of the Publish-Subscribeapproach is that the pull model is more complex than the push model. Asdiscussed earlier, the pull model requires more discussion back andforth than the push model. When the discussion is amongst distributedapplications, the extra communication can significantly hurtperformance.

The communication is more complex with messaging than with RPC. In both cases, Update() is a one-way communication, either an RPC that returns void or a single Event Message from the Subject to the Observer. The tricker part is when an Observer needs to query the Subject's state; GetState() is a two-way communication, either a single RPC that requests the state and returns it, or a Request-Reply—a pair of messages where a Command Message requests the state and a separate Document Message returns it.

What makes Request-Replymore difficult is not just that it requires a pair of messages, butthat it requires a pair of channels to transmit those messages. Onechannel, the get-state-request channel, goes from an Observer to theSubject; an Observer sends the state request on that channel. The otherchannel, the get-state-reply channel, goes from the Subject back to theObserver; the Subject sends the state reply on that channel. All of theObservers can share the same request channel, but they will probablyeach need their own reply channel. Each Observer needs to receive notjust any response, but the particular response for its specificrequest, and the easiest way to ensure this is for each Observer tohave its own reply channel. (An alternative is to use a single replychannel and use Correlation Identifiers to figure out which reply goes to which observer, but a separate channel per observer is a lot easier to implement.)


Publish/Subscribe using the Pull Model

A reply channel for each Observer can lead to anexplosion of channels. Such a large number of channels may bemanagable, but the messaging system administrator does not know howmany static channels to create when the number of Observers needing touse these channels changes dynamicly at runtime. Even if there areenough channels for all of the Observers, how does each Observer knowwhich channel to use?

JMS has a feature, TemporaryQueue, specifically for this purpose. [Hapner, p.60] (Also see the discussion in Request-Reply.) An Observer can create a temporary queue, just for its own use, specify that as the Return Addressin its request, and wait for the reply on that queue. Creating newqueues frequently can be inefficient, depending on your messagingsystem's implementation, and temporary queues cannot be persistent (foruse with Guaranteed Delivery). However, if you don't want to use the push model, you can implement the pull model using temporary queues.

These two classes show how to implement the gateways using the pull model.

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.NamingException;

public class PullSubjectGateway {

public static final String UPDATE_TOPIC_NAME = "jms/Update";
private PullSubject subject;
private Connection connection;
private Session session;
private MessageProducer updateProducer;

protected PullSubjectGateway() {
super();
}

public static PullSubjectGateway newGateway(PullSubject subject)
throws JMSException, NamingException {
PullSubjectGateway gateway = new PullSubjectGateway();
gateway.initialize(subject);
return gateway;
}

protected void initialize(PullSubject subject) throws JMSException, NamingException {
this.subject = subject;

ConnectionFactory connectionFactory = JndiUtil.getQueueConnectionFactory();
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination updateTopic = JndiUtil.getDestination(UPDATE_TOPIC_NAME);
updateProducer = session.createProducer(updateTopic);

new Thread(new GetStateReplier()).start();

connection.start();
}

public void notifyNoState() throws JMSException {
TextMessage message = session.createTextMessage();
updateProducer.send(message);
}

public void release() throws JMSException {
if (connection != null) {
connection.stop();
connection.close();
}
}

private class GetStateReplier implements Runnable, MessageListener {

public static final String GET_STATE_QUEUE_NAME = "jms/GetState";
private Session session;
private MessageConsumer requestConsumer;

public void run() {
try {
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination getStateQueue = JndiUtil.getDestination(GET_STATE_QUEUE_NAME);
requestConsumer = session.createConsumer(getStateQueue);
requestConsumer.setMessageListener(this);
} catch (Exception e) {
e.printStackTrace();
}
}

public void onMessage(Message message) {
try {
Destination replyQueue = message.getJMSReplyTo();
MessageProducer replyProducer = session.createProducer(replyQueue);

Message replyMessage = session.createTextMessage(subject.getState());
replyProducer.send(replyMessage);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}

PullSubjectGateway is very similar to SubjectGateway.The pull version now has a reference to its subject so that the gatewaycan query the subject for its state when requested by an observer. notify(String) has now become notifyNoState(),because the pull model simply sends out notification without includingany state (and because Java already uses the method name notify()).

The big addition for the pull model is GetStateReplier, an inner class that implements Runnable so that it can run in its own thread. It is also a MessageListener, which makes it an Event-Driven Consumer. Its onMessage method reads requests from the GetStatequeue and sends replies containing the subject's state to the queuespecified by the request. In this way, when an observer makes a GetState() request, the gateway sends a reply (see Request-Reply).

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueRequestor;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.NamingException;

public class PullObserverGateway implements MessageListener {

public static final String UPDATE_TOPIC_NAME = "jms/Update";
public static final String GET_STATE_QUEUE_NAME = "jms/GetState";
private PullObserver observer;
private QueueConnection connection;
private QueueSession session;
private MessageConsumer updateConsumer;
private QueueRequestor getStateRequestor;

protected PullObserverGateway() {
super();
}

public static PullObserverGateway newGateway(PullObserver observer)
throws JMSException, NamingException {
PullObserverGateway gateway = new PullObserverGateway();
gateway.initialize(observer);
return gateway;
}

protected void initialize(PullObserver observer) throws JMSException, NamingException {
this.observer = observer;

QueueConnectionFactory connectionFactory = JndiUtil.getQueueConnectionFactory();
connection = connectionFactory.createQueueConnection();
session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Destination updateTopic = JndiUtil.getDestination(UPDATE_TOPIC_NAME);
updateConsumer = session.createConsumer(updateTopic);
updateConsumer.setMessageListener(this);

Queue getStateQueue = (Queue) JndiUtil.getDestination(GET_STATE_QUEUE_NAME);
getStateRequestor = new QueueRequestor(session, getStateQueue);

}

public void onMessage(Message message) {
try {
// message's contents are empty
updateNoState();
} catch (JMSException e) {
e.printStackTrace();
}
}

public void attach() throws JMSException {
connection.start();
}

public void detach() throws JMSException {
if (connection != null) {
connection.stop();
connection.close();
}
}

private void updateNoState() throws JMSException {
TextMessage getStateRequestMessage = session.createTextMessage();
Message getStateReplyMessage = getStateRequestor.request(getStateRequestMessage);
TextMessage textMsg = (TextMessage) getStateReplyMessage; // assume cast always works
String newState = textMsg.getText();
observer.update(newState);
}
}

Again, PullObserverGateway is similar to ObserverGateway, but with some more code to implement the pull model. In initialize, it not only sets up updateConsumer to listen for updates, but also sets up getStateRequestor to send GetState() requests. (getStateRequestor is a QueueRequestor; see Request-Reply.) In the pull version, the gateway's onMessagecode ignores the message's contents because the message is empty. Themessage's existance tells the observer that the subject has changed,but does not tell the observer what the subject's new state is. So allthere is to do is call updateNoState() (named similarly to notifyNoState()).

The difference for the Observer between the push and pull models becomes apparent in the implementation of updateNoState() vs. update(String).Whereas the push version gets the new state as a parameter and just hasto update the observer, the pull version must go get the new statebefore it can update the observer. To get the new state, it uses the getStateRequestorto send a request and get the reply. The reply contains the subject'snew state, which the gateway uses to update the observer. (Note that inthis simple implementation, the gateway is single-threaded, so while itis sending the get-state request and waiting for the reply, it is notprocessing any more updates. Thus if the request or reply messages takea really long time to transmit, the gateway will be stuck waiting andany more updates that occur will simply queue up.)

So as you can see, the pull model is more complexthan the push model. It requires more channels (including a temporaryone for every observer), it requires more messages (three messages perupdate per interested observer instead of one message for allobservers), the subject and observer classes require more code tomanage the additional messaging, and the objects at runtime requiremore threads to execute the additional messaging. If all of this isacceptable in your application, then the pull model is a viableapproach. However, if in doubt, you should probably start with the pushmodel because it is simpler.

Channel Design

So far, we've considered one Subject with one piece of state notifying its Observers. Using the push model, this requires one Publish-Subscribe Channel for communicating changes in the Subject's state to the Observers.

Real enterprise applications are much more complex.An application can contain lots of Subjects that need to announcechanges. Each Subject often contains several different pieces of state,called aspects, that can change independently. A singleObserver might be interested in several different aspects in severaldifferent Subjects, where the Subjects are not only multiple instancesof the same class, but may well be instances of different classes.

So update semantics in sophsticated applicationscan quickly become complex. The Observer pattern address this asimplementation issues like "Observing more than one subject" and"Specifying modifications of interest explicitly." Also, the SASE(Self-Addresses Stamped Envelope) pattern describes a combination ofthe Observer and Command patterns whereby an observer specifies thecommand a subject should send it when a certain change occurs. [Alpert, pp.312-314]

Without getting too deep into the issues of makingsure observers only receive the updates they need, let's consider theimplications for messaging, namely: How many channels will we need?

Let's first consider a simple case. An enterprisemay have several different applications responsible for storing acustomer's contact information, such as a mailing address. When acustomer's address is updated in one of these applications, theapplication should notify other applications that may need this newinformation as well. Meanwhile, there may be several applications thatneed to know when an address changes, so they would like to register toreceive notification.

This is a simple problem to solve. All that is needed is a single Publish-Subscribe Channelfor announcing address changes. Each application that can change anaddress then also has the responsibility to announce that change bypublishing a message on the channel. Each application that wishes toreceive notification subscribes to the channel. A particular changemessage might look like this:



123 Wall Street
New York
NY
10005


321 Sunset Blvd
Los Angeles
CA
90012

Now let's consider another problem. The enterprisemay also have applications that need to announce when they are out of aproduct, and others that need to receive these notifications so thatthey can reorder the product. This is just a different example of thelast problem, and is solved the same way by using a Publish-Subscribe Channel to make out-of-product announcements. One of these messages might look like this:


12345
67890
100

But this leads us to wonder: Can we use the samechannel for customer address changes and for out-of-productannouncements? Probably not. First, Datatype Channeltells us that all of the messages on a channel must be of the sametype, which in this case means that they must all conform to the sameXML schema. is obviously a very different element type from ,so they should not be sent on the same channel. Perhaps the dataformats could be reworked so that both message types fit the sameschema and receivers could tell which messages were for addresses andwhich were for products. But then the problem is that the applicationsinterested in address changes are probablty not the same onesinterested in product updates, so if the messages use the same channel,an application will frequently receive notifications it's notinterested in. Thus is makes sense to have two separate address changeand product update channels.

Now consider a third case where a customer's credit rating could change. The message might look like this:


AAA
BBB

Like the case with product notifications, it mightbe tempting to solve the problem with a new credit-rating-changedchannel (in addition to the address-changed and out-of-productchannels). This would keep the credit rating changes separate from theaddress changes, and would allow dependents to only register for thetype of changes they're interested in.

The problem with this approach is that it can leadto a channel explosion. Consider all the pieces of data that may beknown about a customer: name; contacts (address, phone number, e-mail)for mailing, shipping, and billing; credit rating; service level;standard discount; etc. Each time any one of these aspects changes,other applications may need to know about it. Creating a channel foreach can lead to lots of channels.

Large numbers of channels may tax the messagingsystem. Numerous channels with little traffic on each can wasteresources and make load difficult to distribute. Or, numerous channelswith lots of little messages can add to messaging overhead. Dependentscan become confused as to which of a large number of channels tosubscribe to. Multiple channels require multiple senders and receivers,perhaps leading to lots of threads checking lots of channels that areusually empty. So creating yet more channels may not be such a goodidea.

What may work better is to send both theaddress-changed and credit-rating-changed messages on the same channel,since they both concern changes to the customer and an applicationinterested in one kind of change may be interested in the others aswell. Yet a separate out-of-product channel is still a good idea sinceapplications interested in customers may not be interested in productsand vise versa.

The address-changed and credit-rating-changed messages have different formats, yet Datatype Channeltells us that to be on the same channel, the messages must have thesame format. With XML, this means that all of the messages must havethe same root element type, but perhaps can have different optionalnested elements. So unified customer-changed messages might look likethis:




123 Wall Street
New York
NY
10005


321 Sunset Blvd
Los Angeles
CA
90012




AAA
BBB

There may still be the problem that shippingapplications interested in address changes are not interested in creditrating changes, and billing applications are interested in theopposite. These applications can use Selective Consumersto get only the messages of interest. If selective consumers prove tobe complicated and a messaging system can easily support more channels,then perhaps separate channels would be better after all.

As with may issues in enterprise architecture and design, there are no simple answers and lots of tradeoffs. With Publish-Subscribe Channel(as with any message channel), the goal is to help ensure that theobservers only receive the notifications they need, without anexplosion of separate channels and without taxing the typical observerwith lots of threads running lots of consumers monitoring lots ofchannels.

Conclusions

This chapter shows that Publish-Subscribe Channelsare an implementation of the Observer pattern that make the patternmuch easier to use in distributed environments. When a channel is used,Subject.Notify() and Observer.Update()become much simpler because all they have to do is send and receivemessages. The messaging system takes care of distribution andconcurrency while making the remote notification more reliable. Thepush model is simpler and often more efficient than the pull model,especially for distributed notification, especially with messaging; yetthe pull model can also be implemented using messaging. In complexapplications where lots of data can change, it may be tempting tocreate a channel for every different thing that can change, but it'soften more practical to use the same channel to trasmit similarnotifications going to the same observers. Even if your applicationsdon't need messaging for anything else, if they need to notify eachother of changes, it may well be worth using Messaging just so you can take advantage of Publish-Subscribe Channels.


Home • Patterns • Table of Contents  Previous   Next 

© 2003 Gregor Hohpe • Bobby Woolf • All rights reserved.