当前位置: 首页 > 知识库问答 >
问题:

只在Aurora事务提交时调用Lambda函数,但保证调用(ACID)

羊舌光赫
2023-03-14

对于新项目中的一个微服务,我目前正在考虑是否使用DynamoDB或Aurora MySQL作为底层数据存储。微服务为用户界面提供了一个REST API,还有其他几个微服务。这些其他微服务应该监听由UI连接的服务生成的事件流(事件源),以保持其他读取模型的同步。

我正在试图找出一种方法来保证发布到变更事件流的事件与对底层数据存储中数据的更改完全匹配。通常,需要考虑的是,如果REST API处理程序(例如)在执行过程中被中断,那么它可能已经更改了数据,但还没有创建事件(假设更改事件在数据更改之后发布)。我现在正在寻找能够减轻这种担忧的机制。

对于DynamoDB,有DynamoDB流和AWS Lambda触发器来对数据存储级别的数据更改作出反应。触发的Lambda可以将低层数据变更转换为有意义的变更事件,然后将该事件发布到SNS、SQS或Kinesis。

对于Aurora MySQL,我还没有想出这样的机制。我看到过描述两种机制的文章:

  1. 启用Aurora的二进制日志,并使用一个额外的EC2实例来处理变更流。发布此流中其他服务的事件。
  2. 使用本机lambda_sync或lambda_async函数从MySQL触发器调用Lambda。从此lambda中发布其他服务的事件。

首先,我对这两种方法都不太满意:1)我不希望管理额外的EC2实例和处理原始的SQL更改。2)我计划为Aurora使用约束、乐观并发和事务,这意味着事务可以并且将会失败和回滚。然而,无论事务结果如何,lambda_(a)同步调用都将被执行。

对Aurora有什么更好的主意吗?还是我看这个问题的角度错了?

我想把这个问题和讨论的重点放在如何保证底层数据存储和带有变更事件的传出流之间的变更一致性上,而不是放在Aurora vs.Dynamodb上。

共有1个答案

沈健
2023-03-14

我找到了一个适合我们情况的答案,使用与MySQL兼容的Aurora。在我的研究过程中,我在microservices.io找到了一个很好的信息来源。具体地说,关于事件驱动体系结构模式的页面引用了四个相关的模式,以保证更新状态和发布事件的原子性。

  • 事件源
  • 应用程序事件
  • 数据库触发器
  • 事务日志跟踪

事件来源是不可能的,因为它对于我们想要实现的目标来说太复杂了。我在最初的问题中已经反对tx日志跟踪。应用程序事件和DB触发器非常相似,因为作为事务的一部分,状态被更新,条目被写入事件表:Tx成功提交,状态被持久化,并且事件条目显示在该表中。Tx回滚,状态不变,没有事件条目显示。两者之间的唯一区别是事件条目是由应用程序/服务逻辑本身生成的,还是由DB触发器生成的。

然后,一个外部进程轮询这个表,并基于事件条目为其他微服务发布事件(当然,随后删除发布的事件)。这两种模式保证了状态更改总是导致至少一个事件(只发生一次会稍微复杂一些)。

现在关于如何实现这个...我的第一个想法是使用一个Fargate容器和一个负责轮询的Node应用程序,我认为我可以使用这个解决方案保持无服务器状态。然而,事实证明这并不完全正确:为了保证事件的顺序,应该只有一个容器轮询和发布。单个Fargate容器被分配到一个可用性区域,如果该区域“消失”,那么容器也会消失。现在,我必须在上面构建某种监视,以便在需要时在不同的AZ#2中实例化一个新的容器#2。但是如果AZ#1和容器#1又回来了呢?那么就会有两个实例。这太复杂了。

现在,我确定了以下方法:CloudWatch事件每分钟触发一次轮询Lambda函数(对于CW来说是min.interval)。一旦调用,该函数将继续轮询DB,直到一分钟后第二个Lambda函数调用接管。为了使两个Lambda函数调用协调一致,我在DB中创建了第二个表,事件轮询状态,其中最近的Lambda函数调用更新了该表中的一个专用行,向它启动的前一个函数调用指示(这是在SELECT...for UPDATE和TXs的帮助下完成的,以防止出现争用情况)。在每个轮询周期之前,如果在此期间没有其他函数启动,则该函数将检查处于事件轮询状态的行。

这种方法的优点(在我看来):

  • 真正无服务器且AZ-、VPC、子网不知道。
  • 保证了事件的顺序,因为不会有一个以上的Lambda调用同时轮询和发布。
  • 如果轮询Lambda函数由于任何原因终止(例如,因为AZ消失),发布的间隔最多为1分钟,直到CloudWatch下一次调用Lambda函数。此差距是可以接受的。
 类似资料:
  • 问题内容: 我试图在表单提交中调用特定的php函数,表单和php脚本都在同一页面中。我的代码如下(它不起作用,所以我需要帮助) 问题答案: 在下一行 该动作应该是脚本的名称,并且应该调用该函数,就像这样

  • 我有一个代码可以很好地工作,但当负载很高时失败。无法找到原因的路线。下面是我的用例概述 我正在从JMS队列中读取消息 使用接收到的消息调用一个REST APIendpoint 在我的数据库中保存从#2收到的响应 下面是代码片段 当我在调试模式下运行时,或者当消息不太频繁地进入队列时,这种方法很好。 但是当队列中的消息非常快时,我在方法中得到异常 当多个线程同时访问JMS和JPA事务时,我是不是做错

  • 确保函数只被调用一次。 使用一个闭包,使用一个成为 called 的标志,并在第一次调用该函数时将其设置为 true ,以防止它被再次调用。 为了允许函数改变它的 this 上下文(比如在一个事件监听器中),必须使用function 关键字,并且提供的函数必须应用上下文。 允许使用 rest(剩余)/spread(展开) (...) 运算符为函数提供任意数量的参数。 const once = fn

  • 我创建了一个节点lambda函数,它对Aurora数据库进行简单调用。当我在控制台中测试该函数时,查询返回,我可以在日志中看到结果,但回调似乎永远不会被调用,所以我的lambda函数超时了。我无法找出问题所在。希望这里有人能指出我的问题。 生成的Cloudwatch日志如下所示。。。

  • 下面的代码是否可以正常工作,或者我需要在工作之前开始交易。