Rafael Fogel
Rafael Fogel

Reputation: 33

Spring Webflux / Reactor: reactor-http-nio vs boundedElastic

I'm a bit confused on some reactor concepts on what thread model each request falls on. I read https://projectreactor.io/docs/core/release/reference but it's still not clear. Let's see an example:

@Autowired
UserRepository userRepository;

@GetMapping
Flux<User> findAll() {
    log.info("findAll request arrived");
    final Flux<User> defer = Flux.defer(() -> {
          return Flux.fromIterable(userRepository.findAll());
    });
    return defer;
}

log: [boundedElastic-4] - INFO - findAll request arrived

A GET method is executed inside Schedulers.boundedElastic thread pool(for I/O-bound work as per documentation)

@PostMapping
Mono<User> save(@RequestBody User user) {
    log.info("save request arrived");
    final Mono<User> newUser = Mono.fromCallable(() -> {
         final User userMono = userRepository.save(user);
          log.info("user saved!");
          return userMono;
    });
    return newUser.subscribeOn(Schedulers.boundedElastic());
}

log: [reactor-http-nio-6] - INFO - save request arrived

A POST method falls on http-nio thread pool.

@PostMapping("/test")
Mono<User> test() {
    log.info("test");
    return Mono.just(new User());
}

A POST without body also falls on Schedulers.boundedElastic.

@Bean
public ReactiveWebServerFactory reactiveWebServerFactory() {
    NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory();
    final ReactorResourceFactory reactorResourceFactory = new ReactorResourceFactory();
    reactorResourceFactory.setLoopResources(LoopResources.create("http-nio", 4, true));
    reactorResourceFactory.setUseGlobalResources(false);
    factory.setResourceFactory(reactorResourceFactory);
    factory.setPort(8080);
    return factory;
}

This is how I can configure the http-nio thread pool.

So, my questions are:

  1. Why POST methods with the body are treated by http-nio thread pool?
  2. This http-nio thread pool is supposed to be a smaller thread pool, so why POST methods with the body(which I think are been considered blocking code) falls on them?
  3. Make sense to return newUser.subscribeOn(Schedulers.boundedElastic()); or it is supposed to stay on same thread?

Upvotes: 3

Views: 4735

Answers (1)

ZZ 5
ZZ 5

Reputation: 1954

  1. Because I/O operations like reading/saving something to database or other services should happen on different thread pool. If your repository is reactive then you could see that it operates on different pool, returning http-nio thread to the pool. The same goes for WebClient. If you are using blocking code that's wrapped in Reactor API then you have to make sure that will run on different thread pool.
  2. It depends. From what I can see your repository is not reactive, since it returns User instead of Mono<User. Then it makes sense to use different thread pool so you won't block the http-nio thread. However to make sure that the execution thread will be switched you have to use flatMap so the code would look like:
    @PostMapping
    Mono<User> save(@RequestBody User user) {
        log.info("save request arrived");
        return Mono.just(user)
                .flatMap(user -> saveUser(user));
    }
    
    private Mono<User> saveUser(User user) {
        return Mono.fromCallable(() -> {
            final User userMono = userRepository.save(user);
            log.info("user saved!");
            return userMono;
        }).subscribeOn(Schedulers.boundedElastic());
    }

Also it would be good idea to use Schedulers backed by thread pools that you can control and monitor. IMHO a rule of a thumb is to use a dedicated thread pool per resource. So for instance 1 thread pool for Postgres DB, 1 thread pool for google API (REST calls) and 1 thread pool for GitHub API. Why's that? If any of those resources will have problems (e.g. it will be unavailable for certain amount of time) then code paths ran on other thread pools won't be blocked and your application will be operating, at least for some code paths.

Upvotes: 3

Related Questions