最近,团队一直在讨论如何改进在云应用和微服务中使用Hibernate (ORM) 的方式。特别是,基本假设是这些平台上的事情会经常出错,并且服务应该能够抵御失败。
问题
在微服务或云架构中,服务的启动顺序各不相同(通常不受您控制)。Hibernate ORM 应用程序可能会在数据库启动之前启动。目前,Hibernate ORM 不喜欢这种情况,如果无法连接到数据库,它将明确失败(异常)。
另一个相关问题是,如果运行Hibernate ORM的应用程序启动后数据库停止了一段时间,然后再迅速恢复工作,会发生什么情况。
解决方案 1:Hibernate在启动时等待并重试
一些用户要求我们在启动时延迟并重试连接过程,以防数据库不存在。这可以工作并解决启动问题。但它不会解决在应用程序运行时数据库消失的问题。但至少,您有事务和错误传播机制来保护您。此外,在开发时间,启动问题就会消失,这已经很好。
我理解这可能是实现这个功能的快速胜利,但最好在添加这个功能之前确保问题。我觉得Hibernate ORM的启动不是修复该问题的理想区域。但最终,如果它足够有用,那将是值得的。
我们正在探索这个选项,并考虑其他替代方案,这就是我们需要您的反馈的地方。
等待和重试与平台通知
在这篇博客文章中,我提到了等待和重试的方法。它可以被云平台在服务启动/停止时的通知所取代。 这避免了常规轮询过程,但代价是必须依赖来自各种云平台的多种集成。 |
解决方案 2:Hibernate 以非功能模式启动
如果 Hibernate ORM 无法连接到数据库,它将继续其启动过程。如果在数据库仍然不可用的情况下,有 EntityManager
请求连接,则会抛出一个定义良好的异常。为了避免系统被淹没,连接检查将采用等待和重试系统,即使请求了大量的 EntityManager
也只尝试几次。
在这里,并发性和我们使用启动连接信息来配置 Hibernate ORM 的事实上存在一些细微的困难。最明显的选择是从连接猜测要使用的方言。另一方面,像解决方案 1 提议的那样,在等待和重试时停止应用程序启动过程可能并非没有挑战。
Hibernate ORM 在数据库不可访问时抛出的异常需要被使用的应用程序(框架)妥善处理。例如,像全局 try-catch 一样将应用程序移至降级模式,或将执行错误传播到客户端(例如 HTTP 错误 500)。如果 Hibernate ORM 通过显式 API 揭示未就绪状态,可能会有所帮助。
这可以与云平台进行的健康检查联系起来。应用程序将通过 /health
端点报告 未就绪但正在尝试 的状态,该端点将由 编排器 使用。
关于数据库连接中断
无法连接到数据库有许多原因
对于情况 3、4、5,系统应该进入等待和重试模式吗?还是应该拒绝部署? |
解决方案 3:智能应用程序(框架)
另一个解决方案是让应用程序具有智能的总体启动逻辑。它尝试急切地启动,但如果发生 Hibernate ORM 连接错误,则只启动入站请求框架。它将定期尝试启动,同时在返回 HTTP 500 错误或类似错误的同时等待。
这需要一个可以处理这种情况的应用程序框架。它将断路器逻辑内嵌到应用程序中,并能更好地响应特定错误。不过,我不确定这样的框架有多普遍。
这本质上与解决方案 2 相同,只是它在应用程序(框架)的更高层次上处理,而不是在 Hibernate ORM 中。
解决方案 4:云/MSA 平台重启应用程序
一个更好的解决方案可能是让云平台处理这些情况,并重启在这些情况下失败部署的应用程序。这可能需要某种类型的服务依赖关系管理和一些云基础设施的智能。在启动时,如果抛出特定的错误代码,基础设施将触发等待和重试部署逻辑。但也存在依赖循环的风险,导致系统永远无法启动。
我猜想并非所有的云基础设施都提供这项服务,我们可能需要一个替代方案。OpenShift 允许您 表达依赖关系,以确保在另一个服务启动之前启动给定的服务。用户当然需要声明该依赖关系。
解决方案 5:代理!
另一种解决方案是在应用程序的入站请求之前以及/或应用程序和数据库之间放置代理。代理是云计算平台用来解决数字宇宙中“世界饥饿”的银弹。
在云中提供“Hello world!”需要多少代理和路由逻辑? :) |
这种方法的优势是不需要定制应用程序或库。不便之处在于在您的客户端和应用程序或数据之间有更多中介点。
如果代理在应用程序之前,则需要健康检查或从启动系统获取反馈,以便定期等待并重试应用程序的重部署。我再次不确定云基础设施是否提供所有这些基础设施。
如果代理在Hibernate ORM和数据库之间(如HAProxy for MySQL),您仍然会在JDBC侧遇到一些超时异常。这意味着应用程序将无法启动。但至少,代理可以实现等待和重试逻辑。