The cometd project is nearing a 1.0 release and thus we are make a bit of a push to improve the project documentation. As part of this effort, we have realized that there are many cool features and extensions to cometd that have been under-publicized. So this blog is an attempt to give a whirlwind tour of cometd features and extensions.
Clients and Servers
The cometd project provides many implementations of the Bayeux protocol. The javascript and java implementations are most advanced, but there are also perl and python implementations under development within the project. There are also other implementations of Bayeux available outside the cometd project for groovy, flex, .net and atmosphere.
Javascript Client
There is now a common cometd-javascript client implementation used as the basis of the cometd-dojo and cometd-jquery implementations (dojox in 1.3.1 still contains a dojo specific client, but this will eventually be replaced with the common code base). This common code base should able to be used to easily create implementation for other frameworks (eg ext.js or prototype).
For simplicity, our documentation get’s around the details of which javascript implementation you are using by assuming that you code is written in the context of a:
// Dojo stylevar cometd = dojox.cometd;
or
// jQuery stylevar cometd = $.cometd;
Java Server
The cometd-java server was written originally as part of the Jetty-6 servlet container and included support for asynchronous scaling. While still based on jetty utility components, the cometd-java server is now portable and will run on most servlet containers and will use the asychronous features of Jetty or any servlet 3.0 container.
Java Client
The cometd-java client is based on the Jetty asynchronous HTTP Client, thus it is an excellent basis for devloping scalable load generators for testing your cometd application. It can also be used in rich java UIs that wish to use cometd to communicate over the internet to a server behind firewalls and proxies.
Basic Operation
Publish/Subscribe Messaging
The core operation of cometd is as a publish/subscribe messaging framework. A message is published to a channel with a URI like name (eg. /chat/room/demo ) and cometd will arrange to deliver that message to all subscribers for that channel, either locally in the server, remote in the client or a client of a clustered server. The subscription may be for the channel itself (/chat/room/demo), a simple wildcard (/chat/room/*) or a deep wildcard (/chat/**).
Subscription in javascript needs to provide a callback function to handle the messages:
// Some initialization codevar subscription1 = cometd.addListener('/meta/connect', function() { ... });var subscription2 = cometd.subscribe('/foo/bar/', function() { ... }); // Some de-initialization codecometd.unsubscribe(subscription2);cometd.removeListener(subscription1);
Publishing a method in javascript is simply a matter of passing the channel name and the message itself:
cometd.publish('/mychannel', { wibble: { foo: 'bar' }, wobble: 2 });
Similar APIs for publish subscribe are available via a semi-standard cometd API and several java implementations are now using this.
Service Channels
With publish/subscribe, the basic feature set for a chat room is available. But non-trivial applications cannot be implemented with all communication broadcast on publicly accessible channels. Thus cometd has a convention that any channel in the /meta/** or /service/** name space is a non-broadcast service channel (meta channels are used by the protocol itself and service channels are available to the application). This means that a message published to a service channel will only be delivered to server side clients, listeners and extensions. A message to a service channel will never be remotely distributed to a remote client unless an application explicitly delivers or publishes a message to a particular client.
This allows a client to publish a message to a service channel and know that it will only be received by the server
Private Message Delivery
A cometd application often needs to deliver a message to a specific client. Thus as an alternative to publishing to a channel, a java server side application can deliver a message to a specific user:
Client client = bayux.getClient(someClientId);client.deliver(fromClient,"/some/channel",aMsg,msgId);
Note that a private message delivery still identifies a channel. This is not to broadcast on that channel, but to identify the message handler within the client. This channel may be a service channel, so the client will know it is a private message, or it can be an ordinary channel, in which case the client cannot tell if the message was published or delivered to it.
Such private deliveries are often used to tell a newly subscribed client the latest state message. For example, consider a client that has subscribed to /portfolio/stock/GOOG. That client needs to know the current price of the stock and should not have to wait until the price changes. Thus the portfolio application can detect the subscription server side and deliver a private message to the subscriber to tell them the latest price.
Lazy Messages
One of the key features of comet is delivering messages to clients from the server with low latency, but not all messages need low latency. Consider a system status message, sent to all users, telling them something non urgent (eg maintenance scheduled for later in the day). There is no need for that message to be sent to every single user on the system with minimal latency and there is a significant cost to try to do so. If you have 10,000 users, then waking up 10k long polls will take a few seconds of server capacity which might be better used for urgent application events.
Thus the cometd-java server has the concept of lazy messages. A message may be flagged as lazy by publishing it to a channel that is flagged as lazy (ChannelImpl#setLazy(boolean)) or by publishing to any channel with the ChannelImpl#publishLazy(…) method. [ Note these methods are not yet on the standard API, but should be before 1.0. Until they are, you must cast to ChannelImpl ].
A Lazy message will be queued for a client, but it will not wake up the long poll of that client. So a lazy message will only be delivered when another non-lazy message is sent to that client, or the long poll naturally times out (in 30 to 200 seconds). Thus low priority messages can be delivered with minimal additional load to the server.
Message Batching
Comet applications will often need to send several related messages in respond to the same action (for example subscribing to a chat room and sending a hello message). To maximize communication efficiency, it is desirable that these multiple messages are transported on the same HTTP message. Thus both the cometd client and server APIs support the concept of batching. Once a batch is started, messages for a client a queued but not delivered until such time as the batch is ended. Batches may be nested, so it is safe to start a batch and call some other code that may do it’s own batching.
On the javascript client side, batching can be achieved with code like:
cometd.startBatch();cometd.unsubscribe(myChatSubscription);cometd.publish("/chat/demo",{text:'Elvis has left the building', from:'Elvis'});cometd.endBatch();
On the java server side, batching can be achieved with code like:
public void handleMessage(Client from, Message message){ from.startBatch(); processMessageForAll(from,message); from.deliver(from,message.getChannel(),processResponseForOne(message),null); from.endBatch();}
This will send any message published for all users and the private reply to the client in a single HTTP response.
Listeners, Data Filters and Extensions
There are several different ways that application code can attach to the cometd clients and servers in order to receive events about cometd and to modify cometd behaviour:
- Listeners are available on both client and server implementations and can inform the application of cometd events such as handshake, connections lost, channel subscriptions as well as message delivery.
- DataFilters are available in the java server and can be mapped to channels so that they filter the data of any messages published to those channels. This allows a 3rd party to review an application and to apply validation and verification logic as an aspect rather than being baked in (which application developers never do). There are several utility data filters available.
- Extensions are available on both client and server implementations and allow inbound and outbound messages to be intercepted, validated, modified, injected and/or deleted. The utility extensions provided are detailed in the next section.
Security Policy
The SecurityPolicy API is available in the java server and is used to authorize handshakes, channel creation, channel subscription and publishing. If an SecurityPolicy implementation is constructed with a reference to the Bayeux instance, then it can call the getCurrentRequest() method to access the current HttpServletRequest and thus use standard web authentication and/or HttpSessions when authorizing actions.
Extensions
Timestamp Extension
The timestamp extension simply adds the optional timestamp field to every message sent.
Timesync Extension
The timesync extension implements a NTP-like time synchronization. Thus a client can be aware of the offset between it’s local clock and the servers clock. This allows an application (eg an Auction site) to send a single message with the semantic: “the auction closes at 18:45 EST” and then each client can use it’s own local clock to count down the auction, without the need for wasteful tick messages from the server.
Acknowledged Message Extension
The Cometd/Bayeux protocol is carried over TCP/IP, so it is intrinsically reliable and messages will not get corrupted. However, with cometd, there are some edge cases where messages might get lost (dropped connections) or might arrive out of order (using multiple connections).
The acknowledge extension piggybacks message acknowlegements onto the long polling transports of cometd, so that messages are not lost or delivered out of order.
Reload Extension
The client side only reload extension is provided to allow a comet enabled web page to be reloaded with out needing to rehandshake. The existing client ID can be passed from page to page, so that an Comet/Ajax style of user interface can be merged with a more traditional page based approach.
Clustering
Cometd servers may be aggregated into a cluster using Oort, which is a collection of extensions that use the java cometd client to link the servers together. Currently Oort is under documented, so is best understood by reading this summary and then looking at the Auction example.
Observing Comets
The Oort class allows cometd servers to observe each other, which means to open bayeux connections in both directions. Observations are setup with the Oort#observerComet method and can be used to setup arbitrary topologies of servers (although fully connected is the norm and is implemented by the Oort cloud).
Once observed, Oort Comets may cluster particular channels by calling Oort#observerChannel,which will cause the local server to subscribe to that channel on all observed comet servers. Any messages received on those subscriptions will be re-published to the local instance of that channel (with loop prevention). Thus messages published to an observed channel will be published on all observered comet servers.
The Oort Cloud
The Oort cloud is a self organizing cluster of Oort Comet servers that use the Oort Observed /oort/cloud channel to publish information about discovered Oort Comets. Once an Oort comet is told of another via the /oort/cloud channel, it will observe it and then publish its own list of known Oort comets. This allows a fully connected cluster of Oort comets to self organize with only one or two comet nodes known in common.
Seti
Once an Oort Cloud has been established, a load balancer will be needed to spread load over the cluster, so a user might be connected to any node in the cloud. Seti (as in the Search for Extra Terrestial Intelligence), is a mechanism that allows a private message to be sent to a particular user that may be located anywhere in the cloud. Sharding and location caching can be used to make this more efficient.
Examples
Chat
Chat is the hello world of web-2.0 and the introductory demo for cometd. The server side of the chat monitors the join messages to maintain and distribute a membership list for each room. A services channel is used to provide a private message service.
There is both a dojo chat client and jquery chat client provided.
Auction
The Auction demonstration provided shows the Oort and timesync extensions in use to provide a moderately complete example of a cometd application.
Note that this example uses cometd-dojo for the client, but the UI is implemented in a mushup of prototype, behaviour and other js libs. Volunteers desparately needed to make this all dojo or all jquery.
Archetypes
Assembling the components needed for a cometd web application can be a little complex as the server components need to be mixed with the javascript framework and the cometd client. To make this process easier, cometd now support cometd maven archetypes, that can build a blank cometd war project in a few lines.
So what are you waiting for! Go comet!
3 Comments
Mark Boas · 10/07/2009 at 13:17
Hi Greg,
Great news about the push towards documentation. I think if Cometd is to be adopted by the wider community, clear, comprehensive documentation and examples are a must.
It’s great to see this happening.
I have a quick question regarding the cometd libraries:
What is the difference between the cometd-java-client-1.0.beta10.jar / cometd-java-server-1.0.beta10.jar. (linked to from http://downloads.dojotoolkit.org/cometd/) and the cometd-client-6.1.19.jar / cometd-server-6.1.19.jar that come with Jetty 6.1.19 ?
If it’s just that they are more recent versions can I easily upgrade the latter for the former?
Keep up the good work!
Cheers
Mark
Greg Wilkins · 13/07/2009 at 09:47
The code from cometd.org and from jetty-6 are kept in sync, so they support the same feature sets and same APIs.
The only real difference is the version in jetty is written against the jetty-6 continuation API which will only work asynchronously on jetty-6.
The version from cometd.org is written against the jetty-7 continuations and will work asynchronously on jetty-6, jetty-7, jetty-8 and any servlet-3.0 container.
Anonymous · 23/07/2010 at 09:25
The sad part of dojo cometd is when multiple publishers send msgs simultaneously, then the the subscriber is receiving just one of them.
I want to now what I missed in dojox.
I feel the function parms of -dojox.cometd.subscribe is the culprit.
If they have designed it as –
(channel, script-in-quotes);
developers could have had an opportunity to write
setTimeout(myfunc, 0);
to get some kind of concurrency.
Regards
P G Patrudu
Visakhapatnam
India
Comments are closed.