RestController

Synchronous API

To create an API, we can do as below:

@RestController  
@RequestMapping("/api")  
public class ApiController {
	@GetMapping(value = "/hello", produces = MediaType.APPLICATION_JSON_VALUE)
	public String helloWorld() {
	  System.out.println("Processing a request");
	  Thread.sleep(5000);
	  return "Hello, world!";
	}
}

This will create an api endpoint at localhost:8080/api/hello.

The above code is synchronous which means when a thread receives a request, it cannot receive another request until it finish processing the current request.

[!Note]
By default, Tomcat supports 200 concurrent threads so this might not be obvious. However, if you limit the thread to 1 and call the api, it will be more obvious.

Put the following line in application.properties and call the api:

# Set Tomcat thread pool size to 1  
server.tomcat.threads.max=1  
server.tomcat.threads.min-spare=1

Then you will see a delay when calling 2 GET in the same time

Asynchronous

To solve that problem, we can setup our controller asynchronously. As a result, tomcat will still take as many as possible but delegate the process of generating response to a different thread.

For example, for the code above, we could convert to

@GetMapping(value = "/hello", produces = MediaType.APPLICATION_JSON_VALUE)
public Callable<String> helloWorld() throws InterruptedException {
	return () -> {
		  System.out.println("Processing a request");
		  Thread.sleep(5000);
		  return "Hello, world!";
	};
}

By adding Callable<String> we convert this into an asynchronous call and it will be executed into a different thread.sleep

As a result, although our tomcat only takes 1 thread. It will take a request still and a different thread that's not tomcat will process this request and return Hello, world!.

On the client side, there is nothing that's need to be done, the client will just receive Hello, world doesn't matter how many time concurrently we call it.