users@grizzly.java.net

Re: Problem with Suspend/Resume

From: Oleksiy Stashok <oleksiy.stashok_at_oracle.com>
Date: Thu, 20 Jan 2011 14:05:00 +0100

Hi David,

> Interesting … some questions if you mind :
no problem :)

> * You remove all the tests regarding response.isSuspended() before
> suspending or resuming, is it because it’s useless or not
> “safe” (because I see this method is not sync in the source) ?
useless :) We assume these methods being called just from single
thread at the time, if it's not the case - you might need to use some
custom sync mechanism.

> * In the MyExecutor.run() method, you replace the Response.finish()
> by a Response.flush(), is it because it’s wrong to call finish() in
> sync mode ?
IMO it's better to perform minimal required operation and delegate
finish to be called from Grizzly.

> * Also, you comment the response/request recycling in
> MyAdapter.afterService(). Again, is it because it’s useless or
> because it’s wrong ? I made some tests to call or not recycle, but
> it’s doesn’t seems to break something.
useless. Grizzly should take care of recycling after resume called.

> I ask myself many question also, because right now, in my server
> integration, the usage of the async adapter (MyAdapter) is only a
> decorator that is install or not on the root adapter, based on some
> configuration parameter. And of course this adapter call other
> adapters (like the Jersey GrizzlyContainer). Basically, it’s more or
> less like that :
>
> public class MyAsyncAdapter
> {
> Adapter target;
> ThreadPoolExecutor executor;
>
> public MyAsyncAdpater( Adapter target )
> {
> this.target = target;
> }
>
> public void service( Request request, Response response )
> throws Exception
> {
> response.suspend();
> executor.execute( new
> MyExecutor(request,response,target) );
> }
>
> public void afterService( Request request, Response
> response ) throws Exception
> {
> target.afterService( request, response );
> }
> }
>
> Does it seems to be ok for you ?
Yes, looks fine.


> What if the target adapter does a recycle() on request/response or/
> and finsh() or other call that is not allow to do in async ?
It's allowed, but IMO there is no reason to do that. Look at that as
optimization :)


> Last note, we use this design, because in our server, some request
> takes long time (download huge file for example), and we don’t want
> to block other users for quick request (mainly REST). At first, we
> just increase the selector max thread, but it takes too much memory.
> That’s why we implements this design, by delegating the requests/
> responses to a thread pool and thanks to the suspend/resume it was
> pretty easy. We found this solution more lightweight.
> But maybe, what we done is just plain wrong ?
No, this makes sense.

Though in Grizzly 2.0 we have non blocking streams support, both input
and output, so you'd be able to leverage NIO features and big
downloads wouldn't affect other apps, cause writes wouldn't block.
Here is the sample you might want to take a look at [1].

WBR,
Alexey.

[1] http://java.net/projects/grizzly/sources/svn/content/branches/2dot0/code/samples/http-server-samples/src/main/java/org/glassfish/grizzly/samples/httpserver/nonblockinghandler/NonBlockingHttpHandlerSample.java

>
> Thanks for your help
> Regards
> David.
>
>
> De : Oleksiy Stashok [mailto:oleksiy.stashok_at_oracle.com]
> Envoyé : mercredi 19 janvier 2011 18:17
> À : Gay David (Annecy)
> Cc : users_at_grizzly.java.net
> Objet : Re: Problem with Suspend/Resume
>
> Oh, btw, here are updated test sources for MyAdapter and MyExecutor
> I can propose.
>
> WBR,
> Alexey.
>
>
> package com.acme;
>
> import com.sun.grizzly.tcp.Adapter;
> import com.sun.grizzly.tcp.Request;
> import com.sun.grizzly.tcp.Response;
> import com.sun.grizzly.util.buf.ByteChunk;
> import java.io.IOException;
> import java.util.concurrent.SynchronousQueue;
> import java.util.concurrent.ThreadPoolExecutor;
> import java.util.concurrent.TimeUnit;
>
> /**
> *
> */
> public class MyAdapter implements Adapter
> {
> private ThreadPoolExecutor executor;
> private ByteChunk bc;
> private boolean suspendMode;
>
> public MyAdapter( boolean suspendMode )
> throws IOException
> {
> executor = new ThreadPoolExecutor( 0, 500, 5*60,
> TimeUnit.SECONDS, new SynchronousQueue<Runnable>() );
> bc = new ByteChunk();
> bc.append( "pong".getBytes(), 0, 4 );
> this.suspendMode = suspendMode;
> }
>
> @Override
> public void service( Request request, Response
> response ) throws Exception
> {
> if( suspendMode )
> {
> response.suspend();
> executor.execute( new MyExecutor(request,response,bc) );
> }
> else
> {
> response.setStatus( 200 );
> response.setMessage( "Sounds good" );
> response.doWrite( bc );
> response.flush();
> }
> }
>
> @Override
> public void afterService( Request request, Response
> response ) throws Exception
> {
> // request.recycle();
> // response.recycle();
> }
> }
>
>
>
> package com.acme;
>
> import com.sun.grizzly.tcp.Request;
> import com.sun.grizzly.tcp.Response;
> import com.sun.grizzly.util.buf.ByteChunk;
>
> /**
> *
> * @author dGay
> */
> public class MyExecutor implements Runnable
> {
> Request req;
> Response res;
> ByteChunk bc;
>
> public MyExecutor( Request req, Response res, ByteChunk bc )
> {
> this.req = req;
> this.res = res;
> this.bc = bc;
> }
>
> @Override
> public void run()
> {
> try
> {
> // Thread.sleep( 5 );
> res.setStatus( 200 );
> res.setMessage( "Sounds good" );
> res.doWrite( bc );
> res.flush();
>
> }
> catch( Exception ex )
> {
> System.out.println( "\n\tException while writing
> response !" );
> ex.printStackTrace( System.out );
> System.out.println();
> }
> finally
> {
> try
> {
> res.resume();
> } catch (Exception ex)
> {
> System.out.println( "\n\tException while
> resuming !" );
> ex.printStackTrace( System.out );
> System.out.println();
> }
> }
> }
> }
>
> On Jan 19, 2011, at 17:55 , Gay David (Annecy) wrote:
>
>
>