Skip to main content

Grizzly : Speedup the ProtocolFilter response time

Posted by survivant on November 21, 2008 at 12:51 PM PST

Suppose that you are dealing with sql query that you send to a database.  Some query could take few seconds to run, that will block a Thread.  Even if you have few Threads in your pool, they could all be stuck there too.  To avoid that you can use the Producer/Consumer pattern.

Take a look at this snippets.

....

// default Pipeline settings
Pipeline pipeline = new DefaultPipeline();
pipeline.setMaxThreads(5);
controller.setPipeline(pipeline);

// the ParserProtocolFilter that will parse the query
QuoteQueryProtocolFilter protocolParser = new QuoteQueryProtocolFilter();

// the ProtocolFilter that will process the query
QuoteQueryManagerFilter quoteManagerFilter = new QuoteQueryManagerFilter();

final ProtocolChain protocolChain = new DefaultProtocolChain();
protocolChain.addFilter(protocolParser);
protocolChain.addFilter(quoteManagerFilter);

....

Suppose we want to simulate a waiting IO, you can do a sleep for 30 seconds.  The effect will be that the manager will wait

public class QuoteQueryManagerFilter implements ProtocolFilter {

public boolean execute(Context context) throws IOException {
        String query = (String) context.removeAttribute(ProtocolParser.MESSAGE);

        ......
       
       
// that will simulate that the database take 30 sec to complete the query
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // call the database
        //databaseManager.execute(query);
 
        return true;

}

If you send 10 query to your application, the application will block after 5 query (pipeline threads).  The 6th query will be executed after 30 sec.

We can change that behaviour easily. 

We will need a Producer and a Consumer with a queue.  You can take a LinkedBlockingQueue as a queue. 

Here the snippets for the Consumer : (the Thread.sleep is only for testing)

public class Consumer implements IConsumer, Runnable {

protected BlockingQueue<String> blockingQueue;
....

public void run(){
     .....
     while(!Thread.currentThread().isInterrupted()){
            try {
                String query = blockingQueue.take();
               
                System.out.println("     took item=" + query);
                // now call your database
                // that will simulate that the database take 30 sec to complete
                try {
                     Thread.sleep(30000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                databaseManager.process(command);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
     }
     .....
}

For the Producer, I use my databaseManager.

public class DatabaseManager {

protected BlockingQueue<QueueCommand> blockingQueue;
....

public void init(){
     
      ThreadGroup tg = new ThreadGroup("Producer/Consumer");
      quoteConsumer = new QuoteConsumer(this, blockingQueue);
      new Thread(tg, quoteConsumer,"QuoteConsumer").start();

}

public void addtoQueue(String query) {
        System.out.println("add item=" + query);
        blockingQueue.add(query);
    }

....
}

and don't forget the change the QuoteQueryManagerFilter  :

public boolean execute(Context context) throws IOException {
        String query = (String) context.removeAttribute(ProtocolParser.MESSAGE);

        ......
       
        // call the database
        databaseManager.addToQueue(query);

        return true;

}

If you try this, the database will still take 30 seconds to run, but your application will be able to handle the requests.

Related Topics >>