PubSub and Topics
by Dylan SchiemannOctober 26th, 2007Comet servers and clients make frequent use of PubSub and topics. PubSub, an abbreviation for publish and subscribe, is an asynchronous messaging paradigm found in distributed event handling systems. Topics are useful in a number of cases. The most common use is when you have a number of different objects that all want to broadcast their changes to a number of other interested parties that are listening for changes.
Unlike traditional event registry systems where an event handler is explicitly registered to an event source, subscribers in a PubSub system only declare an interest in messages of a particular class, with no knowledge of the publisher of an event. This declaration of interest occurs through registering a handle to a topic. The simple example that I used in my introduction to PubSub explains this concept more clearly:
dojox.cometd.subscribe("/the/channel", object, "function");
dojox.cometd.publish("/the/channel", {object: "JSON encoded data"});
In both the publish and subscribe examples shown above, there is a topic identified by the string “/the/channel”. Implementations such as mod_pubsub treat topics as paths where “/the” would be a parent topic to “/the/channel”, whereas implementations such as the client-side Dojo Toolkit consider topics to be pure strings with no interpretation of the implied hierarchy.
Topics are not limited to Comet implementations. For example, with Dojo, client-side events can be routed with the use of topics. This is particularly useful in user interface development when a many-to-many event registration system is needed. Not surprisingly, the API is similar to the Cometd client:
dojo.subscribe(topic, context, method);
dojo.publish(topic, args);
or
dojo.subscribe("/the/channel", object, "function");
dojo.publish("/the/channel", {object: "JSON encoded data"});
The most significant difference between Dojo’s topic system and that found in Cometd is that Cometd’s publishes and subscribes over Bayeux to a Comet server. Also of note, Cometd and mod_pubsub treat topics as paths, implying parent and child relationships between topics. This is particularly useful in implementing permissions models. For example, a publisher to a parent topic might be granted rights to publish to a child topic, or a subscriber to a parent topic might elect to receive all messages to all child topics.
Topics are highly useful on the client- and server-side for event driven applications. So, how do you implement a topic system? Let’s start by taking a look at the Dojo source, first by looking inside dojo/_base/connect.js
First, we see an empty object that will store a collection of topics:
dojo._topics = {};
Then, we find an interface for subscribe:
dojo.subscribe = function(topic, context, method){
return [topic, dojo._listener.add(dojo._topics, topic,
dojo.hitch(context, method))];
}
dojo.hitch helps preserve scope. There’s also a call to the private method dojo._listener.add:
add: function(source, method, listener){
source = source || dojo.global;
var f = source[method];
if(!f || !f._listeners){
var d = dojo._listener.getDispatcher();
d.target = f;
d._listeners = [];
f = source[method] = d;
}
return f._listeners.push(listener);
}
The first block checks to see if there is already a registered instance of the method on the source topic—in other words, is there already a subscriber object? If not, a new listener object is instantiated:
getDispatcher: function(){
return function(){
var ap=Array.prototype, c=arguments.callee,
ls=c._listeners, t=c.target;
var r=t && t.apply(this, arguments);
for(var i in ls){
if(!(i in ap)){
ls[i].apply(this, arguments);
}
}
return r;
}
}
So now that we have something listening for messages on a topic, we can simply publish to a topic:
dojo.publish = function(topic, args){
var f = dojo._topics[topic];
if(f){
f.apply(this, args||[]);
}
}
And that’s it. What about in Python? If we take a look at cometd.py
def subscribe(self, request, message):
client = self.clients[message["clientId"]]
self._subscribe(client, message["subscription"])
resp = {
“timestamp”: getTimestamp(),
“channel”: “/meta/subscribe”,
“subscription”: message["channel"],
“successful”: True
}
client.connection.deliver(resp)
return { “success”: True }
Removing some basic validation, we see that there is a call to the private _subscribe method, and then a response is delivered for a successful subscription request.
def _subscribe(self, client, chan):
cparts = chan.split("/")[1:]
root = self.subscriptions
if not chan in root:
root[chan] = weakref.WeakValueDictionary()
root[chan][client.id] = client
We simply split up the channel by paths, and register a client to the desired channel. Then, when an event is to be routed to a subscriber:
def route(self, request, message):
root = self.subscriptions
if root.has_key(message["channel"]):
subs = root[message["channel"]]
for client in subs:
subs[client].connection.deliver(message)
return { “success”: True }
We simply deliver a message to the connection for each registered client listening to that channel.
Topics provide significant event-driven flexibility packed into very concise code fragments. For additional information, Wikipedia has introductions to PubSub, Event Driven Programming, and the Observer pattern.












October 29th, 2007 at 10:17 pm
[...] project is developing Bayuex, “a protocol for routing events between clients and servers in a publish subscribe [...]
November 21st, 2007 at 8:39 am
[...] about the meaning of the messages. One of the most common forms of event messaging is the publish/subscribe mechanism based on channels, in which agents can subscribe to channels and publish events to [...]
January 30th, 2008 at 7:30 am
[...] The full source for Dojo.Deferred is well-documented and explains in great detail how to get the most out of this powerful asynchronous programming pattern, making it very suitable for Comet and Ajax client development, as well as pubsub topic systems. [...]
February 19th, 2008 at 12:47 pm
[...] would not be the best solution here. What we can do is write two lines with Dojo’s pub/sub system, a reliable solution to JavaScript’s lack of any kind of delegation or watcher system. [...]
February 29th, 2008 at 3:03 pm
[...] URL changes to receive messages. This approach will be used by the OpenAjax hub 1.1 to facilitate pub/sub messaging across frames. Another library was just released that uses an approach which involves loading a [...]
March 31st, 2008 at 8:13 pm
[...] blogged about the relationship between REST and RPC. REST and RPC also have a relationship with the publish and subscribe architecture. Joe Walker recently discussed three major API styles for Comet: traditional, [...]
April 9th, 2008 at 12:06 am
[...] Pub/sub is a popular architecture for Comet development. Simple applications may only need to deal with a single pub/sub broker (also known as a hub, which can run on a client or a server) [...]
May 19th, 2008 at 11:02 pm
[...] strong coherency and interoperability with publish and subscribe architectures by equating the PubSub concept of a topic or channel with the REST concept of a resource locator. This allows HTTP [...]
May 19th, 2008 at 11:32 pm
[...] Bayeux is a publish/subscribe messaging system, and this has drawn some criticism that PubSub is not the most flexible paradigm for an Ajax/Comet transport. This article attempts to address [...]
October 9th, 2008 at 12:02 am
[...] Comet communication can be used to route messages through Persevere’s stored objects. The PubSub system provided by Bayeux is powerful paradigm for building Comet applications, but often [...]
November 18th, 2008 at 10:26 am
[...] of method call can serve as event for broadcast to clients. Persevere also supports Bayeux and its PubSub system. These Comet capabilities are exclusive to Persevere, and CouchDB has nothing that is [...]