第 9 章 被动访问数据 反应式访问数据
本作品已使用人工智能进行翻译。欢迎您提供反馈和意见:translation-feedback@oreilly.com
在第5章中,我们解释了在应用程序中使用阻塞式I/O的可扩展性和鲁棒性问题。 本章将重点讨论与数据库的交互,以及Quarkus如何确保应用程序栈的数据层也能异步并利用非阻塞式I/O。
数据访问的问题
访问关系型数据之前需要在与数据库通信时阻塞 I/O。 正如第 5 章中所讨论的,我们希望在堆栈的任何层级避免应用程序中出现阻塞 I/O。 与数据库交互通常需要花费大量时间才能完成,这取决于所涉及的记录数量,因此阻塞 I/O 访问数据库会对我们的应用程序造成更大的影响!我们是什么意思? 假设我们开发了一个小型数据库应用程序;多年来,我们都开发过许多这样的应用程序。 我们通常将它们称为CRUD应用程序,因为它们为数据库中的记录提供创建、读取、更新和删除操作。
在我们的 API 中,每个暴露的端点都需要与数据库交互。 我们将忽略缓存以及缓存在某些情况下如何减少对数据库的请求次数。 在每个端点方法调用数据库时,每次执行都会执行阻塞 I/O,从而降低并发性。
为什么我们在与数据库交互时不得不使用阻塞式 I/O? 用于与数据库交互的 API,如开放数据库连接(ODBC)和 Java 数据库连接(JDBC),都是采用同步和阻塞方式设计的。 多年后出现的 Java 持久性 API(JPA),虽然将对象关系映射(ORM)围绕一个通用 API 进行了整合,但其设计仍然基于 JDBC 现有的同步和阻塞行为。
如果不对数据访问采用反应式编程方法,整个应用程序堆栈就不可能真正实现反应式。 应用程序只能在一定程度上实现反应式。 虽然这样做仍然有益,但并发性和吞吐量仍然无法充分发挥其并发性潜力。
在解释使用 JDBC 和 JPA 访问数据库如何不是反应式的,因而是阻塞式的时,我们说了很多,但这种访问是什么样子的呢? 正如我们在"势在必行模型 "中看到的应用程序逻辑,数据库交互也有类似的问题,如图 9-1 所示。
图 9-1. 阻止数据库客户端
当我们通过 JDBC 或更高抽象的 JPA 与数据库通信时,JDBC 驱动程序会使用请求-响应交互方式。 不过,如图 9-1 所示,JDBC 驱动程序会阻塞线程,直到收到任何响应为止。 这种阻塞方式会占用每个数据库交互的整个线程。 根据您配置数据库连接池的方式,应用程序有可能在达到最大数据库连接数之前就耗尽了线程。
在处理大量需要搜索或检索的数据库记录时,以及在我们的应用程序和数据库之间存在网络延迟时,很可能会出现线程饥饿或资源利用率的问题。
与关系数据库的非阻塞交互
随着各项目最近的工作,如 PostgreSQL、MySQL、IBM Db2、Oracle 和 Microsoft SQL Server 的Vert.x 客户端 API,Java 应用程序现在能够以非阻塞 I/O 的异步方式与数据库交互。
与 JDBC 相比,这些新客户端有什么不同?使用非阻塞数据库客户端时,我们可以避免阻塞线程,如图 9-2 所示。
图 9-2. 非阻塞数据库客户端
此外,数据库客户端现在可以在 I/O 线程上执行,而不是在工作线程上执行。 现在,使用这些新的非阻塞客户端还能带来一个复合优势:减少应用程序可能需要的工作线程数量,因为与这些新客户端的数据库通信可以与任何反应式应用程序代码在同一线程上进行! ...
Become an O’Reilly member and get unlimited access to this title plus top books and audiobooks from O’Reilly and nearly 200 top publishers, thousands of courses curated by job role, 150+ live events each month,
and much more.
Read now
Unlock full access