Comet is not really brand-new (Ajax) term today; however, with most of people how it works remains somewhat mysterious.
I have a little hand-on experience with Comet when creating a hobby game project with DWR Reverse Ajax sometime ago. It (DWR) was simple to start and really worked but required a Java web server (Tomcat, Jetty…) that I found rather expensive (in term of resources) for such small application. I want a small, dedicated and reliable server for Comet apps while don’t like to be deeply sunk in technical terms like “Bayeux” or “Continuation”.
After reviewing the Comet Maturity Guide from Comet Daily I decided to give Meteor Comet server a try for several reasons, especially because it’s built on Perl that can be easily deployed in any server with Perl installed (i.e. almost all Linux servers, no?) and it should be lightweight (up to my experience with Perl). Here is other info about Meteor server:
- Server daemon will run on any platform for which Perl is available.
- In live use typically 1,000 clients per node receiving 2 msgs (~400 bytes) per sec each. Tested up to 5,000 clients per node receiving 1 msg/sec each.
- Nodes exist independently, supports broadcasting for message distribution. Cluster of three Meteor nodes runs FT Alphaville.
- Transports are completely configurable within simple constraints.
- Server supports client ‘catch-up’, allowing clients to regulate quality of service themselves.
- Stable, in production use.
- GPL v2. Free.
Install & Setup Meteor Server
You can find very good how-to guide for installing Meteor server from Meteorserver.org; actually it’s quite simple to follow. Just download, extract to default location (/usr/local/meteor) and configure daemon service as guided. One notice is on Fedora/Cent OS the start function in init scripts must be changed from:
sudo –u root ./meteord >/var/log/meteord 2>&1 &
to:
./meteord >/var/log/meteord 2>&1 &
After installed and run, the server will listen on 2 ports: 4670 and 4671 as default – the first port is “Subscriber port” and the second is “Controller port”. However, you typically want to serve your website on port 80 to avoid cross-site scripting restrictions, therefore, Meteor must also serve on the same port. Yes, the installation guide points out three possible ways to do that:
1. Set SubscriberPort to 80 and SubscriberIP to a specific IP, say X.X.X.1, in the Meteor config file. This will bind Meteor to a particular IP address on port 80 and leave Apache to bind to port 80 on a different IP. To make this work you will need to run Meteor as root, and configure Apache (or whatever web-server you’re using) to bind to port 80 on a different IP, say X.X.X.2.
2. Configure a firewall to redirect traffic on port 80 of one specific IP, to the Meteor Subscriber port (typically 4670). Then both Meteor and Apache can run in their default configurations, and Meteor can be run as a non-privileged user. So, if you’re using iptables you need the following commands:
1 2 3 iptables -t nat -I PREROUTING -p tcp --dport 80 --destination X.X.X.1 -j REDIRECT --to-port 4670 iptables -A INPUT -p tcp --dport 4670 -j ACCEPT /etc/init.d/iptables saveor if you’ve got a hardware firewall, set up an equivilent set of rules on that.
3. Run Meteor on a different physical server to the one running Apache. Then each server need only have one IP, and Apache can be run in its default configuration. Meteor must be set to use port 80 and be run as root.
Well, in brief, you’ll need either another dedicated (virtual) server or to obtain another IP. But if you don’t have another server and don’t want to buy extra IP just for testing, I can tell you the forth way to get Meteor server work in the same machine and the same IP. The answer is to configure “Reverse Proxy” to pass all requests from specific domain to Meteor. Fortunately, it’s very simple to do that with Apache’s mod proxy. Here are what I did.
1. Make sure Apache to load “mod proxy” by commenting out the following line in httpd.conf file.
1 2 3 | LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_connect_module modules/mod_proxy_connect.so LoadModule proxy_http_module modules/mod_proxy_http.so |
2. Add “reverse proxy” for the sub-domain (like data.yourdomain.com) that points to Meteor
1 2 3 4 5 6 7 8 9 10 | NameVirtualHost *:80 # other virtual hosts #... <VirtualHost *:80> ServerAdmin admin@localhost ServerName data.yourdomain.com ProxyPass / http://127.0.0.1:4670/ ProxyPassReverse / http://127.0.0.1:4670/ </VirtualHost> |
Restart Apache and you’ll see the magic work!
The Chat Application
I often start with a Comet solution by building an online chat so that I can test how fast and reliable the solution is across different browsers. Also, it’s interesting to see messages coming in real-time manner.
Of course, there is no rocket science here; it looks simple like the demo below.
Essentially, you’ll have to include Meteor’s scripts from sub-domain, data.javascriptly.com in this demo. This script file comes from Meteor server we configured as above and enclose all functions to pull messages in (near) real time.
<script type="text/javascript" src="http://data.javascriptly.com/meteor.js" charset="utf-8"></script>Then, setup Comet connection on page load:
1 2 3 4 5 6 7 8 9 10 11 | // Set this to something unique to this client Meteor.hostid = "1234567"; // Our Meteor server is on the data. subdomain Meteor.host = "data." + location.hostname; // Call the addMsg() function when data arrives Meteor.registerEventCallback("process", addMsg); // Join the chat channel and get last 10 events, then stream Meteor.joinChannel("chat", 10); Meteor.mode = 'stream'; // Start streaming! Meteor.connect(); |
Every time a message arrives, the addMsg() function will be executed to add it to chat console, simple enough!
Now, when you click “Send” button the message you’ve typed in will be sent to Apache/PHP server (not the Meteor server) using an Ajax POST.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function sendMsg(){ var msg = $('#message').val(); if(msg.length == 0) return; var user = $('#user').val(); if(user.length == 0) user = "Guest"; $.post('chat.php', { user: toCharRef(user), message: toCharRef(msg) }); $('#message').val(""); $('#message').focus(); } |
On receipt Apache/PHP will parse the message and write it to Meteor’s Controller which is listening on port 4671 like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ... $ch = "chat"; $op = false; // Open a controller channel to Meteor server echo "Connecting to Meteor\n"; if (!($op = fsockopen("127.0.0.1", 4671, $errno, $errstr, 5))) { echo "Meteor not responding\n"; } else{ socket_set_blocking($op,false); echo "Connected\n"; $haswritten = false; $buf = ""; $out = "ADDMESSAGE ".$ch." <strong>".$user." ".$time."</strong> ".$message."\n"; ... } |
Meteor server, then, delivers added message to all clients subscribing to “chat” channel. That’s all.
The app works seamlessly on IE6/7/8, Firefox 3, Safari 3.1 while on Opera 9.2 and Google Chrome Meteor uses “simple forever IFRAME” technique for streaming which makes hourglass pointer display all the time. Hope Andrew Betts and team will find some workaround to avoid this issue in next version?
Chat message supports Unicode (the feature many developers ignore) so you can chat in any language you like: German, French, Chinese, Japanese and English of course. Please feel free to leave your comments/questions here.
Related Reading:
- Professional Oracle WebLogic Server (Wrox Programmer to Programmer)
Authoritative guide to Oracle WebLogic Server-from Oracle insiders If you're an experienced J... Read More »
- Inside Microsoft Exchange Server 2007 Web Services
Dive deep into the architecture of Exchange Web Services--and master the intricacies for accessing... Read More »
- Oracle Application Server 10g Web Development (Osborne ORACLE Press Series)
Develop flexible, cross-platform Web applications using Oracle Application Server 10g and this exclu... Read More »







hi!
i tried reverse proxy, restarted apache and i didn’t see the magic work…
Sat Sep 12 15:49:07 2009 debug New Meteor::Subscriber for 127.0.0.1
Sat Sep 12 15:49:07 2009 debug Meteor::Document: Request received for ‘/’
Sat Sep 12 15:49:07 2009 info document / 0 404
Sat Sep 12 15:49:07 2009 debug Will close Meteor::Subscriber for 127.0.0.1 when write buffer empty
Sat Sep 12 15:49:07 2009 debug Closed Meteor::Subscriber for 127.0.0.1
and where i had my starting page there’s only this:
Service Temporarily Unavailable
The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.
——————————————————————————–
Apache/2.2.3 (CentOS) Server at 192.168.1.102 Port 80
any help would be appreciated.
thanks
Thanks for the response Andrew, Would you mind going into a little more detail as to how Meteor does this garbage collection or where I can find some more information about how it works? Thanks.
Jimmy – thanks for an excellent writeup. Regarding your ‘fourth way’ of co-locating Meteor and Apache on the same host with mod_proxy – yes, that does work, but it’s not recommended, because the very reason for using Meteor in the first place is that it’s optimised for handling lots of concurrent connections, whereas Apache is optimised for dealing with lots of *sequential* connections, closing them as quickly as possible (though it does also handle several at once, of course). So if you put Apache in front of Meteor, you lose much of the benefit of Meteor. At low volumes however, it will certainly work.
@Joe: The problem with IE7 is that it has an annoying habit of keeping connections open after the page has been destroyed – so you could have navigated away from the Comet-powered site, but there are still comet connections persisting in memory. You then rapidly encounter the two-connection limit. The solution to this is better garbage collection, which was implemented in Meteor thanks to a contributed solution several releases ago.
@Baderous: Meteor has no concept of authentication, but you could easily set up very obscure channel names, and there is no limit on the number of channels you can create, nor the number you can subscribe to at the same time.
Any idea on how to solve the IE refresh problem? Maybe with flush()?
@Baderous Meteor server does not support private channels (yet) but I think creating something random like UUID is OK.
Any idea how to implement private channels with this? Maybe creating random channel names that only the client and the server is aware of?
Nice article by the way. I came up with this exactly solution myself.
It seems the problem is being caused by IE’s strict connection limiting. Every website is only allowed two simultaneous to any web browser via an RFC standard. For some reason (IE sucks) IE isn’t closing these connections on page close, even if you manually call the ajax.abort() function they still aren’t closed. The one solution I have found is to have the client force the connection to reload. For example, if you put some code in the window.onload() function to post a message like “joe has entered the chat” solves this problem be forcing the connection to close and reopen. This isn’t a perfect solution but at least IE won’t hang anymore.
I have been looking into several different comet applications and so far, including this example, they work perfectly in IE7 until you refresh the page about 2-3 times. After then the page just infinitely loads and the chat doesn’t show up. If anyone has any ideas as to why this is happening in IE I’d love to hear it.