Spring 事务管理是否与 Spring WebFlux 一起使用?
Does Spring Transaction Management Work with Spring WebFlux?
Spring 对 RDBMS 事务管理的支持是否也适用于 Spring WebFlux?
例如,假设配置正确,用 @Transactional
注释注释的方法是否会使用 Spring 事务管理器并在发生错误时回滚事务?
如果事务管理确实有效,必须有一个 @Transactional
方法实际上 throw
和异常,或者 Mono
或 Flux
return 类型必须发出一个错误信号?
我知道 JDBC 本质上是阻塞的,因此任何 JDBC 操作都必须从阻塞过渡到反应,反之亦然。
Spring 事务管理器通过使用 ThreadLocal
(对吗?)来工作,我假设它不会在 Reactor 环境中工作,因为 Reactor 在线程和单线程方面很节俭可以在第一个工作单元等待 I/O 时将一个工作单元换成另一个工作单元。我知道 Reactor 有 Context
对象,它在概念上类似于 ThreadLocal
(对吧?),但我还没有看到任何文档提到事务使用它。此外,事务中发生的所有 JDBC 操作必须使用相同的 Connection
,这在反应式上下文中可能很难做到。
我的组织有使用 WebFlux 和 Cassandra 的经验,但 Cassandra 有一个原生的反应驱动程序。
谢谢!
编辑:此答案不再适用于 Spring 框架版本 5.2 M2 及更高版本。看到这个 article。感谢@Florent Dupont 提到这一点。
据我所知,Spring 标准事务管理不适用于 WebFlux。
使用@Transactional
将不起作用,因为当调用带注释的方法时,事务机制会将事务状态保存在调用线程的ThreadLocal
中。正如您自己所说,这是行不通的。它阻止并共享状态。
但是,您可以使用 .runOn(Schedulers.parallel())
将阻塞代码发送到另一个线程。这样你就可以拥有一个带有可阻塞线程的线程池,你可以将其配置为与你的数据库连接池具有相同的大小。
但即便如此,由于线程池重用线程的方式,您仍然不能依赖 @Transactional
。在标准的 Servlet 架构中,每个 HTTP 请求都有一个线程。当响应被发回时,线程停止,从而关闭事务。但在这种情况下,Reactor 调度程序不会关闭线程并将它们重新用于其他事件。所以即使你可以阻止你仍然有和以前一样的问题。
您确实有提到的 Context
选项,我认为这适用于 Mono
。我不确定它是否适用于 Flux
(我认为 Flux 中的所有事件共享相同的上下文,这是你不想要的)。
另一种选择是使用带有 T1
作为业务对象和 T2
事务上下文的 Touple2。不过,我不推荐这样做,因为您将业务逻辑与技术内容混合在一起,这会使事情变得过于复杂。
我最好的选择是自己进行 transaction/connection 管理:
- 获取数据库连接
- 打开TX
- 做阻塞 IO 的事情
- 关闭交易
- Close/release 数据库连接
全部在阻塞线程上的一个代码块中。
这样会更安全(无泄漏)并且更容易理解。此外,由于基本上所有事情都是您自己完成的,因此您可以选择最适合您的场景的错误处理方式。
@Transactional
现在对 Spring 反应有效。
这里就不赘述了。一切都在这个 post 中解释:https://spring.io/blog/2019/05/16/reactive-transactions-with-spring.
Spring 对 RDBMS 事务管理的支持是否也适用于 Spring WebFlux?
例如,假设配置正确,用 @Transactional
注释注释的方法是否会使用 Spring 事务管理器并在发生错误时回滚事务?
如果事务管理确实有效,必须有一个 @Transactional
方法实际上 throw
和异常,或者 Mono
或 Flux
return 类型必须发出一个错误信号?
我知道 JDBC 本质上是阻塞的,因此任何 JDBC 操作都必须从阻塞过渡到反应,反之亦然。
Spring 事务管理器通过使用 ThreadLocal
(对吗?)来工作,我假设它不会在 Reactor 环境中工作,因为 Reactor 在线程和单线程方面很节俭可以在第一个工作单元等待 I/O 时将一个工作单元换成另一个工作单元。我知道 Reactor 有 Context
对象,它在概念上类似于 ThreadLocal
(对吧?),但我还没有看到任何文档提到事务使用它。此外,事务中发生的所有 JDBC 操作必须使用相同的 Connection
,这在反应式上下文中可能很难做到。
我的组织有使用 WebFlux 和 Cassandra 的经验,但 Cassandra 有一个原生的反应驱动程序。
谢谢!
编辑:此答案不再适用于 Spring 框架版本 5.2 M2 及更高版本。看到这个 article。感谢@Florent Dupont 提到这一点。
据我所知,Spring 标准事务管理不适用于 WebFlux。
使用@Transactional
将不起作用,因为当调用带注释的方法时,事务机制会将事务状态保存在调用线程的ThreadLocal
中。正如您自己所说,这是行不通的。它阻止并共享状态。
但是,您可以使用 .runOn(Schedulers.parallel())
将阻塞代码发送到另一个线程。这样你就可以拥有一个带有可阻塞线程的线程池,你可以将其配置为与你的数据库连接池具有相同的大小。
但即便如此,由于线程池重用线程的方式,您仍然不能依赖 @Transactional
。在标准的 Servlet 架构中,每个 HTTP 请求都有一个线程。当响应被发回时,线程停止,从而关闭事务。但在这种情况下,Reactor 调度程序不会关闭线程并将它们重新用于其他事件。所以即使你可以阻止你仍然有和以前一样的问题。
您确实有提到的 Context
选项,我认为这适用于 Mono
。我不确定它是否适用于 Flux
(我认为 Flux 中的所有事件共享相同的上下文,这是你不想要的)。
另一种选择是使用带有 T1
作为业务对象和 T2
事务上下文的 Touple2。不过,我不推荐这样做,因为您将业务逻辑与技术内容混合在一起,这会使事情变得过于复杂。
我最好的选择是自己进行 transaction/connection 管理:
- 获取数据库连接
- 打开TX
- 做阻塞 IO 的事情
- 关闭交易
- Close/release 数据库连接
全部在阻塞线程上的一个代码块中。
这样会更安全(无泄漏)并且更容易理解。此外,由于基本上所有事情都是您自己完成的,因此您可以选择最适合您的场景的错误处理方式。
@Transactional
现在对 Spring 反应有效。
这里就不赘述了。一切都在这个 post 中解释:https://spring.io/blog/2019/05/16/reactive-transactions-with-spring.