簡體   English   中英

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

[英]Spring Webflux / Reactor: reactor-http-nio vs boundedElastic

我對每個請求落在哪個線程 model 上的一些反應堆概念有點困惑。 我讀了 https://projectreactor.io/docs/core/release/reference但還是不清楚。 讓我們看一個例子:

@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;
}

日志:[boundedElastic-4] - 信息 - findAll 請求到達

GET 方法在 Schedulers.boundedElastic 線程池中執行(根據文檔用於 I/O 綁定工作)

@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());
}

日志:[reactor-http-nio-6] - 信息 - 保存請求到達

POST 方法落在http-nio線程池上。

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

沒有正文的 POST 也會落在 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;
}

這就是我配置http-nio線程池的方式。

所以,我的問題是:

  1. 為什么帶有正文的 POST 方法由http-nio線程池處理?
  2. 這個http-nio線程池應該是一個較小的線程池,那么為什么帶有主體的 POST 方法(我認為被認為是阻塞代碼)落在他們身上?
  3. 返回 newUser.subscribeOn(Schedulers.boundedElastic());是有意義的或者它應該留在同一個線程上?
  1. 因為像讀取/保存某些東西到數據庫或其他服務這樣的 I/O 操作應該發生在不同的線程池中。 如果您的存儲庫是反應式的,那么您可以看到它在不同的池上運行,將http-nio線程返回到池中。 WebClient也是如此。 如果您使用的是包裝在 Reactor API 中的阻塞代碼,那么您必須確保它將在不同的線程池上運行。
  2. 這取決於。 據我所知,您的存儲庫不是反應性的,因為它返回User而不是Mono<User 然后使用不同的線程池是有意義的,這樣你就不會阻塞http-nio線程。 但是,為了確保執行線程將被切換,您必須使用 flatMap 以便代碼如下所示:
    @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());
    }

此外,使用由您可以控制和監視的線程池支持的調度程序也是個好主意。 恕我直言,一個經驗法則是為每個資源使用一個專用線程池。 因此,例如 1 個用於 Postgres DB 的線程池,1 個用於 google API(REST 調用)的線程池和 1 個用於 GitHub API 的線程池。這是為什么呢? 如果這些資源中的任何一個出現問題(例如,它將在一定時間內不可用),那么在其他線程池上運行的代碼路徑將不會被阻塞,您的應用程序將繼續運行,至少對於某些代碼路徑而言。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM