仅在 Aurora 事务提交时调用 Lambda 函数,但保证调用 (ACID)
Only call Lambda function when Aurora transaction commits, but guarantee call (ACID)
对于新项目中的微服务,我目前正在考虑是使用DynamoDB还是AuroraMySQL作为底层数据存储。微服务为用户界面提供了一个 REST API,并且还会有其他几个微服务。这些其他微服务应该监听由 UI 连接的服务生成的事件流(事件源),以保持其他读取模型同步。
我正在尝试找出一种方法来保证发布到更改事件流的事件与基础数据存储中的数据更改完全匹配。通常,关注的是如果 REST API 处理程序,例如在执行中途被中断,它可能已经更改了数据但尚未创建事件(假设更改事件是在数据更改后发布的)。我现在正在寻找可以减轻这种担忧的机制。
对于 DynamoDB,有 DynamoDB 流和 AWS Lambda 触发器来响应数据存储级别的数据更改。被触发的 Lambda 可以将低级数据变化转化为有意义的变化事件,然后将事件发布到 SNS、SQS 或 Kinesis。
对于 Aurora MySQL 我还没有想出这样的机制。我看过描述两种机制的文章:
- 为 Aurora 启用二进制日志并使用额外的 EC2 实例来处理更改流。从此流发布其他服务的事件。
- 使用本机 lambda_sync 或 lambda_async 函数从 MySQL 触发器调用 Lambda。从此 Lambda 中发布其他服务的事件。
其一,我对这两种方法都不太满意:1) 我不希望管理额外的 EC2 实例和处理原始 SQL 更改。 2) 我计划为 Aurora 使用约束、乐观并发和事务,这意味着事务可以并且将会失败和回滚。但是,无论交易结果如何,lambda_(a)sync 调用都会执行。
Aurora 有什么更好的主意吗?还是我看问题的角度不对?
我想将这个问题和讨论集中在如何保证底层数据存储的更改和具有更改事件的传出流之间的一致性问题上,而不是在 Aurora 与 DynamoDB 上。
我找到了适合我们情况的答案,使用具有 MySQL 兼容性的 Aurora。在我的研究过程中,我在 microservices.io. Specifically, the page about the event-driven architecture pattern 找到了极好的信息来源,它提到了四种相关模式,以保证更新状态和发布事件的原子性。
- 事件溯源
- 申请事件
- 数据库触发器
- 事务日志拖尾
事件溯源是不可能的,因为它对于我们想要实现的目标来说太复杂了。我已经在我原来的问题中反对 tx 日志拖尾。应用程序事件和数据库触发器非常相似,因为作为事务的一部分,状态被更新并且条目被写入事件 table:Tx 成功提交,状态被持久化并且事件条目显示在table。 Tx 回滚并且状态未更改并且没有显示任何事件条目。两者之间的唯一区别是事件条目是由 application/service 逻辑本身生成的,还是由数据库触发器生成的。
然后外部进程轮询此table并根据事件条目为其他微服务发布事件(当然之后会删除已发布的)。这两种模式保证状态变化总是导致至少一个事件(恰好一次会更复杂一些)。
现在关于如何实现这个...我的第一个想法是使用 Fargate 容器和一个执行轮询的 Node 应用程序,我认为我会使用这个解决方案保持无服务器。然而,事实并非如此:为了保证事件的顺序,应该只有一个容器轮询和发布。单个 Fargate 容器被分配到一个可用性区域,如果该区域 "goes away",容器也是如此。现在我必须在顶部构建某种监控,以便在需要时在不同的 AZ #2 中实例化新容器 #2。但是,如果 AZ #1 和容器 #1 回来怎么办?那么就会有两种情况。这太复杂了。
现在我决定采用以下方法:CloudWatch 事件每分钟触发一次轮询 Lambda 函数(CW 的最小间隔)。调用后,该函数会继续轮询数据库,直到第二个 Lambda 函数调用在一分钟后接管。为了协调两个 Lambda 函数调用,我在我的数据库中创建了第二个 table,事件轮询状态,其中最近的 Lambda 函数调用更新了 table 中的专用行,指示它启动的先前函数调用(这是在 SELECT ... FOR UPDATE 和 TXs 的帮助下完成的,以防止竞争条件)。在每个轮询周期之前,如果没有其他函数在此期间启动,该函数将检查处于事件轮询状态的行。
这种方法的优点(如我所见):
- 真正的无服务器和 AZ-、VPC、子网感知。
- 事件的顺序是有保证的,因为不会有超过一个 Lambda 调用并行轮询和发布。
- 如果轮询 Lambda 函数因任何原因终止(例如,因为 AZ 消失),发布间隔最多为 1 分钟,直到 CloudWatch 下次调用 Lambda 函数。这个差距是acceptable.
对于新项目中的微服务,我目前正在考虑是使用DynamoDB还是AuroraMySQL作为底层数据存储。微服务为用户界面提供了一个 REST API,并且还会有其他几个微服务。这些其他微服务应该监听由 UI 连接的服务生成的事件流(事件源),以保持其他读取模型同步。
我正在尝试找出一种方法来保证发布到更改事件流的事件与基础数据存储中的数据更改完全匹配。通常,关注的是如果 REST API 处理程序,例如在执行中途被中断,它可能已经更改了数据但尚未创建事件(假设更改事件是在数据更改后发布的)。我现在正在寻找可以减轻这种担忧的机制。
对于 DynamoDB,有 DynamoDB 流和 AWS Lambda 触发器来响应数据存储级别的数据更改。被触发的 Lambda 可以将低级数据变化转化为有意义的变化事件,然后将事件发布到 SNS、SQS 或 Kinesis。
对于 Aurora MySQL 我还没有想出这样的机制。我看过描述两种机制的文章:
- 为 Aurora 启用二进制日志并使用额外的 EC2 实例来处理更改流。从此流发布其他服务的事件。
- 使用本机 lambda_sync 或 lambda_async 函数从 MySQL 触发器调用 Lambda。从此 Lambda 中发布其他服务的事件。
其一,我对这两种方法都不太满意:1) 我不希望管理额外的 EC2 实例和处理原始 SQL 更改。 2) 我计划为 Aurora 使用约束、乐观并发和事务,这意味着事务可以并且将会失败和回滚。但是,无论交易结果如何,lambda_(a)sync 调用都会执行。
Aurora 有什么更好的主意吗?还是我看问题的角度不对?
我想将这个问题和讨论集中在如何保证底层数据存储的更改和具有更改事件的传出流之间的一致性问题上,而不是在 Aurora 与 DynamoDB 上。
我找到了适合我们情况的答案,使用具有 MySQL 兼容性的 Aurora。在我的研究过程中,我在 microservices.io. Specifically, the page about the event-driven architecture pattern 找到了极好的信息来源,它提到了四种相关模式,以保证更新状态和发布事件的原子性。
- 事件溯源
- 申请事件
- 数据库触发器
- 事务日志拖尾
事件溯源是不可能的,因为它对于我们想要实现的目标来说太复杂了。我已经在我原来的问题中反对 tx 日志拖尾。应用程序事件和数据库触发器非常相似,因为作为事务的一部分,状态被更新并且条目被写入事件 table:Tx 成功提交,状态被持久化并且事件条目显示在table。 Tx 回滚并且状态未更改并且没有显示任何事件条目。两者之间的唯一区别是事件条目是由 application/service 逻辑本身生成的,还是由数据库触发器生成的。
然后外部进程轮询此table并根据事件条目为其他微服务发布事件(当然之后会删除已发布的)。这两种模式保证状态变化总是导致至少一个事件(恰好一次会更复杂一些)。
现在关于如何实现这个...我的第一个想法是使用 Fargate 容器和一个执行轮询的 Node 应用程序,我认为我会使用这个解决方案保持无服务器。然而,事实并非如此:为了保证事件的顺序,应该只有一个容器轮询和发布。单个 Fargate 容器被分配到一个可用性区域,如果该区域 "goes away",容器也是如此。现在我必须在顶部构建某种监控,以便在需要时在不同的 AZ #2 中实例化新容器 #2。但是,如果 AZ #1 和容器 #1 回来怎么办?那么就会有两种情况。这太复杂了。
现在我决定采用以下方法:CloudWatch 事件每分钟触发一次轮询 Lambda 函数(CW 的最小间隔)。调用后,该函数会继续轮询数据库,直到第二个 Lambda 函数调用在一分钟后接管。为了协调两个 Lambda 函数调用,我在我的数据库中创建了第二个 table,事件轮询状态,其中最近的 Lambda 函数调用更新了 table 中的专用行,指示它启动的先前函数调用(这是在 SELECT ... FOR UPDATE 和 TXs 的帮助下完成的,以防止竞争条件)。在每个轮询周期之前,如果没有其他函数在此期间启动,该函数将检查处于事件轮询状态的行。
这种方法的优点(如我所见):
- 真正的无服务器和 AZ-、VPC、子网感知。
- 事件的顺序是有保证的,因为不会有超过一个 Lambda 调用并行轮询和发布。
- 如果轮询 Lambda 函数因任何原因终止(例如,因为 AZ 消失),发布间隔最多为 1 分钟,直到 CloudWatch 下次调用 Lambda 函数。这个差距是acceptable.