0

一段时间以来,我一直想使用完整的无服务器架构来实现移动应用程序,最后开始研究细节。到目前为止,我发现 AWS 提供了这种设置所需的大部分服务(API Gateway、Cognito、Lambda、DynamoDB、SQS 等),但我还没有解决一个(可能是理论上的)问题; 事件溯源。

由于(历史)数据如今变得越来越有价值,因此(以我的拙见)存储有关用户的历史数据非常重要。当前的事件溯源产品(例如 Akka Persistence)通过仅将事件持久化到数据库并将当前状态保存在内存中(并将快照保存到数据库等)来实现这一目标。

我的问题是,我没有能力将这种状态存储在内存中,因为我的 Lambda 函数在其单一目的完成后终止。我的问题归结为,目前是否有一个框架可以支持事件溯源(在 Java 上),它将当前状态保存在 ElastiCache(Redis)之类的东西中。既然我对 Akka 有很多经验,那么 Persistence 已经可以做到这一点了吗?是否值得将事件溯源与无服务器后端(此时)结合使用,还是现在还不是正确的时机?

到目前为止,我还没有在 Akka Persistence 文档中找到很多关于这个(可能不是)问题的信息。请就我在无服务器宇宙的使命中可能错过的内容提出建议;我还在学习,就像我们一样。

4

4 回答 4

0

这将主要基于意见,因此不是最适合 Stack Overflow,但我会尽量保持真实。

akka-persistence 不适合无服务器部署策略,原因如下。它依赖于一个强有力的假设,即在任何时候对于给定的 id 只有一个 PersistentActor。在分布式环境中强制执行这意味着节点间协调,通常使用 akka-cluster-sharding。这不会让自己部署在旨在运行简单功能的无服务器环境中。

一般来说,事件溯源意味着从存储在日志中的事件(或最新快照+随后的事件)中重建状态,并且在无状态环境之上执行此操作意味着函数的每次执行都非常低效,因为可能存在没有本地缓存​​。在事件溯源之上添加分布式缓存可以在一定程度上缓解这种情况。但是,您仍然面临协调的挑战,以防止函数的多个实例之间出现竞争条件。这些因素不利于无服务器旨在提供的操作简单性。

于 2017-06-15T08:26:12.627 回答
0

是的,您可以在无服务器中进行事件溯源。

使用 AWS 的一种方法是使用 DynamoDB 作为您的事件存储。然后,您可以将 DynamoDB 流与 Lambda 触发器结合使用,将它们具体化到您的状态存储(可以是任何其他数据库)中。

于 2017-08-21T11:19:40.013 回答
0

您可以在 Lambda 中使用 akka-persistence 进行事件溯源,如果您对(一些可配置的)最终一致性很好并且愿意也应用CQRS

这样的设置如何?

您将拥有(1 个或更多)创建n 个 lambda 实例的lambda 函数(我们称它们为 QUERY-Lambda;其中 n 基本上是无限的或受您帐户中可用的并发限制的限制)能够处理您的读取端(处理通过阅读日志/快照存储然后回答查询),最大。每个聚合处理写入操作的 lambda 实例(使用 lambda 配置中的并发参数确保这一点)(我们称它们为 COMMAND-Lambda)。这对于确保期刊不会因为有多个参与者写信而被破坏很重要。

根据您的一致性保证,确保在处理查询后立即停止 QUERY-Lambdas 中的参与者,或者将接收超时设置为符合您的一致性保证的值,因为知道多个参与者可能会给您一个不同的状态.

如果您有 CRUD 操作,请确保在应用更改之前向用户显示当前状态的读取操作(例如,在更新之前在表单中显示客户对象的当前值)也由 WRITE-Lambda 处理,因此您可以确保您正在更改的状态是最后一个可用状态。

您不必为此创建多个 jar 文件,您可以简单地将同一个 jar 文件部署为多个 lambda 函数。您需要确保在您的 API 网关中,更改状态的请求被路由到 WRITE-Lambda(s),而那些一致性不那么重要的请求被路由到 READ-Lambda(s)。

还要确保在重放日志时不创建快照,而仅在执行命令处理时创建(因为 READ-Lambdas 也在重放日志,因此如果它们创建快照可能会破坏您的状态)

为了获得最佳性能,请在每个更改状态的命令之后或至少在关闭 actor 之前创建一个快照,这样下一次调用将必须进行最少的读取。AFAIK Java 中的 Lambda 也会在一段合理的时间内保持活跃,所以冷重启应该不是什么大问题。如果它们适合你,请创建一些每 5-10 分钟调用 lambda 的 cron 以使其保持活动状态。或者,您可以使用https://doc.akka.io/docs/alpakka/current/awslambda.html简单地每 x 分钟向自己发送一个请求。您可以使用Source.tick(3 minutes)然后调用您的 WRITE-Lambda 函数,如图所示在 Alpakka 文档中。

还要确保您需要与两个聚合(Saga / Coordinator)对话的操作由相同的 WRITE-Lambda 处理。这可能会成为瓶颈,但当然您仍然可以通过 API 网关中的路由应用某种分片。它只是比拥有一个普通的 Akka 集群更努力。

如果有不清楚的地方,请发表评论,我会尽力回答。

于 2019-05-01T17:49:40.837 回答
0

您可以使用 DynamoDB Streams 来做到这一点,但只有 Event Store 是不够的。生成下一个事件的代码应该被序列化,即当时只允许一个代码实例为特定的聚合实例生成一个事件。否则事件的顺序可能不确定。

使用事件溯源命令发送到聚合。当命令生效时,即修改聚合,会生成一个事件,添加到日志并通常发布。为命令生成事件通常需要聚合的当前状态。这就是为什么此类代码不应为同一个聚合实例并行运行的原因。

一个解决方案是拥有一个“命令存储”,它是一个 DynamoDB 表,用于存储每个聚合实例的最后一条命令。因此,关联的流包含对该项目的更新。此流的 Lambda 触发器使用事件存储重构聚合实例的状态并生成新事件。然后将事件保存在事件存储中。事件存储的流负责事件的发布。

为了加快聚合状态的重建,可以使用快照表。例如,每 100 个事件,可以在其中更新完整的聚合。然后重建包括获取快照,然后仅获取序列号高于快照中的事件的事件。

对可能存在于各种读取存储中的事件和关联的聚合副本进行编号,具有使幂等性变得容易的优点。以这种方式重播事件是可能的。

于 2018-08-15T20:24:45.443 回答