用于简单 RESTful 调用的反应式 R2DBC
Reactive R2DBC for simple RESTful calls
由于 R2DBC 是反应式且非阻塞的,我想了解在简单的 RESTful CRUD 服务中使用 R2DBC 的好处
假设 spring 引导应用程序使用下面的存储库公开 RESTful 服务
public interface CustomerRepository extends ReactiveCrudRepository<Customer, Long> {
@Query("SELECT * FROM customer WHERE last_name = :lastname")
Flux<Customer> findByLastName(String lastName);
}
此存储库是从服务调用的,结果需要在返回到控制器之前在服务中进行转换。
Flux<Customer> customers = repository.findAll();
为了访问完整的客户列表,我需要在 Flux 上调用 blockLast()
,这使其阻塞并破坏了使用反应组件的目的
这是否意味着在这个简单示例中使用 R2DBC 没有任何好处?我错过了什么吗?
Flux 能否仅用于在不同线程中处理 Flux 集合的异步订阅?
In order to access the complete list of customers , I need to invoke blockLast() on the Flux which makes it blocking and defeats the purpose of using reactive components
如果你想真正获得对 List<Customer>
的引用,你只需要调用 blockLast()
- 但正如你所说,如果你这样做,你将失去所有反应优势。 (您唯一真正应该考虑阻止恕我直言的情况是,如果您正在迁移到反应式系统,并将反应式库放在适当的位置,但尚未准备好使整个系统反应式。)
如果您只想立即访问完整的客户列表,您可以明智地调用 collectList()
以获得 Mono<List<Customer>
。这样你就处于反应式上下文中,但在任何反应式运算符中你都可以使用整个列表。
这里唯一要注意的是内存占用——如果你只是按原样处理一个 Flux<Customer>
,那么你永远不需要在内存中存储它们的一组,所以它真的不需要不管有多少(甚至可以是无限的。)如果你先把它收集到一个列表中,那么所有这些客户都必须同时存储在内存中。
这是否是一个问题取决于您的用例。如果您谈论的是 10 个左右的客户,则完全没有问题。如果您谈论的是数十亿,那么这很可能不是一个明智的解决方案。
- 由于该方法返回一个
Flux
,它 returns 一个可以在未来某个时间完成的承诺。
- 调用此方法的线程不应阻塞——这就是反应式的全部意义所在。如果使用
.blockLast()
,就会使调用线程阻塞。正如 Michael 在这个回答中所说,只有当您将反应式代码和非反应式代码集成在一起时才应该这样做。
- 这是一张解释异步如何工作的图表。计算是工作(反应式,
CompletableFuture
,等等)。希望这能消除您的疑虑;
如您在图中所见,如果您这样做 .blockLast()
,您将失去真正的非阻塞。理想情况下,调用线程应立即获得空闲,以便它可以执行其他工作。
- 一个正确编写的反应式应用程序将是一个单链,订阅发生在最上层。整个应用程序应该是一个异步管道。您应该使用像
BlockHound
这样的应用程序来检查您的线程是否在任何地方阻塞。
“Flux 能否仅用于在不同线程中处理 Flux 集合的异步订阅?” - Flux 只不过是一个承诺,表示它可以在未来某个时间完成。如图所示,回调将在异步的线程上执行。计算完成。您可以使用 .publishOn()
.
切换此线程
由于 R2DBC 是反应式且非阻塞的,我想了解在简单的 RESTful CRUD 服务中使用 R2DBC 的好处
假设 spring 引导应用程序使用下面的存储库公开 RESTful 服务
public interface CustomerRepository extends ReactiveCrudRepository<Customer, Long> {
@Query("SELECT * FROM customer WHERE last_name = :lastname")
Flux<Customer> findByLastName(String lastName);
}
此存储库是从服务调用的,结果需要在返回到控制器之前在服务中进行转换。
Flux<Customer> customers = repository.findAll();
为了访问完整的客户列表,我需要在 Flux 上调用 blockLast()
,这使其阻塞并破坏了使用反应组件的目的
这是否意味着在这个简单示例中使用 R2DBC 没有任何好处?我错过了什么吗?
Flux 能否仅用于在不同线程中处理 Flux 集合的异步订阅?
In order to access the complete list of customers , I need to invoke blockLast() on the Flux which makes it blocking and defeats the purpose of using reactive components
如果你想真正获得对 List<Customer>
的引用,你只需要调用 blockLast()
- 但正如你所说,如果你这样做,你将失去所有反应优势。 (您唯一真正应该考虑阻止恕我直言的情况是,如果您正在迁移到反应式系统,并将反应式库放在适当的位置,但尚未准备好使整个系统反应式。)
如果您只想立即访问完整的客户列表,您可以明智地调用 collectList()
以获得 Mono<List<Customer>
。这样你就处于反应式上下文中,但在任何反应式运算符中你都可以使用整个列表。
这里唯一要注意的是内存占用——如果你只是按原样处理一个 Flux<Customer>
,那么你永远不需要在内存中存储它们的一组,所以它真的不需要不管有多少(甚至可以是无限的。)如果你先把它收集到一个列表中,那么所有这些客户都必须同时存储在内存中。
这是否是一个问题取决于您的用例。如果您谈论的是 10 个左右的客户,则完全没有问题。如果您谈论的是数十亿,那么这很可能不是一个明智的解决方案。
- 由于该方法返回一个
Flux
,它 returns 一个可以在未来某个时间完成的承诺。 - 调用此方法的线程不应阻塞——这就是反应式的全部意义所在。如果使用
.blockLast()
,就会使调用线程阻塞。正如 Michael 在这个回答中所说,只有当您将反应式代码和非反应式代码集成在一起时才应该这样做。 - 这是一张解释异步如何工作的图表。计算是工作(反应式,
CompletableFuture
,等等)。希望这能消除您的疑虑;
如您在图中所见,如果您这样做 .blockLast()
,您将失去真正的非阻塞。理想情况下,调用线程应立即获得空闲,以便它可以执行其他工作。
- 一个正确编写的反应式应用程序将是一个单链,订阅发生在最上层。整个应用程序应该是一个异步管道。您应该使用像
BlockHound
这样的应用程序来检查您的线程是否在任何地方阻塞。
“Flux 能否仅用于在不同线程中处理 Flux 集合的异步订阅?” - Flux 只不过是一个承诺,表示它可以在未来某个时间完成。如图所示,回调将在异步的线程上执行。计算完成。您可以使用 .publishOn()
.