无服务器是否可以与事件采购相结合?

时间:2017-06-14 20:39:04

标签: java akka aws-lambda akka-persistence serverless-architecture

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

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

我的问题在于,我没有能力将这样的状态存储在内存中,因为我的Lambda函数在完成其单一目的后终止。我的问题归结为,是否存在一个支持事件源(在Java上)的框架,它将当前状态保存在ElastiCache(Redis)之类的东西中。由于我对Akka有很多经验,这是Persistence已经可以做的事情吗?是否值得追逐事件采购与无服务器后端(此时)或者它现在还不是正确的时间?

至于这个(可能的非)问题,我还没有在Akka Persistence文档中找到太多。请给我一些关于我在无服务器世界的使命中可能错过的建议;正如我们所有人一样,我还在学习。

4 个答案:

答案 0 :(得分:0)

这主要是基于意见的,因此不适合Stack Overflow,但我会尽量保持尽可能的事实。

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

通常,事件来源意味着从存储在日志中的事件(或最新的快照+后面的事件)重建状态,并且在无状态环境之上执行此操作意味着每次执行函数时都会产生很多低效率,因为没有本地缓存​​。在事件源之上添加分布式缓存可以稍微减轻这种影响。但是,您仍然面临协调的挑战,以防止多个函数实例之间的竞争条件。这些因素不利于无服务器提供的操作简单性。

答案 1 :(得分:0)

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

使用AWS的一种方法是使用DynamoDB作为您的事件存储。然后,您可以将DynamoDB Streams与Lambda触发器一起使用,将它们实现到State Store(可以是任何其他数据库)。

答案 2 :(得分:0)

您可以使用DynamoDB流执行此操作,但仅事件存储是不够的。产生下一个事件的代码应该被序列化,即,当时只允许一个代码实例为特定的聚合实例产生一个事件。否则,事件的顺序可能不确定。

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

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

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

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

答案 3 :(得分:0)

如果您满意(可以配置)最终一致性,并且愿意也应用,则可以在Lambda中使用akka-persistence进行事件来源 > CQRS

这样的设置看起来如何?

您将拥有(1个或多个) lambda函数,它们创建 n个lambda实例(我们称其为QUERY-Lambda;其中n基本上不受限制或受并发性限制可以处理您的阅读方面(通过读取日志/快照存储库然后回答)来处理查询)和最高。每个汇总 1个lambda实例,即处理写操作(请确保在lambda的配置中使用并发参数进行设置)(将其称为COMMAND-Lambda)。重要的是要确保日记中没有多位演员写信,以免损坏日记。

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

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

您不必为此创建多个jar文件,只需将相同的jar文件部署为多个lambda函数即可。您需要确保在API网关中,将请求更改状态路由到WRITE-Lambda,将一致性不太重要的请求路由到READ-Lambda。

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

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

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

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