实例的连接池和扩展

Connection pooling and scaling of instances

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

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

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

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

假设:

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

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

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

如果您可以限制应用程序中的吞吐量,使您的并发请求永远不会超过 (X * 0.95) / Y 个 'get database connection',那么很快问题就会消失。但是,当然,这通常是不现实的(事实上,少即是多……告诉您的客户停止与您交谈通常是一个奇怪的信号)。这就把我们带到了问题的症结所在:

Now the application has the capacity to scale automatically based on the load.

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

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

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

这种设计通过服务实例集群上的负载均衡器进一步增强,该服务实例集群在它们之间分配流量(循环法或什至通过某种机制,每个节点报告其 'busy-ness' 使用该反馈引导负载均衡器的行为,例如将更多流量引导至 NodeA,因为它未被充分利用,将较少流量引导至 NodeB,因为它被过度利用)。

以上对非阻塞服务的描述只是粗浅的描述;他们还有很多(以及大量的文档、博客文章、Internet 上有用的零碎信息)但是考虑到您的问题陈述(在面对客户端不断增加的负载时关注服务器端资源)这听起来不错合身。