注意:这是一个与设计相关的问题,我找不到令人满意的答案。因此在这里问。
我有一个部署在云中的 Spring 启动应用程序(云铸造厂)。应用连接到预言机数据库以检索数据。应用程序使用连接池(HikariCp)来维护与数据库的连接。假设连接数设置为 5。现在,应用程序能够根据负载自动扩展。所有实例将共享同一个数据库。在任何时候,同一应用程序的 50 个实例都可以运行,这意味着数据库连接总数将为 250(即 5 * 50)。
现在假设数据库只能处理 100 个并发连接。在当前方案中,20 个实例将用完 100 个可用连接。如果接下来的 30 个实例尝试连接到数据库,会发生什么情况?如果这是设计问题,如何避免?
请注意,为简单起见,问题中提供的数字是假设的。实际数字要高得多。
假设:
- 可用数据库连接数 =
X
- 应用程序的并发实例数 =
Y
- 应用程序的每个实例中数据库连接池的最大大小 =
X / Y
这有点简单,因为您可能希望能够从其他客户端(例如支持工具)连接到您的数据库,因此也许更安全的公式是(X * 0.95) / Y
。
现在,您已确保应用程序层不会遇到"不存在数据库连接"问题。但是,如果(X * 0.95) / Y
是 25,并且您有超过 25 个并发请求同时通过您的应用程序需要数据库连接,那么其中一些请求在尝试获取数据库连接时会遇到延迟,如果这些延迟超过配置的超时,它们将导致请求失败。
如果您可以限制应用程序中的吞吐量,以便您永远不会有超过(X * 0.95) / Y
个并发的"获取数据库连接"请求,那么问题就会消失。但是,当然,这通常不现实(事实上,因为少即是多......告诉你的客户停止和你说话通常是一个奇怪的信号)。这就引出了问题的症结:
现在,应用程序能够根据负载自动扩展。
向上扩展不是免费的。如果您希望在处理N
并发请求时具有与处理100000N
并发请求时相同的响应能力,则必须提供一些东西;您必须扩展这些请求所需的资源。因此,如果他们使用 databaase 连接,那么数据库支持的并发连接数将不得不增长。如果服务器端资源不能按客户端使用量的比例增长,则需要某种形式的背压,或者需要仔细管理服务器端资源。管理服务器端资源的一种常见方法是...
- 使您的服务非阻塞,即将每个客户端请求委托给线程池,并通过服务中的回调响应客户端(Spring 通过 DeferredResult 或其异步框架或其 RX 集成来促进这一点)
- 根据服务实例的客户端请求线程池的总大小,配置服务器端资源(例如数据库允许的最大可用连接数)以匹配服务的最大吞吐量
客户端请求线程池限制每个服务实例中当前处于活动状态的请求数,它不限制客户端可以提交的请求数。此方法允许服务向上扩展(到由所有服务实例中的客户端请求线程池的大小表示的限制),并且这样做允许服务所有者保护资源(例如其数据库)不被过载。由于所有客户端请求都被接受(并委托给客户端请求线程池),因此客户端请求永远不会被拒绝,因此从他们的角度来看,扩展似乎是无缝的。
这种设计通过服务实例集群上的负载均衡器进一步增强,该负载均衡器在它们之间分配流量(轮询,甚至通过某种机制,每个节点报告其"忙碌",该反馈用于指导负载均衡器的行为,例如将更多流量定向到 NodeA,因为它没有得到充分利用, 将较少的流量定向到节点 B,因为它被过度使用)。
上述对非阻塞服务的描述只是划伤了表面; 他们还有很多(以及大量的文档、博客文章、互联网上的有用片段),但考虑到你的问题陈述(面对来自客户端不断增加的负载时对服务器端资源的担忧),这听起来很合适。