Oct 23, 2016 - HBase: My Study Note

Comments

HBase: My Study Note

I’ve never used Apache HBase before, and now would be a great time to give it a try. I am also pretty new to Docker, so I figured, why not try them together?

 

Running HBase (via Docker)

> docker pull oddpoet/hbase-cdh5
> docker run -p 2181:2181 \
  -p 65000:65000 \
  -p 65010:65010 \
  -p 65020:65020 \
  -p 65030:65030 \
  -h $(hostname) -d oddpoet/hbase-cdh5

Now if we load our browser at http://localhost:65010/ and http://localhost:65030/ we should see the master & region server status.

 

Shell Access

Next step is to use the interactive shell:

> docker ps

CONTAINER ID        IMAGE                COMMAND
5a0ca2bf030e        oddpoet/hbase-cdh5   "/bin/bash start.sh"
...

Mark the “CONTAINER ID” value, and issue a command to attach an interactive shell into the running container:

> docker exec -i -t 5a0ca2bf030e /bin/bash

[root@Nings-MacBook-Pro /]#

Now we are inside the container that is running the HBase instance. If we run the command hbase shell now we can interact with the HBase. The following session creates a table, add some data, and then display and query them:

(Please note that the following commands can be pretty slow, almost like they are failing)

[root@Nings-MacBook-Pro /]# hbase shell

hbase(main):001:0> create 'testTable', 'cf'
0 row(s) in 15.8690 seconds
=> Hbase::Table - testTable

hbase(main):002:0> put 'testTable', 'r1', 'cf:c1', 'v1'
0 row(s) in 0.1400 seconds

hbase(main):003:0> put 'testTable', 'r2', 'cf:c1', 'v2'
0 row(s) in 0.0180 seconds

hbase(main):004:0> scan 'testTable'
ROW                                  COLUMN+CELL
 r1                                  column=cf:c1, timestamp=1477295738212, value=v1
 r2                                  column=cf:c1, timestamp=1477295800618, value=v2
2 row(s) in 0.0320 seconds

hbase(main):005:0> get 'testTable', 'r2'
COLUMN                               CELL
 cf:c1                               timestamp=1477295800618, value=v2
1 row(s) in 0.0160 seconds

 

Java Client

My goto language is Java, so this study is not complete without a very simple working Java client.

We will be using the official hbase-client library:

(pom.xml)
    <dependencies>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>

and the Java code:

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;

public class TestApp {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Configuration config = HBaseConfiguration.create();
        Connection connection = ConnectionFactory.createConnection(config);
        Table table = connection.getTable(TableName.valueOf("testTable"));

        Get g = new Get(Bytes.toBytes("r2"));
        Result r = table.get(g);
        byte [] value = r.getValue(Bytes.toBytes("cf"), Bytes.toBytes("c1"));
        String valueStr = Bytes.toString(value);
        System.out.println("GET: " + valueStr);

        table.close();
    }
}

And when we run this java code we see the result “v2”, which was what we added earlier: r2 column=cf:c1, timestamp=1477295800618, value=v2 So it works!







Aug 14, 2016 - Java Concurrency: Future vs Promise, Part III

Comments

Use Promise To Combine Async Requests

In Part II we talked about Future vs CompletableFuture(Java’s version of Promise). Now let’s take a look at some examples of how we can actually use them.

Some of the most common real world scenarios that keeps popping up, have to deal with multiple async requests.

  • One is serial: we need the result of “request 1” to feed into “request 2”, and the result of “request 2” into “request 3”, and so on. So request 1,2,3 have to be run in that order, and one can not start before the prior one ended.

  • The other one is parallel: we need all the results from “request 1.1”, “request 1.2”, … “request 1.N” before we could proceed to request 2, while “1.1”, “1.2”, … “1.N” are independent of each other, thus can be executed in parallel.

For each of these two challenges, we will first take a look at how we used to solve them without Promise, and then, with.

 

Multiple Async Requests - Serial

 

Setup

For this post, let’s use a simple setup: we have three slow requests, each one being slow by doing a Thread.sleep(2000); Also I have intentionally made the input & output of each request different and chain-able:

  • request 1 output a String,
  • request 2 take a String and output a int (length of input),
  • and request 3 take a int, and output a String.
public class AsyncSerial {
  private static String slow_request_1() {
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) { e.printStackTrace(); }
    return "result from request 1";
  }

  private static int slow_request_2(final String input) {
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) { e.printStackTrace(); }
    return input==null ? 0 : input.length();
  }

  private static String slow_request_3(int input) {
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) { e.printStackTrace(); }
    return "the length of request 1 result ="+input;
  }

}

 

Blocking version

The blocking version is the easiest:

public class AsyncSerial {
  . . . 

  public static void main(String[] args) {
    // // blocking version
    String result1 = slow_request_1();
    int result2 = slow_request_2(result1);
    String result3 = slow_request_3(result2);
    System.out.println( result3 );
  }
}

and after about 6 seconds, we see the output the length of request 1 result =21

 

Callback version

It’s at this stage that we realized that Future is not very helpful in this scenario. Because of the serial nature of the call chain, we need one request to finish before calling the next. So if we want to do this in anything that resemble some kind of async fashion, we could use ‘callback-style’: pass in the ‘next request when this one is done’ as an argument to the current request.

Unfortunately we need to modify our existing requests with the additional callback as a parameter. And here is where it gets ugly:

  private static String slow_request_1_withCB(
        final BiFunction<String, Function<Integer, String>, String> cb1,
        Function<Integer, String> cb2) {
    String request1Output = slow_request_1();
    return cb1.apply( request1Output, cb2 );
  }

  private static String slow_request_2_withCB(final String input, final Function<Integer, String> callback) {
    int request2Output = slow_request_2(input);
    return callback.apply( request2Output );
  }

  private static String slow_request_3_withCB(int input) {
    String request3Output = slow_request_3(input);
    return request3Output;
  }

  public static void main(String[] args) {
    // callback (hell) version
    System.out.println(slow_request_1_withCB(AsyncSerial::slow_request_2_withCB, AsyncSerial::slow_request_3_withCB));
  }

As you can see, the problem is that the chaining of the calls have to be written into the logic. When we call ‘request 1’ not only do we have to pass in ‘request 2’ as a parameter, we also have to supply ‘request 3’ as a parameter to ‘request 1’! This is because at the end of request 1, when 1 calls 2, 1 needs to tell 2 that when it finishes it needs to call 3. In other words ‘request 1’ needs to know not only its immediate successor, but the whole chain.

 

Promise (CompletableFuture) version

The good news is, the CompletableFuture version is pretty much as simple as the blocking version, while remain async.

  public static void main(String[] args) {
    // promise version
    CompletableFuture.supplyAsync( AsyncSerial::slow_request_1 )
        .thenApply( AsyncSerial::slow_request_2 )
        .thenApply( AsyncSerial::slow_request_3 )
        .thenAccept( System.out::println );
    System.out.println("to prove it is async, this line should be printed before the result from above is printed");
  }

As we can see, the power & flexibility of CompletableFuture is in full display here. The CompletableFuture.supplyAsync turns a regular blocking call into an Async Promise, and you can easily chain multiple requests by .thenApply and .thenAccept.

The difference between .thenApply and .thenAccept is that

  • .thenApply takes a Function< T,R > which has both an input and an output. So it’s usually meant for a middle transformation.
  • .thenAccept takes a Consumer< T > which just has an input. So it’s usually meant for the last transformation.

A complete Java source code can be found here.

 

Multiple Async Requests - Parallel

 

Setup

Let’s again use a simple setup: we have three slow requests, this time each one being slow by doing a Thread.sleep(n); but sleep for a random 1 - 2 seconds. After the sleep, request 1, 2, 3 will return String “1”, “2”, “3”. This time we have to wait for all three requests to complete, before proceeding.

public class AsyncParallel {
  private static SecureRandom secureRandom = new SecureRandom();
  static {
    secureRandom.setSeed( System.currentTimeMillis() );
  }
  private static void waitForAWhile() {
    try {
      long sleepTimeInMilli = 1000L + 100L*secureRandom.nextInt(10);  // 1-2 seconds
      Thread.sleep(sleepTimeInMilli);
    } catch (InterruptedException ie) { ie.printStackTrace(); }
  }

  private static String slow_request_1() {
    waitForAWhile();
    return "1";
  }
  private static String slow_request_2() {
    waitForAWhile();
    return "2";
  }
  private static String slow_request_3() {
    waitForAWhile();
    return "3";
  }
}

 

Blocking version

Again, the blocking version is simple:

public class AsyncParallel {
  . . . 

  public static void main(String[] args) {
    // // blocking version
    String s1 = slow_request_1();
    String s2 = slow_request_2();
    String s3 = slow_request_3();
    System.out.println( s1+s2+s3 )
  }
}

and after about 3-6 seconds, we see the output 123. Of course though simple, this code is really bad. We are wasting a lot of time waiting for each request to wake up from their sleep. In fact we are waiting for the sum of their sleep time.

 

CompletionService version

Since Java 7, Oracle introduced CompletionService, where we can submit multiple requests, and these requests can run in parallel, and we can get the result back not in the order of submission but in the order of who finishes first.

  private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();

  public static void main(String[] args) {
    // completionService version
    final ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(THREAD_POOL);
    completionService.submit( AsyncParallel::slow_request_1 );
    completionService.submit( AsyncParallel::slow_request_2 );
    completionService.submit( AsyncParallel::slow_request_3 );
    int numFetched = 0;
    StringBuilder buf = new StringBuilder();
    while (numFetched<3) {
      try {
        final Future<String> future = completionService.take();
        buf.append(future.get());
        ++numFetched;
      } catch (InterruptedException ie) {
        break;
      }
      catch (Throwable e) {
        ++numFetched;
      }
    }
    System.out.println( buf.toString() );
  }

If we run the above code several times, we will see that sometimes the printout is “123”, sometimes it could be “132”, “312”, and so on. This is because completionService.take() will return in the order of who finishes first.

Though the above code is longer than the blocking version, we now run requests in parallel, and the total wait time is the slowest request, instead of the sum of all requests. Much more efficient!

 

Promise version

As usual we saved the best for last. With CompletableFuture we can achieve the efficiency of the CompletionService version, while keeping the blocking version’s simplicity:

public static void main(String[] args) {
  // promise version
  CompletableFuture<String> cf1 = CompletableFuture.supplyAsync( AsyncParallel::slow_request_1 );
  CompletableFuture<String> cf2 = CompletableFuture.supplyAsync( AsyncParallel::slow_request_2 );
  CompletableFuture<String> cf3 = CompletableFuture.supplyAsync( AsyncParallel::slow_request_3 );
  CompletableFuture.allOf(cf1, cf2, cf3).thenApply(v -> {
    System.out.println( cf1.join()+cf2.join()+cf3.join() );
    return null;
  });
  System.out.println("to prove it is async, this line should be printed before the result from above is printed");
}

A complete Java source code can be found here.








Aug 12, 2016 - Java Concurrency: Future vs Promise, Part II

Comments

Future vs Promise

In Part I we briefly talked about the Future interface in Java. In other programming languages such as Javascript, there is also Promise, which is quite similar to Future, but are more powerful in the sense that you can chain them. Now Java 8 introduces CompletableFuture which can be considered as Java’s Promise. In this post I will use CompletableFuture and Promise interchangeably.

Now before diving into the features and usages of CompletableFuture, let us first consider the question: what’s the relationship & differences between Future and Promise? Other than that Future is an interface, and CompletableFuture is a class that implements it?

Both Future and Promise represents the result of an asynchronous computation result. The difference is that a Future is a read-only container of that result, while a Promise can be updated (completable).


One way to imagine this difference, is we have a Caller thread, the Consumer of a (potentially) slow routine, and a Callee thread, which is the (async) Implementor of the slow routine:

Caller/Consumer  --- calls (async) --->  Callee/Implementor 

The Caller/Consumer calls the async routine, and much like in Part I, gets a Future back, and wait on the Future object till the result is in. From this side of the view, the Future object is read-only: all we can do is to wait. We could decide to wait for a limited time only (.get(long timeout, TimeUnit unit)), or even cancel it (.cancel(boolean mayInterruptIfRunning)), but we can’t alter the result. Afterall, we are the caller, the uninfluential outsider.

  // Caller / Consumer
  public void someMethod() {
    Future<String> f = asyncSlowFetch();
    String result = f.get();
    System.out.println( "caller thread got result back: " + result );
  }

The Callee/Implementor usually construct a CompletableFuture instance, and returns it immediately, before starting on its slow and steady routine. When the slow routine is finished, the Callee/Implementor finishes the CompletableFuture instance with the .complete(T value) method. (and it is at this point in time, that the caller’s .get() can return). From this side of the view, we are dealing with a CompletableFuture object that the implementor can manipulate.

  private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();

  // Callee / Implementor
  public Future<String> asyncSlowFetch() {
    CompletableFuture<String> promise = new CompletableFuture<>();
    THREAD_POOL.execute( ()-> {
      String result = slow_calculattion(...);
      promise.complete(result);
    } );
    return promise;
  }

A complete Java source code can be found here.


To hammer this point home, let’s try another example. Let’s say we have many threads all waiting for a single thread to finish, before they could continue. In the old days we could do something like this:

  private static final String LOCK = "LOCK";

  // in the N (waiting) threads
  synchronized (LOCK) {
    LOCK.wait();
  }

  // in the Single (holding) thread, to wake up everyone
  synchronized (LOCK) {
    LOCK.notifyAll();
  }

A complete Java source code can be found here (notify/wait version).

Now with Future & Promise we could achieve similar behavior like this:

  private static final CompletableFuture<String> PROMISE = new CompletableFuture<>();

  // in the N (waiting) threads
  PROMISE.get();

  // in the Single (holding) thread, to wake up everyone
  PROMISE.complete("WAKE UP!!!");

A complete Java source code can be found here (future/promise version).








Aug 3, 2016 - Minimal Web Server & Client

Comments

NodeJS Server

A friend was asking me, “how to setup a minimal web server, so I can have some simple web pages, and interact with the backend?”

Pre-Req

  • NodeJS should be installed
  • npm install express
  • npm install body-parser

 

Starter

The simplest code I can think of, would be NodeJS based. Here is a working 10-line example:

var express = require('express');
var app = express();
var server = app.listen(8000, function () {
  console.log("REST API listening on ", JSON.stringify(server.address()))
});

app.get('/', function (req, res) {
  console.log('Server received GET')
  res.sendStatus(200);
})

If we save the above as a file server.js, run the command node server in the command line, and load http://127.0.0.1:8000 in the browser, we should see an OK in the browser, and Server received GET in the command line console window.

 

Add static file handler

Our goal here is to have some simple web pages, so it would be helpful to be able to serve static files. To do this, we need to add one extra line at the bottom of the above server.js:

var express = require('express');
var app = express();
var server = app.listen(8000, function () {
  console.log("REST API listening on ", JSON.stringify(server.address()))
});

app.get('/', function (req, res) {
  console.log('Server received GET')
  res.sendStatus(200);
})

app.use('/public', express.static('static'));

The line app.use('/public', express.static('static')); tells NodeJS/Express that whenever we receive a request of the pattern http://...:8000/public/*, serve static files from the folder static.

Now save the above to server.js, create a folder static (in the same folder as server.js), and add a file static/test.html. You can put whatever content in there. Now load http://127.0.0.1:8000/public/test.html in the browser, we should see the static content we just put in the file.

 

Handle Form Submit

We may want to handle form submit. Let’s first write a simple form:

<form action="/formHandler" method="post">
  <input type="text" name="fieldA" value="">
  <input type="submit">
</form>

Change the static/test.html created in the previous step to these 4 lines. Now load http://127.0.0.1:8000/public/test.html and we should see a simple one-line form.

To hanlde the form submit (read the submitted value), we can use the body-parser library.

var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var server = app.listen(8000, function () {
  console.log("REST API listening on ", JSON.stringify(server.address()))
});

app.get('/', function (req, res) {
  console.log('Server received GET')
  res.sendStatus(200);
})

app.use('/public', express.static('static'));

app.use( bodyParser.json() );
app.use( bodyParser.urlencoded({extended: true}) );
app.post('/formHandler', function(req, res) {
  console.log( 'Form Submit Value = ' + req.body.fieldA );
  res.sendStatus(200);
});

(The second line and the last block were added this round)

Now if we load http://127.0.0.1:8000/public/test.html and fill that one-line form with some value, and click [Submit], we should see in the browser window that the URL is changed to http://127.0.0.1:8000/formHandler with content ‘OK’. In the command line console window we should see Form Submit Value = ... with the value we just submitted.

 

REST API & Client Side Dynamic Data

To wrap this up, let’s have the server send some JSON data via a REST API:

var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var server = app.listen(8000, function () {
  console.log("REST API listening on ", JSON.stringify(server.address()))
});

app.get('/', function (req, res) {
  console.log('Server received GET')
  res.sendStatus(200);
})

app.use('/public', express.static('static'));

app.use( bodyParser.json() );
app.use( bodyParser.urlencoded({extended: true}) );
app.post('/formHandler', function(req, res) {
  console.log( 'Form Submit Value = ' + req.body.fieldA );
  res.sendStatus(200);
});

var myObj = {
  name: 'John Doe',
  age: 21,
  interest: ['Chess', 'Music', 'Tennis']
}
app.get('/data', function (req, res) {
  res.setHeader('Content-Type', 'application/json');
  res.send( JSON.stringify(myObj) );
})

(The last block was added this round)

Here we added a (GET) /data handler. Load http://127.0.0.1:8000/data in the browser we should see {"name":"John Doe","age":21,"interest":["Chess","Music","Tennis"]} .

For a really dumb way to do client side dynamic data, we could change static/test.html to the following content:

<script src="https://code.jquery.com/jquery-3.1.0.min.js"
  integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s="
  crossorigin="anonymous"></script>

<form action="/formHandler" method="post">
  <input type="text" name="fieldA" value="">
  <input type="submit">
</form>

Name: <div id='name'></div>
Age: <div id='age'></div>
Interest: <div id='interest'></div>
<script>
  $.ajax({
    url: "http://127.0.0.1:8000/data",
    success: function(data, status, jqXHR) {
      document.getElementById('name').innerHTML=data.name;
      document.getElementById('age').innerHTML=data.age;
      document.getElementById('interest').innerHTML=data.interest;
    },
    error: function(jqXHR, status, errorThrown) {
      console.log(status);
    }
  });
</script>

Here we use jQuery to do a GET on http://127.0.0.1:8000/data, and fill in the placeholders with the retrieved (thus dynamic) value.

Load http://127.0.0.1:8000/public/test.html we should see

Name:
John Doe
Age:
21
Interest:
Chess,Music,Tennis

And the values are really dynamically loaded from the backend!

GitHub Repo

I have put the above code into a simple github repo: https://github.com/nzhong/minimal-web-server-client. There are really just three files

To try it out, you can do the following:

  • git clone https://github.com/nzhong/minimal-web-server-client
  • cd minimal-web-server-client
  • npm install
  • node server

Open up your chrome browser, and load “http://127.0.0.1:8000/public/test.html”







Jul 23, 2016 - Simple Long Polling

Comments

Server Push

Our goal really is to implement server push over HTTP. Long Polling is just one method to achieve this, together with (regular) polling, HTTP streaming, and WebSockets:

(In the order of slowest to fastest)

  • (regular) Polling: client sends AJAX request at regular interval (say every 1 sec), and server responds immediately. Thus the client will be notified with at most (1 second) delay.
  • Long Polling: client sends AJAX request, and server holds on to the request/response until it has an update (push event). When the client receives the push event it sends another AJAX request.
  • HTTP streaming: Server responds with a header with “Transfer Encoding: chunked” and hence the client do not need to initiate a new request immediately.
  • WebSockets: About HTML5 WebSocket

This post only focuses on Long Polling.

Long Polling: Client Side Javascript

The traditional client side javascript code that does long polling is as follows:

(function poll(){
    $.ajax({ 
        url: "server-url", 
        success: function(data){
            // do something with the data
        },
        dataType: "json",
        complete: poll, 
        timeout: 60000 
    });
})();

The above code sends an AJAX request, and when the call is completed (either success, or error, which includes timeout), will fire off another request by recursively calling itself.

In the real world, we may want to differentiate the success and failure cases however. If the server is down for example, the above client code will fire off a million requests non-stop, one after another, since each request will be completed immediately with net::ERR_EMPTY_RESPONSE or net::ERR_CONNECTION_REFUSED. One simple solution is something like the following:

  • If the request is completed successfully, fire off another request immediately.
  • If the request fails with timeout (the timeout is set by client JS), it means there is no push event from server. fire off another request immediately.
  • If the request fails with something other than timeout, wait for a while (for the server to recover), and then fire off another request.

The above logic looks something like this:

(function poll(){
    $.ajax({ 
        url: "server-url",
        success: function(data, status, jqXHR) {
            // do something with the data
            setTimeout( poll, 10 );
        },
        error: function(jqXHR, status, errorThrown) {
            if (status=='timeout') {
                console.log( 'request timed out.' );
                setTimeout( poll, 10 );
            }
            else {
                console.log(status);
                setTimeout( poll, 60000 );
            }
        },
        dataType: "json",
        timeout: 60000
    });
})();

Long Polling: Server Side Java

For this post, for simplicity, we will use embedded Jetty. Before we start, here is a simple routine that I use to simulate a long running process:

    private static SecureRandom secureRandom = new SecureRandom();
    static {
        secureRandom.setSeed( System.currentTimeMillis() );
    }

    private static String waitFor5To15Seconds() {
        long start = System.currentTimeMillis();
        try {
            long sleepTimeInMilli = 5000L + 1000L*secureRandom.nextInt(10);  // 5 - 15 seconds
            Thread.sleep(sleepTimeInMilli);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
        long end = System.currentTimeMillis();
        return "After "+(end-start)+"ms the server responds with ECHO.";
    }

Here we simply take a random number between 5 to 15 seconds, Thread.sleep, and return how long we spent. Please note that this waitFor5To15Seconds() rountine is a BLOCKING call.

Now the simplest embedded Jetty code for a traditional non-async servlet looks like this:

public class TestServer {
    . . . 
    private static String waitFor5To15Seconds() {
        // as above
    }

    public static class BlockingServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            try {
                resp.getWriter().write( waitFor5To15Seconds() );
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws Exception
    {
        Server server = new Server(9000);
        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath("/");
        ServletHolder blockingHolder = context.addServlet(BlockingServlet.class,"/block");
        server.setHandler(context);
        server.start();
        server.join();
    }
}

If you run the above Java code, and in your browser load the URL “http://127.0.0.1:9000/block”, after 5 - 15 seconds the browser will display something like “After 9004ms the server responds with ECHO.”

But this style of servlet:

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            try {
                resp.getWriter().write( waitFor5To15Seconds() );
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

will not work well with Long Polling. Since each servlet handler will make a blocking call to wait for the next push event, and most servlet engines uses a limited thread pool to handle incoming requests, we will face Thread Starvation very soon, with no available threads to handle new incoming requests.

Fortunately Asynchronous Servlets has been introduced since servlet spec 3.0. Instead of waiting for the processing to be finished, the servlet handler simply start another new worker thread, pass along an instance of AsyncContext to the worker thread, and be done. The servlet handler thread is returned to the pool to handle other incoming requests, and with the AsyncContext instance, the new worker thread has everything it needs to complete the request.

The Async version of the above code looks like this:

    public static class EmbeddedAsyncServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            final AsyncContext asyncCtx = req.startAsync();
            asyncCtx.start( new Runnable(){
                public void run()
                {
                    ServletResponse response = asyncCtx.getResponse();
                    try {
                        response.getWriter().write( waitFor5To15Seconds() );
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    asyncCtx.complete();
                }
            });
        }
    }

    public static void main(String[] args) throws Exception
    {
        . . .
        ServletHolder blockingHolder = context.addServlet(BlockingServlet.class,"/block");

        ServletHolder asyncHolder = context.addServlet(EmbeddedAsyncServlet.class,"/async");
        asyncHolder.setAsyncSupported(true);
        . . .
    }

If you want to test this, similar to the blocking version, try loading the URL “http://127.0.0.1:9000/async” in your browser. After 5 - 15 seconds the browser should display something like “After 7003ms the server responds with ECHO.”

GitHub Repo

I have put the above code into a simple github repo: https://github.com/nzhong/simple-long-polling. There are really just two files

To try it out, you can do the following:

  • git clone https://github.com/nzhong/simple-long-polling
  • cd simple-long-polling
  • mvn clean package
  • java -jar target/simple-long-polling-1.0-SNAPSHOT.jar
  • Open up your chrome browser, and load “http://127.0.0.1:9000/static/index.html”
  • Open your Chrome’s Developer Tools -> Console, and see the messages.

I tweaked the client side timeout a bit, just to add some varieties. The server side will wait randomly between 5-15 seconds, and the client side make its AJAX requests timeout = 12 seconds. So in the Chrome console you should see something like

  • After 7002ms the server responds with ECHO.
  • After 6004ms the server responds with ECHO.
  • 2 request timed out.
  • After 9003ms the server responds with ECHO.

The server side AsyncServer.java was also tweaked a bit, just so that the program can listen to three URLs:

  • http://127.0.0.1:9000/block
  • http://127.0.0.1:9000/async
  • http://127.0.0.1:9000/static/*.html

Source Material

The following links, among others, are what educated me about long polling: