Long lived HTTP connection

567 views
Skip to first unread message

Lau_of_DK

unread,
Aug 4, 2009, 4:29:18 AM8/4/09
to Compojure
Hi Group,

I've recently implemented a webchat based on some online Ajax/JSON
tutorial, which basically fires a javascript which calls itself every
2 seconds to poll the server new messages.

Obviously polling is a bad way to go and I'm interested in opening a
HTTP connection via which I can push new messages out to the clients.

How would I go about that using Compojure?


Best regards,
Lau

Daniel Renfer

unread,
Aug 4, 2009, 10:46:59 AM8/4/09
to comp...@googlegroups.com
Lau,

You might want to look into BOSH[1] for doing your communication. I'm
pretty sure there are some java libraries for it, but I am unable to
give you more than that as to how to do it in Compojure.

Good luck.

[1]: http://xmpp.org/extensions/xep-0124.html

Luke Renn

unread,
Aug 4, 2009, 1:41:49 PM8/4/09
to Compojure
On Aug 4, 10:46 am, Daniel Renfer <d...@kronkltd.net> wrote:
> You might want to look into BOSH[1] for doing your communication. I'm
> pretty sure there are some java libraries for it, but I am unable to
> give you more than that as to how to do it in Compojure.

Also read up on Comet[1] if you haven't already. The Grizzly[2]
project has a comet server iirc. Like Daniel's suggestion, none of
this will help you in irt Clojure/Compojure.

1: http://en.wikipedia.org/wiki/Comet_(programming)
2: https://grizzly.dev.java.net/

James Reeves

unread,
Aug 4, 2009, 2:23:51 PM8/4/09
to Compojure
There's several ways.

The easiest way is to use Object/wait:

(def message-mutex (Object.))

(defroutes messages
(GET "/messages"
(or (get-messages)
(do (.wait message-mutex 2000)
(get-messages))))
(POST "/messages"
(put-message params)
(.notifyAll message-mutex)))

The disadvantage with this approach is that it requires one thread to
be running for each client. Java can usually handle up to a few
thousand threads, so if you're not expecting any more than 1000
simultaneous users, you should be fine.

If you're expecting tens or hundreds of thousands of simultaneous
users, then you'll need something more sophisticated. Neither the
servlet 2.5 spec nor the Ring spec will support this, but some Java
servers, like Jetty, have custom extensions to manage this.

Jetty includes the ContinuationCometdServlet, which is an
implementation of the Cometd Bayeux protocol. There isn't enough space
in this post to go into Cometd in detail, but it's not too hard to
access the libraries via Clojure. Unfortunately, there isn't a lot of
documentation on it, so it can be tricky to work out.

- James

Lau

unread,
Aug 16, 2009, 10:38:32 AM8/16/09
to Compojure
Hi James,

I was hoping you'd chime in!

Your example raises 2 questions.

1) What is the purpose of the (or ..) statement? I would imagine that
once someone
accesses that route, get-messages will be called, and then the waiting
begins?

2) You speak of 1 thread per user, but where are these threads spawned/
handled?

Thanks,
Lau

Lance Carlson

unread,
Aug 16, 2009, 11:58:53 AM8/16/09
to comp...@googlegroups.com
I personally think the best way to implement this is to figure out the
client first. I would start with trying to create a swf (flash file)
that can connect to jabber. Expose the sending and receiving and
presence information to javascript. This will create a real time
experience for the user and is very scalable. If you approach it this
way, you can use any of the pre-existing jabber servers available.

Lau

unread,
Aug 16, 2009, 12:03:29 PM8/16/09
to Compojure
Hi Lance,

I appreciate you giving your input, but I'm using ajax/json/compojure
etc. Flash is the last technology I want to pick up and the discussion
is regarding long lived HTTP connections.

/Lau

On Aug 16, 5:58 pm, Lance Carlson <lancecarl...@gmail.com> wrote:
> I personally think the best way to implement this is to figure out the
> client first. I would start with trying to create a swf (flash file)
> that can connect to jabber. Expose the sending and receiving and
> presence information to javascript. This will create a real time
> experience for the user and is very scalable. If you approach it this
> way, you can use any of the pre-existing jabber servers available.
>

Lance Carlson

unread,
Aug 16, 2009, 12:08:10 PM8/16/09
to comp...@googlegroups.com
You can still use ajax/json for all of your other application
requirements, but why re-invent the wheel? There are a lot of pretty
solid jabber servers out that there already talk to flash. Create a
small 1x1 swf and is the communication bridge and you have a pretty
solid push system.

James Reeves

unread,
Aug 16, 2009, 12:15:10 PM8/16/09
to Compojure
On Aug 16, 3:38 pm, Lau <lau.jen...@bestinclass.dk> wrote:
> 1) What is the purpose of the (or ..) statement? I would imagine that
> once someone
> accesses that route, get-messages will be called, and then the waiting
> begins?

In my example, get-messages does not block, but returns nil if no
messages are waiting.

(GET "/messages"
(or (get-messages)
(do (.wait message-mutex 2000)
(get-messages))))

This logic says that if there are messages, return those messages
immediately to the user. If there are no messages, wait up to 2
seconds to be notified of messages. Notification occurs when another
client performs a POST:

(POST "/messages"
(put-message params)
(.notifyAll message-mutex))

In other words, this code acts as a "blocking poll". The requests and
responses might look something like this (assuming no lag between
server and client):

Time (ms) | Action
------------------------------------------
0 | <= GET messages
2000 | => 0 messages
2000 | <= GET messages
4000 | => 0 messages
4000 | <= GET messages
4500 | <= POST messages
4500 | => 1 message (returns early)

You may wish to increase the length of the poll to reduce the number
of queries per second. If you set the poll to 60 seconds, for example,
then the minimum number of queries per user would be one per minute.

> 2) You speak of 1 thread per user, but where are these threads spawned/
> handled?

They're spawned by the web server. The web server spawns a new thread
per request. If you block the thread handling the request, you can
have long-lived HTTP connections.

But the disadvantage of thread-blocking is that it uses up one thread
per client. This limits the number of clients you can have on a single
server at once to a few thousand.

- James

James Reeves

unread,
Aug 16, 2009, 12:28:18 PM8/16/09
to Compojure
On Aug 16, 5:08 pm, Lance Carlson <lancecarl...@gmail.com> wrote:
> You can still use ajax/json for all of your other application
> requirements, but why re-invent the wheel? There are a lot of pretty
> solid jabber servers out that there already talk to flash. Create a
> small 1x1 swf and is the communication bridge and you have a pretty
> solid push system.

The advantage of using long-lived HTTP connections is you don't have
to change your Javascript polling code. If Lau has already written an
ajax-based poller, it's only going to take a few lines of server code
to turn that into a real-time system. This will scale up to a few
thousand concurrent users for a single server.

If you need more performance, then yes, you could use Flash, or you
could use a dedicated Ajax push server like Orbitd or Ape. There are a
few around, although admittedly they're relatively young projects so
they lack documentation in places.

- James

Ben

unread,
Aug 18, 2009, 10:45:35 AM8/18/09
to Compojure
I've tried the chat-demo on the google code page here:
http://groups.google.com/group/compojure/files and it worked well
enough.
But I noticed that it uses a pre-1.0 clojure. If you go the cometd
route it might be worth looking at this, and maybe even updating it to
clojure 1.0 for the community.

-Ben

ngocdaothanh

unread,
Aug 26, 2009, 8:48:03 AM8/26/09
to Compojure
Hi,

Is there any plan for an adapter for Netty (http://jboss.org/netty/
performance.html) so that Compojure can power Comet connections? As
far as I know it is not a servlet web server like Jetty or Grizzly
thus writing one can be difficult.

Ngoc.


On Aug 18, 11:45 pm, Ben <ben.groot...@gmail.com> wrote:
> I've tried the chat-demo on the google code page here:http://groups.google.com/group/compojure/filesand it worked well

James Reeves

unread,
Aug 26, 2009, 8:57:55 PM8/26/09
to Compojure
On Aug 26, 1:48 pm, ngocdaothanh <ngocdaoth...@gmail.com> wrote:
> Is there any plan for an adapter for Netty (http://jboss.org/netty/
> performance.html) so that Compojure can power Comet connections? As
> far as I know it is not a servlet web server like Jetty or Grizzly
> thus writing one can be difficult.

HTTP push technologies are essentially separate protocols in their own
right. They may use HTTP as a carrier, but the way they interact with
clients is fundamentally different. If you were going to write a
Clojure implementation of a comet protocol, I think you'd structure
the API very differently to Compojure or Ring. I don't think there's
enough overlap between classical HTTP and comet protocols for it to
make sense to put them in one library.

- James

ngocdaothanh

unread,
Aug 27, 2009, 12:27:48 AM8/27/09
to Compojure
As I understand Comet and normal HTTP requests/responses are the same.
The only different is that for normal requests the app should respond
as quick as possible and for Comet requests the app can delay. Other
than that everything else is the same: cookie, session, content type
etc.

Do you mean that the app should listen for normal connections on a
port with Compojure support and Comet connections on a separate one
without Compojure support?

Or do you mean the API for Comet should not be part of Compojure? If
so how to handle an HTTP connection directly without using Compojure?

Thanks.

Shantanu Kumar

unread,
Aug 27, 2009, 12:53:10 AM8/27/09
to comp...@googlegroups.com
Comet is not part of the Servlet specification as of date (version 2.5) - the upcoming 3.0 specification is trying to fix that. Servlets so far have assumed short-lived request-response cycle for HTTP. Hence there are proprietary (in the API sense) implementations available, such as in Jetty and Resin.

The fundamental difference between a regular HTTP request-response cycle and Comet can be illustrated by the example below:

Regular Servlet: REQUEST<--->RESPONSE [Socket may or may not close, handled by web container]

Comet: <---REQUEST---->RESPONSE--->RESPONSE--->RESPONSE<---REQUEST--->RESPONSE--->RESPONSE
[Socket presumably remains in Keep-alive mode]

As you will notice, even though basic elements like cookie, session, content type etc are the same, the REQUEST-RESPONSE cycle is not predictably the same anymore. This requires the Comet action to behave like a server for each client, and cannot be retro-fitted onto a regular Servlet.

I am thinking about adding Comet support to Jettify (URL below), but no promise yet:


Regards,
Shantanu

ngocdaothanh

unread,
Aug 27, 2009, 2:30:08 AM8/27/09
to Compojure
There are many kinds of Comet:
http://cometdaily.com/2007/12/11/the-future-of-comet-part-1-comet-today/

I think from the server app point of view, there are only 2 kinds:
1. Long polling: delay + respond only once
2. Forever frame: write to output stream + flush HTTP connection
socket

1 is exactly the same as normal response. 2 is streaming (Erlang
style: http://yaws.hyber.org/stream.yaws), not too different from
normal response. I doubt that the only reason Comet is seen different
is that most web server cannot support thousands of connections at the
same time.


On Aug 27, 1:53 pm, Shantanu Kumar <kumar.shant...@gmail.com> wrote:
> Comet is not part of the Servlet specification as of date (version 2.5) -
> the upcoming 3.0 specification is trying to fix that. Servlets so far have
> assumed short-lived request-response cycle for HTTP. Hence there are
> proprietary (in the API sense) implementations available, such as in Jetty
> and Resin.
> The fundamental difference between a regular HTTP request-response cycle and
> Comet can be illustrated by the example below:
>
> Regular Servlet: REQUEST<--->RESPONSE [Socket may or may not close, handled
> by web container]
>
> Comet:
> <---REQUEST---->RESPONSE--->RESPONSE--->RESPONSE<---REQUEST--->RESPONSE--->RESPONSE
> [Socket presumably remains in Keep-alive mode]
>
> As you will notice, even though basic elements like cookie, session, content
> type etc are the same, the REQUEST-RESPONSE cycle is not predictably the
> same anymore. This requires the Comet action to behave like a server for
> each client, and cannot be retro-fitted onto a regular Servlet.
>
> I am thinking about adding Comet support to Jettify (URL below), but no
> promise yet:
>
> http://bitbucket.org/kumarshantanu/jettify/
>
> Regards,
> Shantanu
>

ngocdaothanh

unread,
Aug 27, 2009, 2:47:02 AM8/27/09
to Compojure
Google for ContinuationCometdServlet and Compojure I found this:
http://github.com/weavejester/compojure/blob/2d79d234c0f8a3a4fa7aedcb24c239e4b6181994/lib/compojure/cometd/cometd.clj

How come this file is not part of latest version of Compojure anymore?

James Reeves

unread,
Aug 27, 2009, 5:17:42 PM8/27/09
to Compojure
On Aug 27, 5:27 am, ngocdaothanh <ngocdaoth...@gmail.com> wrote:
> As I understand Comet and normal HTTP requests/responses are the same.
> The only different is that for normal requests the app should respond
> as quick as possible and for Comet requests the app can delay.

Not really. Let me illustrate this with an example:

Imagine you need to create a real-time chat application between two
people over HTTP. This is a more complex problem than it might seem.

Your first problem is that RFC 2616 says that clients should maintain
only 2 simultaneous connections to HTTP servers. This probably isn't
enough, especially if a user opens up your application in more than
one browser tab. To get around this, you'll need to have a DNS entry
for wildcard subdomains, and then add some Javascript to ensure you
connect to "<random-string>.yoursite.com". Browsers will treat each
subdomain as a separate server.

Your next problem is maintaining a reliable stream of data. Say you
use a long-polling XHR; each time you receive data, or each time the
XHR times out, you need to disconnect, then reconnect. You need to
ensure that you're not missing any data that is sent during the times
you are not connected, so you need to mark each message with a
incremental sequence number. When the client connects to the server,
it passes the sequence number of the last message it received as a
parameter. The server then passes back any message with a greater
sequence number. This means you'll need a message stack for your
server.

Next, you need to handle disconnections. There isn't any reliable
cross-browser way to inform a server of a closing browser, so you'll
need to create a timeout of how long you'll wait for clients to send a
request before they're considered "disconnected".

Okay, so now you've got a fairly reliable message stream set up. This
requires a fair bit of Javascript and server-side code. But we're not
done yet! Most web servers open up a thread per request, and this will
limit the number of simultaneous users you can support to a few
thousand per server. To improve this, you'll need to use something
like java.nio to make use of low-level OS networking constructs like
selectors.

So my point is that a library designed to serve web pages and
resources, like Compojure, is an entirely different beast to a
streaming network library that happens to use HTTP as a carrier. In
Compojure, you might write something like this:

(defroutes documents
(GET "/document/:id"
(render-document
(headers "accept")
(params :id)))

But in a comet library, you're probably going to be writing something
like this:

(add-listener "/users/connect"
(fn [client message]
(if-let [channel (get-channel (message :channel))]
(if-let [user (get-user client)]
(send-to channel {:type :announce,
:mesg (str (user :login) " joined")})))))

In essense, a good web framework is about resource defining. A good
comet framework is about event handling. Two entirely different
paradigms that really need two separate libraries.

- James

James Reeves

unread,
Aug 27, 2009, 5:30:40 PM8/27/09
to Compojure
On Aug 27, 7:47 am, ngocdaothanh <ngocdaoth...@gmail.com> wrote:
> Google for ContinuationCometdServlet and Compojure I found this:http://github.com/weavejester/compojure/blob/2d79d234c0f8a3a4fa7aedcb...
>
> How come this file is not part of latest version of Compojure anymore?

It had a few issues, ones that were probably entirely solvable, but
comet wasn't my focus at the time. In addition, it probably belongs in
a separate library, as it doesn't have anything in common with the
rest of Compojure. Another problem is that it's tied to a specific
server (Jetty), and to be honest, the Bayeux protocol that
ContinuationCometdServlet uses strikes me as somewhat overcomplicated.

But if anyone wants to take the code I have and turn it into a
library, they're more than welcome.

- James
Reply all
Reply to author
Forward
0 new messages