spring webflux 如何在反应世界中管理顺序业务逻辑代码
spring webflux how to manage sequential business logic code in reactive world
这种方法是否反应友好?
我有一个响应式控制器 "save" 方法调用 myService.save(请求)。
服务层需要:
- jdbc 保存(在另一个调度程序上,因为代码正在阻塞),
- 生成模板字符串(在另一个调度程序上),
- 发送电子邮件(在另一个调度程序上),
- 最后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()
运算符放置而不是包装和安排一切。
这种方法是否反应友好?
我有一个响应式控制器 "save" 方法调用 myService.save(请求)。
服务层需要:
- jdbc 保存(在另一个调度程序上,因为代码正在阻塞),
- 生成模板字符串(在另一个调度程序上),
- 发送电子邮件(在另一个调度程序上),
- 最后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()
运算符放置而不是包装和安排一切。