【问题标题】:Akka Actors: Handling DB Failures Without Losing DataAkka Actors:在不丢失数据的情况下处理数据库故障
【发布时间】:2012-12-17 03:04:47
【问题描述】:

场景
应用程序的数据库已关闭。这会导致任何负责将重要数据提交到数据库的参与者无法获得连接

首选行为
重要数据会在未来某个时间恢复时写入数据库。

当前实施
Actor 捕获 DBException,将数据包装在 DBWriteFailed 案例类中,并将消息发送给其主管。然后,主管使用 system.scheduler.scheduleOnce(...) 在未来某个时间(例如 1 分钟)安排另一次写入,这样我们就不会在等待数据库恢复时绕圈子太多。

这个实现确实有效,但我觉得可能有更好的方法。

  • 当提交参与者必须在成功提交后响应原始发件人时,协议会变得有点混乱。
  • 发送到提交 Actor 的常规消息流不会以任何方式受到限制,并且 Actor 会愉快地处理新消息,可能无法为每条消息连接到数据库。
  • 如果消息在此重试循环中被捕获的时间过长,提交参与者的邮箱将开始膨胀。提交这些数据很重要,但如果应用程序因内存使用过多而停止或崩溃,这些都无关紧要。

我是一名 akka 新手,在主管策略方面我基本上没有经验,但我觉得我可以利用其中一个来处理一些重试逻辑。

akka 中是否有解决此类问题的通用方法?我是在正确的轨道上还是应该朝着不同的方向前进?

感谢任何帮助。

【问题讨论】:

    标签: database scala akka actor


    【解决方案1】:

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

    【讨论】:

    • 感谢您的回答,帕特里克;直到你提到它,我才知道断路器的存在。你们为我们在 Akka 中打包了很多好东西:D
    • AFAICS 存储不能保证保留消息顺序,并且数据库更新重新排序可能导致更新无法提交(例如,尝试在旧行被删除之前插入新版本的行,导致违反主键)。因此,我编写了自己的替代方案。
    【解决方案2】:

    如果您计划在您的应用中实施完全故障转移

    不要。

    不要将数据库故障转移责任推到应用层。就您的应用而言,数据库应该刚刚启动并准备好接受读取和写入。

    如果您的数据库经常出现故障,请花点时间让您的数据库更加健壮(网络上已经有大量资源可用于此目的:在网络上搜索“复制”、“高可用性”、“负载平衡”等术语和“集群”,并从highscalability.com 上的其他人的战争故事中学习)。这完全取决于您的数据库中断的原因是什么(例如,我曾经在数据库主机上用尽了网卡,并通过在线上启用 GZIP 间歇性地“修复”了问题)。

    如果你走这条路,你会很高兴你坚持关注点分离。

    如果您打算实施奇怪的重试逻辑和处理 DB 断电

    如果您不希望您的应用成为替代数据库,那么Patrik's answer 是最好的选择。

    【讨论】:

    • 虽然我喜欢在编写应用程序时不必处理数据库故障的想法,但它们确实会发生。一个特定的场景是数据库上的高负载导致间歇性连接失败。让我的数据库更健壮是一个很好的解决方案,但它是一种反应性的解决方案;未来的升级不会帮助当前正在使用该系统的用户。你是在建议我努力失败(执行不写)并告诉客户改天再试吗?
    • 我假设了最坏的情况,并认为您打算处理严重、广泛的数据库中断。答案已修改。
    • 谢谢。我完全同意你的观点,尝试在我的应用程序中处理扩展的数据库故障充其量是一种痛苦,最坏的情况是使我的应用程序成为一个笨拙的野兽。 +1
    • 分布式系统的基本教训是,故障不是十亿分之一的事件,可以安全地忽略;您的分布式系统越复杂,您就越有可能在其中遇到某种形式的故障。是的,即使在 2013 年,路由器确实会出现故障,并且路由器确实配置错误,并且路由器的固件中确实存在错误。
    猜你喜欢
    • 1970-01-01
    • 2020-02-21
    • 2012-04-30
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-21
    相关资源
    最近更新 更多