Inside Reactive Spring [Part 1]

Arika Saputro
3 min readJul 24, 2022

--

Reactive programming solve 2 problem for us, memory and performance. When we use traditional way of developing application with Spring MVC, we deploy that application on servlet container and the servlet container will use dedicated thread pool to handle the http request and each request will have thread assigned that handle the entire lifecycle of the request (thread per request). This means that application will only handle concurrent requests that equals to the size of the thread pool. The thread pool size is configurable, but the higher thread pool size, the higher memory that will be consume.

How reactive thread work? Let’s dive into this!

Reactive spring will use Reactor Netty as a default embedded server. Netty will create a bunch of worker threads for request processing that related to the number of CPU core. Let’s check it.

First I will create simple reactive spring rest service that will return flux object.

@Builder
@Data
@AllArgsConstructor
@ToString
public class User {
private String id;
private String username;
}
@Service
public class UserService {
public Flux<User> constructUser(int size){
return Flux.range(0, size)
.map(n->new User(String.valueOf(n), "user-"+n));
}
}
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Autowired
UserService userService;

@GetMapping("/getUserWithSize/{size}")
public Flux<User> getUserWithSize(@PathVariable int size) {
return userService.constructUser(size).log();
}
}

Lets hit the service and see the flux log.

Here the flux log, the onSubscribe, onNext, and onComplete run on reactor-http-nio-2 thread, same thread.

Let’s check the number of worker thread that handle the request processing. I will hit it concurrent and i will print the number of CPU core.

@PostConstruct
public void checkCPUCore() {
log.info("CPU Core : {}", Runtime.getRuntime().availableProcessors());
}

Here the result, netty will create the reactor-http-nio thread with size of 8 that the same number of CPU core of machine that I use.

Lets dive deeper on the thread model, I will create reactive user repository that will get user data from mongo db.

@Repository
public interface UserRepository extends ReactiveMongoRepository<User, String>{
}
@Autowired
UserRepository userRepository;

public Flux<User> getAllUser(){
return this.userRepository.findAll();
}
@GetMapping("/getUserFromDB")
public Flux<User> getUserFromDB() {
return userService.getAllUser().log();
}

Lets hit the service and see the flux log.

Here the result, it seems the reqeust processing and the db I/O processing handle by different pool.

Let’s try hit it with 100 concurrent and see what happen.

Let’s see the result, reactor-http-nio-1 handle the request 97 and after that the db i/o completed. Time between handling request 97 and completed, reactor-http-nio-1 also handle the request 98, it does not block the thread while waiting for a result. The idea of non-blocking threads is the main concept around reactive programming, which allows us to handle many requests with the small numbers of threads.

I hope this will be helpful to you. Cheers!

--

--