Server push

28 views
Skip to first unread message

dflorey

unread,
May 12, 2007, 10:52:53 AM5/12/07
to Google Web Toolkit
Hi,
I just started to implement my first GWT-based app and I came across
the requirement that I need a listener on client side that gets
actively notified by server event. A pull-approach is not feasable as
I need realtime-notifications.
I finally implemented a notification mechanism based on "hanging"
listener threads for each client. I know that this has many
disadvantages (tomcat needs to provide enough listener threads to
server all clients) but I wonder if anybody is interested in this.
I could provide a reusable & simplified version if appreciated but
perhaps I'm completely on the wrong track and there might be a better
solution availbale already?

Daniel

Cameron Taggart

unread,
May 12, 2007, 2:41:17 PM5/12/07
to Google Web Toolkit
I've been looking into "Server push" too lately. Looking at
gpokr.com, it is obviously possible. I saw an announcement about
Direct Web Remoting v2 which includes support for "Reverse Ajax".
Also, Grizzly which is apart of GlassFish released some stuff which
may be of interest to you. Jean-Francois Arcand, Grizzly's author,
also posted some links to the Bayeux Protocol Specification in early
draft form. Apparently it is being written under the Dojo Foundation
which looks to have a bunch of contributors from large companies.
http://dojotoolkit.org/foundation

http://www.theserverside.com/news/thread.tss?thread_id=45164

http://weblogs.java.net/blog/jfarcand/archive/2007/05/first_community.html
# Grizzly Comet: A tiny/embeddable web server which support Comet
Request Processing
# Grizzly Cometd: A tiny/embeddable web server which support Cometd/
Bayeux JSON based protocol.

http://weblogs.java.net/blog/jfarcand/archive/2007/04/new_bayeux_spec.html#comments

Well, there you go. Those are the resources that I have found. Hope
that helps,

Cameron

charlie...@gmail.com

unread,
May 12, 2007, 3:14:01 PM5/12/07
to Google Web Toolkit
Might want to check out Luca Masini's work with Comet and GWT:
http://www.jroller.com/page/masini?entry=a_comet_implementation_for_google.

On May 12, 7:52 am, dflorey <daniel.flo...@gmail.com> wrote:

Reinier Zwitserloot

unread,
May 12, 2007, 7:51:37 PM5/12/07
to Google Web Toolkit
Couple of quick notes:

1. Jetty has continuations. Unless you want your server to come to a
grinding halt on pathetically small loads, you NEED continuations.
Continuations scale. Tomcat does not scale for server push.

2. Jetty has support for cometd, but cometd is a protocol to multiplex
unrelated 'server push' connections across a single connection. The
reason you need to do that, is because browsers only open at most 2
connections to a single host. Usually you don't have multiple
unrelated streams that all need server push. In those circumstances,
cometd is pure overkill, and you shouldn't use it.

3. server push has a metric kilopissload (about 4 imperial buttloads)
of caveats, quid pro quos, and other nasty surprises. You really need
to know what you're doing. to wit:

- IE can't handle reading server push properly (There are hacks, but
those don't work on other browsers), so you need to implement half
server push: dangle the connection, yes, but close it right after
sending a single 'packet' of information. E.g., a single chat line.

- proxies don't know about server push. Again, closing the connection
can help 'flush the stream' as it were, but even with that (also
solves IE problem above), proxies tend to just call timeout and close
the connection after a while of no traffic. This means you should
probably be sending inconsequential characters (usually whitespace)
every so often, and close down the connection after a minute or two
with the notification to the client that A. nothing happend and B. if
the client would be so kind to re-open the same connection?

and I haven't even covered the half of it.

Did you guys read this more extensive comment?

http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/fb3c775e81903d97/b6d649b9e065e8f5?lnk=st&q=&rnum=41#b6d649b9e065e8f5


On May 12, 9:14 pm, "charlie.coll...@gmail.com"
<charlie.coll...@gmail.com> wrote:
> Might want to check out Luca Masini's work with Comet and GWT:http://www.jroller.com/page/masini?entry=a_comet_implementation_for_g....

dflorey

unread,
May 13, 2007, 8:50:28 AM5/13/07
to Google Web Toolkit
Hi folks,
thanks a lot for your valuable input! Seems this is a great
community.I will leave the implementation of my silly simple server
push as it is as I'm building a call-center softphone application
where I exactly know the number of agents=number of dangling threads.
It works pretty well - beside the limitations Rainer mentioned.
So if anybody interested in this little NotificationService I could
provide a demo project. But as far as I got form the responses so far
the limitations make this approach not very attractive... It is really
limited to inhouse apps where you serve a known amount of concurrent
users. Nothing for larger web apps.
Thanks!
Daniel

> http://groups.google.com/group/Google-Web-Toolkit/browse_thread/threa...


>
> On May 12, 9:14 pm, "charlie.coll...@gmail.com"
>
>
>
> <charlie.coll...@gmail.com> wrote:
> > Might want to check out Luca Masini's work with Comet and GWT:http://www.jroller.com/page/masini?entry=a_comet_implementation_for_g....
>
> > On May 12, 7:52 am, dflorey <daniel.flo...@gmail.com> wrote:
>
> > > Hi,
> > > I just started to implement my first GWT-based app and I came across
> > > the requirement that I need a listener on client side that gets
> > > actively notified by server event. A pull-approach is not feasable as
> > > I need realtime-notifications.
> > > I finally implemented a notification mechanism based on "hanging"
> > > listener threads for each client. I know that this has many
> > > disadvantages (tomcat needs to provide enough listener threads to
> > > server all clients) but I wonder if anybody is interested in this.
> > > I could provide a reusable & simplified version if appreciated but
> > > perhaps I'm completely on the wrong track and there might be a better
> > > solution availbale already?
>

> > > Daniel- Zitierten Text ausblenden -
>
> - Zitierten Text anzeigen -

dflorey

unread,
May 13, 2007, 9:41:28 AM5/13/07
to Google Web Toolkit
I just had a quick look at the jetty Continuations. This seems to be
exactly what I need to scale my server-push approach to larger
environments. Thanks a lot for the hint, Rainer!
BTW: I've used a timestamp-based event retrieval to deal with missed
events while re-establishing the connection. Might be useful for other
impls as well.

Cheers,
Daniel

> > - Zitierten Text anzeigen -- Zitierten Text ausblenden -

Reinier Zwitserloot

unread,
May 13, 2007, 12:31:59 PM5/13/07
to Google Web Toolkit
Yes, the standard way of doing this is to see the datastream as a
series of notifications of 'events', each event occuring at a set
timepoint. You could use timestamps, but it's more usual to use a
simple counter, because that way you can have multiple events in a
single millisecond, and it's more obvious when you missed something.

You then make a request for 'all events with id 858 and up'. There are
now 2 scenarios:

1. event with id 858 already happend. In this case, send ALL events
numberred 858 and up to the client, then close the connection; no
server push aspect at all in this scenario. e.g: if id 861 is already
on the books, send 858, 859, 860, and 861, then close the connection.

2. event with id 858 hasn't actually happend yet. throw the
RetryException (jetty's continuation system) or freeze the thread
(tomcat unscalable server push) and wait for 858 to happen. Wake up
every 20 seconds and push a noop across the line (e.g. whitespace).
Once 858 happends, send it and close the connection. If 858 never
happends, after 90 seconds or so, send an explicit no-data marker and
close the connection.

As a client you know where you are, event-wise. Let's say you've got 1
through 857 covered, so you make a request for 858. Three things can
now happen:

1. You get a bunch of events in, (how long it took isn't important;
the fact that the server might be in scenario #1 or #2 is not relevant
client side). You do what you're supposed to do with them (in a chat
app, for example, display the lines in the browser), update your
internal state counter as 'I've processed up to e.g. 861', and
immediatly re-open the connection, this time asking for 862 and
upwards. clear the error counter.

2. You get no events in, but you do get the specific no-data marker.
You re-open the connection for the same event id (858, again). clear
the error counter.

3. You get no events in, and no specific no-data marker. Sounds like
server trouble. Increase the error counter. If the error counter is at
5, stop the app and update the browser to reflect there's some sort of
network problem. The reason you need this scenario is because without
it, your app might faulty endlessly retry the connection, faultily
concluding the server simply has no data for us and would like for us
to re-connect, completely freezing out the browser as you try and fail
the AJAX call thousands of times a second. You might consider trying
to reconnect every 30 seconds or so, so that your app can jump right
back in the moment networking is restored, or the moment the server
recovers (as you can't really know if the server just went down, or
the local user's network, or something in between, unless you do some
heuristics on exactly how long it takes for the connection to fail.
Not recommended - quick return doesn't neccessarily mean it's a local
problem, and significant time passed between attempt and failure does
not neccessarily mean there is no local problem).

dflorey

unread,
May 14, 2007, 3:41:52 AM5/14/07
to Google Web Toolkit
I prefer to stick with my timestamps as I can send all missed events
to the client app after restart without persisting the proposed
counter.
But many thanks for your suggestions anyway!!

Daniel

dflorey

unread,
May 14, 2007, 3:43:37 AM5/14/07
to Google Web Toolkit
This is how my server code looks like:

package com.floreysoft.cti.server;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.floreysoft.cti.client.Event;
import com.floreysoft.cti.client.MessageEvent;

public class EventQueue {
private static final long TIMEOUT = 60000;
private static final int MAX_EVENTS = 10;

public static EventQueue eventQueue = new EventQueue();

private Map<String, List<Event>> eventMap = new HashMap<String,
List<Event>>();
private Map timestamps = new HashMap();
private boolean showMissedEvents = false;
private int maxEvents = MAX_EVENTS;
private long timeout = TIMEOUT;

public static EventQueue getInstance() {
return eventQueue;
}

public void addEvent(String sessionHandle, Event event) {
List<Event> eventList;
synchronized (eventMap) {
eventList = eventMap.get(sessionHandle);
if (eventList == null) {
eventList = new ArrayList<Event>();
eventMap.put(sessionHandle, eventList);
}

}
synchronized (eventList) {
eventList.add(event);
if (eventList.size() > maxEvents) {
eventList.remove(0);
}
eventList.notify();
}
}

public List<Event> getEvents(String sessionHandle) {
List<Event> eventList = getEventList(sessionHandle);
synchronized (eventList) {
try {
eventList.wait(timeout);
} catch (InterruptedException e) {
// Ignore
}
}
return eventList;
}

private List<Event> getEventList(String session) {
List<Event> eventList;
synchronized (eventMap) {
eventList = eventMap.get(session);
if (eventList == null) {
eventList = new ArrayList<Event>();
eventMap.put(session, eventList);
}
}
return eventList;
}

@SuppressWarnings("unchecked")
public List getNewEvents(String session) {
Date lastTimestamp = (Date) timestamps.get(session);
List<Event> eventList = null;
List newEvents = new ArrayList();
if (lastTimestamp == null) {
if (showMissedEvents) {
lastTimestamp = new Date(0);
eventList = getEventList(session);
} else {
lastTimestamp = new Date();
// Wait for next event
eventList = getEvents(session);
}
timestamps.put(session, lastTimestamp);
} else {
// Check for events that might have been occured in the meantime
eventList = getEventList(session);
synchronized (eventList) {
for (Event event : eventList) {
if (event.getTimestamp().after(lastTimestamp)) {
newEvents.add(event);
}
}
if (newEvents.size() > 0) {
timestamps.put(session, new Date());
return newEvents;
} else {
// No events in the meantime, so wait for new events
eventList = getEvents(session);
timestamps.put(session, new Date());
}
}
}
synchronized (eventList) {
for (Iterator i = eventList.iterator(); i.hasNext();) {
Event event = (Event) i.next();
if (event.getTimestamp().after(lastTimestamp)) {
newEvents.add(event);
}
}
}
return newEvents;
}
}

On 13 Mai, 18:31, Reinier Zwitserloot <reini...@gmail.com> wrote:

Reinier Zwitserloot

unread,
May 14, 2007, 10:26:24 AM5/14/07
to Google Web Toolkit
Right. The code is fine, except it won't scale for beans. Rewriting
this to fit Jetty Continuations is 5 minutes of work.

On May 14, 9:43 am, dflorey <daniel.flo...@gmail.com> wrote:
> This is how my server code looks like:
>

> [snip]

Maarten Volders

unread,
May 15, 2007, 3:59:20 AM5/15/07
to Google-We...@googlegroups.com
Hi Reinier,

I was just following up on this topic, I'm new to Jetty Continuations so if you have some time could you elaborate on what actually needs to be done to transform it to the Jetty Cont. philosophy.
Another question, I'm designing a fully GWT enabled application, so the full app is contained in one single .html, I have a lot of small client / server requests, will it drastically improve performance when changing to continuations? What is your vision on this?

Grtz

On 5/15/07, Maarten Volders <maa...@sherpa-consulting.be> wrote:
Hi Reinier,

I was just following up on this topic, I'm new to Jetty Continuations so if you have some time could you elaborate on what actually needs to be done to transform it to the Jetty Cont. philosophy.
Another question, I'm designing a fully GWT enabled application, so the full app is contained in one single .html, I have a lot of small client / server requests, will it drastically improve performance when changing to continuations? What is your vision on this?

Grtz

Reinier Zwitserloot

unread,
May 15, 2007, 5:21:28 AM5/15/07
to Google Web Toolkit
There are plenty of tutorials on Jetty continuations out there. I
suggest you go and find them.

Continuations help ONLY for when you ordinarily would freeze the
thread waiting for an external event (so not a db return or file
access, something that would be trigger by another request and might
take multiple seconds).

The only practical way to improve performance when you have lots of
small requests is to turn them into larger requests. Instead of making
3 sequential calls, make 1 servlet that makes those 3 calls for you
and rolls them into a single GWT-RPC return object / JSON return
value / XML stream - whatever protocol you're using. That'll help. Not
much, but a little.

On May 15, 9:59 am, "Maarten Volders" <maarten.vold...@gmail.com>


wrote:
> Hi Reinier,
>
> I was just following up on this topic, I'm new to Jetty Continuations so if
> you have some time could you elaborate on what actually needs to be done to
> transform it to the Jetty Cont. philosophy.
> Another question, I'm designing a fully GWT enabled application, so the full
> app is contained in one single .html, I have a lot of small client / server
> requests, will it drastically improve performance when changing to
> continuations? What is your vision on this?
>
> Grtz
>

> On 5/15/07, Maarten Volders <maar...@sherpa-consulting.be> wrote:
>
>
>
> > Hi Reinier,
>
> > I was just following up on this topic, I'm new to Jetty Continuations so
> > if you have some time could you elaborate on what actually needs to be done
> > to transform it to the Jetty Cont. philosophy.
> > Another question, I'm designing a fully GWT enabled application, so the
> > full app is contained in one single .html, I have a lot of small client /
> > server requests, will it drastically improve performance when changing to
> > continuations? What is your vision on this?
>
> > Grtz
>

Reply all
Reply to author
Forward
0 new messages