问题 Akka演员:在不丢失数据的情况下处理数据库故障


脚本
应用程序的数据库已经关闭。这导致任何负责将重要数据提交给DB的任何actor都无法获得连接

首选行为
重要数据在重新启动时写入数据库 将来的某个时候

目前的实施
该actor捕获DBException,将数据包装在DBWriteFailed案例类中,并将消息发送给其主管。然后,主管使用system.scheduler.scheduleOnce(...)在将来某个时间(例如1分钟)安排另一次写入,这样我们就不会在等待DB重新启动时过多旋转。

这种实现当然有效,但我觉得可能有更好的方法。

  • 当提交的actor在成功提交后必须响应原始发送者时,协议会变得有点麻烦。
  • 对提交actor的常规消息流不受任何限制,并且actor会愉快地处理新消息,可能无法为每个消息连接到DB。
  • 如果消息在此重试循环中被捕获的时间过长,则提交的actor的邮箱将开始出现气球。提交此数据非常重要,但如果应用程序因内存使用过多而导致停止或崩溃,则无关紧要。

我是一名akka新手,在涉及主管策略方面我基本缺乏经验,但我觉得我可能能够利用其中一个来处理一些重试逻辑。

在akka中有一个共同的方法来解决这样的问题吗?我是在正确的轨道上还是我应该向不同的方向前进?

任何帮助表示赞赏。


3264
2017-12-17 03:04


起源



答案:


您可以使用 Akka断路器 减少连接尝试。不使用调度程序作为重试队列,我将在actor内部使用缓冲区(具有最大大小限制),并在断路器再次关闭时重试那些(onClose回调应该向自己的actor发送消息)。另一种方法是将断路器与a组合 存放邮箱


9
2017-12-17 08:53



谢谢你的回答,Patrik;直到你提到它,我才意识到断路器的存在。你们为我们收拾了很多好吃的东西:D - Jake Greene
AFAICS存储不保证保留消息排序,并且数据库更新重新排序可能导致无法提交更新(例如,在删除旧版本之前尝试插入新版本的行,从而导致主键违规)。因此,我写了自己的替代方法来藏匿。 - Robin Green


答案:


您可以使用 Akka断路器 减少连接尝试。不使用调度程序作为重试队列,我将在actor内部使用缓冲区(具有最大大小限制),并在断路器再次关闭时重试那些(onClose回调应该向自己的actor发送消息)。另一种方法是将断路器与a组合 存放邮箱


9
2017-12-17 08:53



谢谢你的回答,Patrik;直到你提到它,我才意识到断路器的存在。你们为我们收拾了很多好吃的东西:D - Jake Greene
AFAICS存储不保证保留消息排序,并且数据库更新重新排序可能导致无法提交更新(例如,在删除旧版本之前尝试插入新版本的行,从而导致主键违规)。因此,我写了自己的替代方法来藏匿。 - Robin Green


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

别。

不要将数据库故障转移责任限制在应用层中。就您的应用而言,数据库应该刚刚启动并准备好接受读写操作。

如果您的数据库经常出现故障,请花时间让您的数据库更加健壮(网络上已有大量资源用于此:在网络上搜索“复制”,“高可用性”,“负载平衡”和“群集”等术语',并从他人的战争故事中学习 highscalability.com)。这一切都取决于数据库中断的原因(例如,我曾经在数据库主服务器上最大化了网卡,并通过在线路上启用GZIP来间歇性地“解决”问题)。

如果沿着这条路走下去,你会很高兴你坚持分离。

如果您计划实施奇数重试逻辑并处理数据库欠压

如果您不希望您的应用成为替代数据库,那么 帕特里克的回答 是最好的方式。


1
2017-12-17 08:07



虽然我喜欢在编写应用程序时不必处理数据库故障的想法,但它们确实发生了。特定情况是DB上的高负载导致间歇性连接故障。让我的数据库更强大是一个很好的解决方案,但它是一个被动的;未来的升级无助于当前使用该系统的用户。你是建议我努力工作(不写不写)并告诉客户再试一次? - Jake Greene
我假设最坏的,并认为你打算处理艰难,广泛的数据库中断。答案已修改。 - opyate
谢谢。我完全同意你的观点,试图在我的应用程序中处理扩展数据库故障充其量只会是一种痛苦,最糟糕的是使我的应用程序成为一个笨拙的野兽。 +1 - Jake Greene
分布式系统的基本教训是,失败不是十亿分之一的事件,可以安全地忽略;分布式系统越复杂,您就越有可能遇到某种形式的故障。是的,即使在2013年,路由器确实发生故障,路由器也会出现配置错误,路由器也会在其固件中提供错误。 - Robin Green