我有以下应用场景 .
我需要根据多个用户操作和规则在我的服务器中调用API . 在我的网络应用程序中,用户可能会单击一个按钮,我需要在 X
时间之后安排一个事件,其中 X
值取决于用户配置 . 该事件必须触发对将处理某些数据的API的回调 .
为了实现这一目标,我使用 AWS
服务以多种方式思考 . 因此,一旦用户点击我的网络应用程序按钮服务器将:
SQS Approach
- 创建
SQS
队列,然后每2分钟运行一次Lambda
函数并按队列检查以查看元数据时间戳值是否准时执行API调用 . 完成API调用后,删除队列 .
这里的问题是我可以让10,000个并发用户单击它自己的按钮,这意味着10,000个队列,每个队列将有自己的元数据时间戳值,这是我在 Lambda
需要调用API时的预定时间 . 从阅读中无法根据元数据值查询 SQS
:Ex . 获取时间戳等于或小于实际时间的所有队列 .
此外,我正在调查队列延迟功能,只能使需要执行的队列可见,但缺点是延迟最大值是15分钟,我可以有超过6小时的预定时间 .
DynamoDB Approach
- 服务器将创建
DynamoDB
记录,而不是使用SQS
. 然后Lambda
每2分钟执行一次,如果Timestamp成员等于或小于实际时间,将拉出所有表记录并循环验证每个表记录,如果是,则调用API并删除DB记录 .
这种情况可能看起来更好,因为我猜从 DynamoDB
获取记录的性能更快(是一种猜测)但又缺乏强大的查询,因为我只能查询主键 . 我正在阅读我可以扫描,但不知道是否可以根据日期和时间值进行扫描 .
Cloudwatch Approach
- 而不是使用
SQS
或DynamoDB
,在每个用户按钮单击时,服务器必须创建Cloudwatch
规则,该规则将具有我需要触发Lambda
函数的预定时间 . 执行Lambda
并调用API后,Lambda
将需要删除Cloudwatch
规则,以便它不会再次执行 .
这个场景需要创建数以千计的 Cloudwatch
规则并且不知道这是否可行,但我喜欢这种方法,因为我不必拉数据,循环每个项目,验证时间戳并触发 Lambda
,因为 Cloudwatch
会自动执行 .
任何建议或线索,哪一个是正确的方法或可能我错过了其他人 . 谢谢
3 回答
我不会使用您概述的任何方法 . 相反,我会开发一个利用Amazon Step Functions的解决方案 .
当用户单击该按钮时,实例化步骤功能,其中第一步骤之一是参数化等待状态 . 这将为您提供用户配置的等待时间,并且可以根据需要进行长或短 . 在等待状态之后,您可以在工作流程中执行其余步骤 .
与使用步骤函数相比,您概述的所有方法都显得笨拙,易碎且昂贵 . 通过无服务器解答,您可以无缝扩展并高效运行 .
https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-wait-state.html
Dynamo方法
我认为这是你走的最佳方式 . 你实际上可以拥有所谓的composite primary key:“这种类型的键由两个属性组成 . 第一个属性是分区键,第二个属性是排序键” .
例如,排序键可以是应该执行作业的时间戳 . 这样,您可以同时查询主键(也称为Hash属性) AND 排序键(也称为分区键),以便仅检索将在某个时间点执行的作业,而无需扫描 .
OBS . :
now()
将是一个返回当前时间戳的函数 .单击用户按钮后,生成应运行请求的时间戳(例如,从现在起5小时=
now() + 60 * 60 * 5
)并将此时间戳保存为Dynamo中的排序键 .在Lambda函数中(每2分钟自动触发),您将查询Dynamo以使用
sort_key < now()
检索请求,这将检索将在该特定时间点执行的所有请求 .处理完成后,您将从Dynamo中删除请求或将其标记为已执行 .
小心那个Dynamo限制单个查询中要返回的项目数以及查询结果的大小(以MB为单位) . 此外,Lambda的执行时间限制为5分钟 . 根据您的处理需要多长时间以及在某些时候处理的请求数量,您需要将其拆分为块,否则Lambda可能会超时 .
这里可以使用各种方法:
相同的Lambda函数在每个作业结束时调用自身以继续处理挂起的请求(如果're any). This is easier to implement, but the downside is when you have too many chunks: the latter ones would be delayed (they'll等待第一个作业执行) . 延迟可能会有问题,因为您的用户希望先前处理该作业 .
您可以使用Composer函数从Dynamo中检索所有内容(可以运行多个查询,如果有太多待处理作业)并且并行多次触发另一个Lambda函数(在异步模式下) . 第二个Lambda将负责实际完成所有繁重的工作 . 这种方法的优点是几乎同时执行每一块作业请求,这可以防止不希望的延迟 .
SQS方法
正如您已经表达的那样,SQS不是处理这种用例的工具 .
Cloudwatch(CW)方法
这里的问题是CW的限制为100 rules per region per account . 您可以申请增加,但我不允许您拥有多达数十或数十万条规则 . 它不适用于这种用例 .
如果您的日程安排不需要粒度,您仍然可以通过设置可由不同用户共享的标准规则来使用CW . 例如:
设置每小时运行24条规则,以便覆盖整天 . 您可以使用当天的小时来识别每个规则:"rule1:00AM","rule2:00AM"等 .
让's say it'上午7点UTC,用户想要从现在起安排3小时 . 您将使用主键(如
rule10:00AM-reqXYZ123
)在Dynamo中保存此请求 .在上午10:00,相应的CW规则将触发Lambda,Lambda将从Dynamo检索所有具有以"rule10:00AM"开头的主键的请求(请参阅Conditional Queries中的BEGIN_WITH) . 然后,您可以在Lambda上正常处理请求 .
处理完成后,您将从Dynamo中删除请求或将其标记为已执行 .
还要遵守我上面提到的Dynamo和Lambda的相同限制 . 如果您需要更多粒度,则可以每30分钟运行48个CW规则,或者每15分钟运行96个CW规则 . 但无论如何,我更喜欢上面的Dynamo方法 . 它将花费您更多的时间来实现,但它更灵活,可重用 .
虽然你已经通过使用Step Function找到了答案,但我还是想分享我对此的看法,因为你的用例与我的用例非常相似,我最终使用的是DynamoDB .
但是,我的方法不是使用Lambda来查询和验证时间戳,而是使用DynamoDB的生存时间(时间戳列设置为TTL),表中的记录将在到期时删除并删除记录将出现在DynamoDB流中 . 一旦它出现在流中,就可以触发Lambda进行进一步处理 . 您可以在此处找到有关TTL和Stream的文档 .
因此,一般情况下,我的应用程序将通过在DynamoDB中添加记录来记录进程事件,并在事件发生时添加时间戳(时间戳为TTL) . 然后,一旦达到时间戳,DynamoDB将删除记录并将其放入将触发Lambda启动事件的流中 .
使用这种方法的决定是因为我的应用程序需要能够"scheduled event"的另一个用例 . 所以只要记录还在表中,我仍然可以操纵它们 .