-
FEATURED COMPONENTS
First time here? Check out the FAQ!
I have design problem that I'm looking to solve with ZK.
There is a requirement to build a web app to support a portal style multi window display where each window is displays two panes: a query statement in the top pane, and the query results in a grid in the second pane. The queries are being sent to an underlying custom analytics engine. Each query window need to be modeless - think MDI. The data queries to the underlying custom analytics engine can be very long running - 2 minutes plus sometimes to return results.
To address this I want to provide the user with a MDI style interface where they can:
* Open a new query window
* Type in the query string and select 'go'. Under the hood the code will fire off the query to the remote custom analytics engine
* Open a second query window
* In the second query window type in a new query and select 'go'
* Repeat this for a third window and so on.
* Once a query window has received its results, the user is free to change the query statement and fire off a second different query.
Behind the scenes something has to receive a notification that a query has completed and that the related window grid needs to be updated with the results. There is no indication of when a query will be completed by the underlying custome analytics engine. If my app fires off queries from windows A, B C and D; the results could return in any order.
Any suggestions for how to deliver this in ZK?
You have a good point there. The ZK-provided server push implementations are not very good.
PollingServerPush: High latency, high cpu load on server because all users are constantly polling the server
CometServerPush: Low latency, low CPU load on server, but comet threads are not asynchronous and will eat up the server thread pool if there are plenty of users
I've blogged shortly about these shortcomings here.
Ok, so why don't I recommend iframes then?
From the server side point of view, iframes have similar problems as CometServerPush has. You'll end up running background job in the servlet threads, which means that they will eat up the server thread pool. It's slightly more efficient with small amount of windows because there are only zkau threads (but some of those are blocked ones).
From the client side point of view, iframes have some disadvantages:
- CSS styling bugs (probably just IE). MDI style interface was a requirement so windows might be stacked on top of another. I'm not sure if ZK works around these styling bugs but if it doesn't, you'll end up with problems.
- I'm not 100% sure but I think that browser max connections limits are usually per host. For example in older IE versions the limit is 2 connections. That means that you cannot have more than 2 long running queries at the same time. Opening 3 windows and running a query in each of them would not work.
Summary:
Either way you are screwed on the server side. I think that CometServerPush is the best solution here because there is only one blocking thread per desktop. Running 100 queries simultaneously in one browser window would block only 1 server thread (it would of course block 100 background threads but that is unavoidable). Running 100 queries with the iframe solution would block 100 server threads (but no background threads).
The technically correct solution is to make the server push asynchronous, but ZK does not provide such functionality out of the box. In case you are interested, I'm working on a prototype implementation that can be found here.
CometServerPush:
1 blocking comet thread per desktop
non-blocking zkau threads
1 blocking background thread per query
Iframes:
1 blocking zkau thread per query
non-blocking zkau threads
(AsynchronousServerPush):
1 non-blocking comet thread per desktop
non-blocking zkau threads
1 blocking background thread per query
Im very interested of reading more of this too.
Thanks for the explanation Joonas.
thanks
Stephan
I started writing a series of blog posts about this. I'll let you guys know once I've published them :)
There's also a working sample app at https://github.com/Gekkio/zk-async-example
Many thanks for that Joonas.
I have time to checkout on weekend.
thanks
Stephan
PS: I found an interesting article that discribes the blocking problem: http://www.theserverside.com/news/1363576/What-is-the-Asynchronous-Web-and-How-is-it-Revolutionary
I know that I've joined this discussion late, but I have a question.
First, gekkio, you've done a nice piece of work.
Second, I'm wondering if you or someone else can compare and contrast it to an implementation that uses only the ZK event mechanism. If your WindowComposer had these methods:
public void onClick$runButton() { // Let's disable the button so the user cannot start another task while a previous one is processing runButton.setDisabled(true); runButton.setLabel("Running..."); Events.echoEvent( "onProcessRequest", self, null ) ; Clients.showBusy( "Processing...", self ) ; // optional if you want to occupy UI while task is running } /** * This event listener method will be called to perform the long running work * * @param event */ public void onProcessRequest(Event event) { List<Integer> results = null ; // do whatever the long running task is... results = doWhatever() ; // Re-enable the button runButton.setDisabled(false); runButton.setLabel("Run task"); // Let's visualize results in the grid results.setModel(new ListModelList(results)); Clients.clearBusy( self ) ; // optional if you want to occupy UI while task is running }
What are the ramifications of this approach compared to your AsyncTask + ThreadPool approach?
Cary, that's a good question.
The most obvious ramification is that doing long running work in servlet threads blocks all other event processing in the same desktop (= browser window). This happens because ZK serializes client-to-server requests (when I say "serializes", I don't mean java.io.Serializable, I mean that requests are processed serially, never in parallel). So basically this is what happens:
1. Client->Server: runButton.onClick is sent
2. Server->Client: server response commands client to do runButton modifications (label=Running..., disabled=true) and echo back with onProcessRequest
3. Client->Server: onProcessRequest is sent
4. long running process while server handles onProcessRequest. During this time no other Client->Server requests can happen (e.g no onClicks of other buttons)
5. Server->Client: server response commands client to show results and re-enable runButton
6. (Client->Server): Any server-side events you have caused in the browser window (button clicks, etc) during the long running process are sent at this point.
So, to elaborate further, using echoEvent and processing directly in the servlet thread is not ok for a MDI-style interface where you have multiple windows doing long running tasks. You'd be able to only open one window and it would effectively block the whole UI while processing.
I've done some updates to the example project. There's now an alert button that can be used to test when new server-side events are processed. I also refactored the code a bit so it can be modified more easily to test other solutions.
You can now test your solution yourself with these modifications to WindowComposer:
public void onClick$runButton() { visualizeProcessingStart(); Events.echoEvent("onProcessRequest", self, null); } public void onProcessRequest() { List<Integer> resultData = ExampleTask.generateNumbers(amount.intValue()); visualizeProcessingEnd(resultData); }
You can see the effect I described earlier by doing those modifications, starting one process and clicking the alert button. You'll see that the button is unresponsive and the alert pops up only when the processing is finished.
Thanks Stephan, that's a good article!
By using the terminology from the article, the server push implementations can be classified like this:
PollingServerPush: HTTP Polling, normal servlets
CometServerPush: HTTP Long Polling, normal servlets
AtmosphereServerPush: HTTP Long Polling, asynchronous servlets
(AtmosphereServerPush is my implementation that uses the Atmosphere framework for truly asynchronous processing)
A quick update on the example project:
In the async branch there's now a version of the project with AtmosphereServerPush.
You can now easily test and see the major difference between CometServerPush vs AtmosphereServerPush (thread starvation).
1. Uncomment the relevant section in pom.xml (= limits Jetty to 3 threads)
2. Run the project
3. Open more than one browser window/tab and start processing in them
See the project README for instructions.
CometServerPush quickly eats the 3 threads and you'll see that the UI becomes completely unresponsive. AtmosphereServerPush does not care and will work even if you open 100 browser windows.
Once I finish my blog posts, I'll explain this better :)
I think there is real business value for ZK in having some tangible examples of the different server push options made available in the framework and in the demo apps.
With the recently announced death of Flash and similar technologies like Silverlight not long for this world, I suspect that any GUI framework that can show that it has the async server push area covered is going to have a huge advantage.
Asked: 2011-10-31 18:42:05 +0800
Seen: 1,261 times
Last updated: Jan 28 '12