连接池和实例扩展

jub*_*bin 2 oracle spring database-connection cloud-foundry spring-boot

注意:这是一个与设计相关的问题,我找不到令人满意的答案。所以才来这里问的。

我有一个部署在云(Cloud Foundry)中的 Spring Boot 应用程序。该应用程序连接到 Oracle 数据库以检索数据。应用程序使用连接池(HikariCp)来维护与数据库的连接。假设连接数设置为 5。现在应用程序能够根据负载自动扩展。所有实例将共享同一个数据库。在任何时刻,同一应用程序可能有 50 个实例在运行,这意味着数据库连接总数将为 250 个(即 5 * 50)。

现在假设数据库只能处理 100 个并发连接。在当前场景中,20 个实例将用完 100 个可用连接。如果接下来的 30 个实例尝试连接到数据库会发生什么?如果这是设计问题,如何避免?

请注意,为简单起见,问题中提供的数字是假设的。实际数字要高得多。

gly*_*ing 6

比方说:

  • 可用数据库连接数 =X
  • 应用程序的并发实例数 =Y
  • 应用程序每个实例中数据库连接池的最大大小 =X / Y

这有点简单,因为您可能希望能够从其他客户端(例如支持工具)连接到数据库,因此也许更安全的公式是(X * 0.95) / Y.

现在,您已经确保您的应用程序层不会遇到“不存在数据库连接”的问题。但是(X * 0.95) / Y,如果是 25 个,并且您的应用程序有超过 25 个并发请求同时需要数据库连接,那么其中一些请求在尝试获取数据库连接时将遇到延迟,并且如果这些延迟超过配置超时,它们将导致请求失败。

如果您可以限制应用程序中的吞吐量,以便您永远不会有超过(X * 0.95) / Y并发的“获取数据库连接”请求,那么问题就消失了。但是,当然,这通常是不现实的(事实上,少即是多……告诉你的客户停止与你交谈通常是一个奇怪的信号)。这给我们带来了问题的关键:

现在,应用程序能够根据负载自动扩展。

向上扩展并不是免费的。如果您希望在处理N并发请求时具有与处理100000N并发请求时相同的响应能力,那么就必须做出一些牺牲;您必须扩大这些请求所需的资源。因此,如果他们使用数据库连接,那么数据库支持的并发连接数量将必须增加。如果服务器端资源无法与客户端使用量成比例增长,那么您需要某种形式的背压,或者需要仔细管理服务器端资源。管理服务器端资源的一种常见方法是......

  • 使您的服务非阻塞,即将每个客户端请求委托给线程池,并通过服务中的回调响应客户端(Spring 通过 DeferredResult 或其异步框架或其 RX 集成来促进这一点)
  • 配置服务器端资源(例如数据库允许的最大可用连接数),以根据服务实例的客户端请求线程池的总大小匹配服务的最大吞吐量

客户端请求线程池限制每个服务实例中当前活动请求的数量,但不限制客户端可以提交的请求数量。这种方法允许服务向上扩展(达到由跨所有服务实例的客户端请求线程池的大小表示的限制),并且这样做允许服务所有者保护资源(例如他们的数据库)免于过载。由于所有客户端请求都被接受(并委托给客户端请求线程池),因此客户端请求永远不会被拒绝,因此从他们的角度来看,好像扩展是无缝的。

这种设计通过服务实例集群上的负载均衡器进一步增强,该负载均衡器在它们之间分配流量(循环,甚至通过某种机制,每个节点报告其“繁忙度”,并使用该反馈来指导负载均衡器的例如,将更多流量引导到 NodeA,因为它未得到充分利用,将较少流量引导到 NodeB,因为它已被过度利用)。

上面对非阻塞服务的描述仅仅触及了表面;它们还有很多内容(以及大量文档、博客文章、互联网上有用的点点滴滴),但考虑到您的问题陈述(面对客户端负载不断增加,对服务器端资源的担忧),这听起来不错合身。