Akka演员:处理数据库故障而不丢失数据

11

情景
应用程序的数据库崩溃了。这导致任何负责将重要数据提交到数据库的操作者都无法获得连接。

期望行为
在未来的某个时间,当数据库重新启动时,将重要数据写入数据库。

当前实现
操作者捕获DBException,将数据包装在DBWriteFailed case类中,并将消息发送给其监管者。然后,监管者使用system.scheduler.scheduleOnce(...)安排另一个写入,在未来的某个时间(例如1分钟)进行,以便我们不会在等待数据库重新启动时被卡住。

这种实现肯定有效,但我觉得可能还有更好的方法。

  • 当提交操作者必须在成功提交后响应原始发送方时,协议会变得有点混乱。
  • 提交操作者的常规消息流程没有进行任何限制,该操作者将愉快地处理新消息,很可能每个消息都无法连接到数据库。
  • 如果消息在这个重试循环中被卡住太长时间,提交操作者的邮箱将开始膨胀。重要的是提交这些数据,但如果应用程序由于过度使用内存而变得缓慢或崩溃,则所有这些都没有意义。

我是一个Akka新手,当涉及到监管者策略时我基本上没有经验,但我觉得我可能可以利用其中一种来处理一些重试逻辑。

在Akka中是否有解决此类问题的常见方法?我走对了还是应该朝着不同的方向前进?

感谢任何帮助。

2个回答

9
您可以使用Akka Circuit Breaker来减少连接尝试。我建议使用一个带有最大大小限制的缓冲区(而不是使用调度程序作为重试队列)在Actor内部,并在断路器再次关闭时重试这些操作(onClose回调应该向自己的Actor发送消息)。另一种选择是将断路器与stashing mailbox结合使用。

谢谢你的回答,Patrik;在你提到它之前,我不知道断路器存在。你们为我们在Akka中打包了这么多好东西:D - Jake Greene
据我所知,隐藏并不能保证消息的顺序,数据库更新排序可能导致无法提交更新(例如尝试在删除旧行之前插入新版本的行,造成主键冲突)。因此,我编写了自己的替代方案来代替隐藏。 - Robin Green

1

如果您计划在应用程序中实现完全故障转移

不要这样做。

不要将数据库故障转移责任上升到应用程序层。就您的应用程序而言,数据库应该只是处于准备好接受读写操作的状态。

如果您的数据库经常出现故障,请花时间使其更加健壮(网络上已经有大量资源可供参考:搜索“复制”、“高可用性”、“负载均衡”和“集群”,并从highscalability.com等其他人的经验故事中学习)。这真的取决于您的数据库故障原因是什么(例如,我曾经使DB主服务器的NIC达到最大值,并通过在传输线路上启用GZIP来间歇性地“修复”问题)。

如果您走这条路,您会很高兴遵守关注点分离原则。

如果您计划实现偶尔的重试逻辑和处理DB停机

如果您不希望您的应用程序成为替代数据库,则Patrik's answer是最佳选择。


虽然我喜欢在编写应用程序时不必处理数据库故障的想法,但它们确实会发生。一个特定的情况是数据库负载过高导致间歇性连接失败。使我的数据库更加健壮是一个很好的解决方案,但这是一种被动的方法;未来的升级将无法帮助当前正在使用系统的用户。您是否建议我执行硬性失败(不执行写入操作)并告诉客户端稍后再试? - Jake Greene
我曾经做了最坏的打算,认为你是指处理严重而复杂的数据库故障。回答已修改。 - opyate
谢谢。我完全同意你的观点,尝试在我的应用程序中处理扩展的数据库故障最多只会带来痛苦,最坏的情况下会使我的应用程序变得笨重。+1 - Jake Greene
1
分布式系统的基本教训是,故障不是可以安全地忽略的千载难逢的事件;你的分布式系统越复杂,你就越有可能在其中遇到某种形式的故障。是的,路由器会崩溃,路由器会配置错误,路由器的固件中甚至会存在漏洞,即使在2013年也是如此。 - Robin Green

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接