spring webflux 如何在反应世界中管理顺序业务逻辑代码

spring webflux how to manage sequential business logic code in reactive world

这种方法是否反应友好?

我有一个响应式控制器 "save" 方法调用 myService.save(请求)。

服务层需要:

  1. jdbc 保存(在另一个调度程序上,因为代码正在阻塞),
  2. 生成模板字符串(在另一个调度程序上),
  3. 发送电子邮件(在另一个调度程序上),
  4. 最后return将实体保存到controller层

我无法将我所有的调用都链接在一个管道中,或者我不知道如何实现这一点,因为我想在我这样做后立即发回丢失的 (1) ....flatMap( templateService::generateStringTemplate) 例如。

所以我改为在 (1) 中触发我的子操作。

我应该如何处理这个问题,还是有一种巧妙的方法可以在一个管道中完成?

下面的代码可以支持这个问题。谢谢。

Controller层调用的服务

    public Mono<Prospect> save(final Prospect prospect) {

    return Mono.fromCallable(
            () -> {
                Prospect savedProspect = transactionTemplate.execute(status -> prospectRepository.save(prospect));

                templateService.generateProspectSubscription(savedProspect)
                        .map(t ->
                                EmailPostRequest.builder()
                                       ...
                                        .build())
                        .flatMap(emailService::send)
                        .subscribe();

                return savedProspect;
            })
            .subscribeOn(jdbcScheduler);

}

模板服务

public Mono<String> generateProspectSubscription(final Prospect prospect) {        
    return Mono.fromCallable(
            () -> {
                Map<String, Object> model = new HashMap<>();
               ...

                Template t = freemarkerConfig.getTemplate(WELCOME_EN_FTL);
                String html = FreeMarkerTemplateUtils.processTemplateIntoString(t, model);
                return html;
            }
    ).subscribeOn(freemarkerScheduler);
}

电子邮件服务

 public Mono<Void> send(final EmailPostRequest e) {

    return Mono.fromCallable(
            () -> {
                MimeMessage message = emailSender.createMimeMessage();
                MimeMessageHelper mimeHelper = new MimeMessageHelper(message,
                        MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
                        StandardCharsets.UTF_8.name());


                mimeHelper.setTo(e.getTo());
                mimeHelper.setText(e.getText(), true);
                mimeHelper.setSubject(e.getSubject());
                mimeHelper.setFrom(new InternetAddress(e.getFrom(), e.getPersonal()));

                emailSender.send(message);

                return Mono.empty();
            }
    ).subscribeOn(emailScheduler).then();
}

已编辑服务 我认为这个版本的服务层更干净,但欢迎任何评论

    public Mono<Prospect> save(final Prospect prospect) {

    return Mono.fromCallable(
            () -> transactionTemplate.execute(status -> prospectRepository.save(prospect)))
            .subscribeOn(jdbcScheduler)
            .flatMap(savedProspect -> {
                        templateService.generateProspectSubscription(savedProspect)
                                .map(t ->
                                        EmailPostRequest.builder()
                                                ...
                                                .build())
                                .flatMap(emailService::send)
                                .subscribe();

                        return Mono.just(savedProspect);
                    }
            );
}

这种方法不是响应式友好的,因为您 100% 包装了阻塞库。 对于这个用例,您无法真正看到反应式运行时的好处,而且您的应用程序的性能很可能比阻塞式运行时更差。

如果您的主要动机是表现,那么这可能会适得其反。 将大量阻塞 I/O 工作卸载到专门的 Schedulers 会在内存(创建更多线程)和 CPU(上下文切换)方面产生运行时成本。如果性能和可伸缩性是您最关心的问题,那么切换到 Spring MVC 并在适当的地方利用 Flux/Mono 支持,甚至调用 block() 运算符可能更好适合。

如果您的主要动机是使用特定的库,例如 Spring Framework 的 WebClient 和 Spring MVC,那么您最好在所选中使用 .block() 运算符放置而不是包装和安排一切。