Pasha
Pasha

Reputation: 1912

How to implement Long Polling REST endpoint in Spring Boot app?

Would you be so kind as to share any up-to-date manual or explain here how to implement a REST Long Polling endpoint with the latest Spring (Spring Boot)?

Everything that I've found by this time is quite out-dated and was issued a couple of years ago.

So, I've raised a question is Long Polling still a good approach? I know it's used in chess.com

Upvotes: 21

Views: 31242

Answers (2)

ahmetknk
ahmetknk

Reputation: 348

Note that when using Spring MVC stack, it is impossible to create an efficient implementation of long polling without utilizing NIO components such as those found in Reactive Stack.

The reason behind blocking implementations being so inefficient is that they block the thread they are running on even though you spawn a new thread. When you spawn a new thread like in the answer of @GolamMazid Sajib, you just change the thread being blocked. Although the worker thread is free now, the background thread is being blocked.

Assume you have created a long polling solution as provided by the @GolamMazid Sajib:

@GetMapping("/test")
DeferredResult<String> test(){
    long timeOutInMilliSec = 100000L;
    String timeOutResp = "Time Out.";
    DeferredResult<String> deferredResult = new DeferredResult<>(timeOutInMilliSec, timeOutResp);
    CompletableFuture.runAsync(()->{
        try {
            //Long polling task; if task is not completed within 100s, timeout response returned for this request
            TimeUnit.SECONDS.sleep(10);
            //set result after completing task to return response to client
            deferredResult.setResult("Task Finished");
        }catch (Exception ex){
        }
    });
    return deferredResult;
}

CompletableFuture.runAsync method changes the execution context to a thread pool.

Now, assume that you receive 1000 requests simultaneously. Now, each worker thread processes the 1000 requests instantly. However, the background thread pool now becomes the bottleneck. Assume there are 50 threads in the pool. Your throughput becomes 5 requests/second. Moreover, the requests take much much more than 10 seconds since they have to wait for background threads to finish. Most of the requests will be timed out due to the 100 seconds timeout.

You should consider implementing a NIO solution if you want to implement long polling.

Upvotes: 0

GolamMazid Sajib
GolamMazid Sajib

Reputation: 9437

For long polling requests, you can use DeferredResult. When you return a DeferredResult response, the request thread will be free and the request will be handled by a worker thread. Here is one example:

@GetMapping("/test")
DeferredResult<String> test(){
    long timeOutInMilliSec = 100000L;
    String timeOutResp = "Time Out.";
    DeferredResult<String> deferredResult = new DeferredResult<>(timeOutInMilliSec, timeOutResp);
    CompletableFuture.runAsync(()->{
        try {
            //Long polling task; if task is not completed within 100s, timeout response returned for this request
            TimeUnit.SECONDS.sleep(10);
            //set result after completing task to return response to client
            deferredResult.setResult("Task Finished");
        }catch (Exception ex){
        }
    });
    return deferredResult;
}

This request demonstrates providing a response after waiting 10s. If you set sleep(100) or longer, you will get a timeout response.

Check this out for more options.

Upvotes: 27

Related Questions