我有一个用例,我将AWS Step功能在文件上传到S3时触发,从那里第一步运行ffprobe以从外部服务(如transloadit)获取文件的持续时间输出被写回S3。
我可以从该事件创建一个新的步骤函数,但是如果可以在原始步骤函数中有Await承诺然后继续到下一个函数,我就会徘徊 - 考虑到ffprobe可能需要更长的时间复出。
对于如何解决这个问题,我们非常感激。
答案 0 :(得分:2)
AWS Step Functions现在支持将一流的长时间运行步骤的异步回调。
这类似于@mixja的答案,但经过简化。工作流程中的单个状态可以直接调用Lambda,SNS,SQS或ECS,并等待对SendTaskSuccess
的调用。
有一个good example用于SQS的文档,其中步进功能发送消息并暂停工作流程执行,直到提供回调为止。 Lambda是等效的(假设像transloadit这样的主要处理过程发生在Lambda本身之外)
您的步进函数定义如下
"Invoke transloadit": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke.waitForTaskToken",
"Parameters": {
"FunctionName": "InvokeTransloadit",
"Payload": {
"some_other_param": "...",
"token.$": "$$.Task.Token"
}
},
"Next": "NEXT_STATE"
}
然后在您的Lambda中执行类似的操作
def lambda_handler(event, context):
token = event['token']
# invoke transloadit via SSM, ECS, passing token along
然后,在主要的长期运行过程中,您将使用shell脚本/ CLI中的令牌aws stepfunctions send-task-success --task-token $token
或类似的API调用发出回调。
答案 1 :(得分:1)
不能提出简单的解决方案,只有很少的方向可供探索。
首先,步骤函数有一种特定的方法来处理长时间运行的后台工作:活动。 https://docs.aws.amazon.com/step-functions/latest/dg/concepts-activities.html它基本上是一个队列。
如果您想要100%无服务器,这将是复杂或丑陋的。
Retry
子句如果您可以为后台工作人员分配“1/8微”实例,那么它不是优雅而是简单,可以通过即时反应实现。低硬件要求暗示我们将仅使用机器进行同步。
定义StepFunction活动,以示例video-duration
命名。
为即时反应定义SQS队列或为持续时间结果轮询S3。
状态函数伪代码:
{
StartAt: ffprobe
ffprobe: {
Type: Task
Resource: arn:...lambda:launch-ffprobe
Next: wait-duration
}
wait-duration: {
Type: Task
Resource: arn...activity:video-duration
End: true
}
}
后台工作者伪代码:
statemap = dict/map filename to result
thread1:
loop:
taskToken, input = SF.GetActivityTask('video-duration') # long poll
sync(key=input.filename, waiter=taskToken)
thread2:
loop:
msg = SQS.ReceiveMessage(...) # or poll S3
sync(key=msg.filename, duration=msg.result)
function sync(key, waiter, duration):
state = statemap[key]
if waiter:
state.waiter = waiter
if duration:
state.duration = duration
if state.waiter and state.duration:
SF.SendTaskSuccess(state.waiter, state.duration)
S3触发伪代码:
if filename is video:
SF.StartExecution(...)
else if filename is duration:
content = S3.GetObject(filename)
SQS.SendMessage(queue, content)
答案 2 :(得分:1)
当您将请求发送到transloadit时,请根据上传的文件密钥将s3中的步骤的taskToken保存在可预测的密钥中。例如,如果媒体文件位于&x 39://my-media-bucket/foobar/media-001.mp3',则可以创建包含当前步骤和存储的任务标记的JSON文件它在同一个桶中使用相同的密钥,例如' s3://ffprobe-tasks/foobar/media-001.mp3.json'。在步骤结束时将媒体发送到transloadit 不在步骤中调用成功或失败 - 让它继续运行。
然后,当您收到s3通知表明transloadit结果已准备就绪时,您可以确定获取任务令牌的s3键(' s3://ffprobe-tasks/foobar/media-001.mp3' ),加载JSON(并从s3中删除它)并为该任务发送成功。步骤功能将继续执行中的下一个状态。
答案 3 :(得分:0)
您可以通过AWS Lambda函数替换此API网关,例如由S3事件触发(文档:http://docs.aws.amazon.com/lambda/latest/dg/with-s3.html)。只需确保您的任务有适当的超时。
答案 4 :(得分:0)
当我尝试将SFN组合到协调AWS Batch作业时,我也会遇到这个问题。 上面提到的做法是有问题的,因为你应该传递taskToken,因此你需要从一个lambda inside 状态机,从队列中轮询TaskToken,并将它传递给S3或某个地方,另一个lambda将提交活动状态。
问题是:当您轮询taskToken时,您无法知道它是否属于您的状态机实例。 您可以在同一个状态机的另一个实例上获取令牌。 就个人而言,我认为如果AWS支持这种功能,那将是很好的,他们很容易做到......
答案 5 :(得分:0)
您通常希望将异步任务作为步骤函数活动启动。此处的关键字是启动 - 换句话说,一旦您的活动有待处理操作,就会触发异步操作。这样做的原因是您需要与待处理活动相关联的任务令牌 - 然后只要您的" future"可以以某种方式包含此令牌(例如,您可以将其设置为引用或请求ID),然后您可以完成"使用SendTaskSuccess或SendTaskFailure调用成功或失败的活动。
启动任务有两种方法:
轮询新活动。您可以设置CloudWatch预定活动,每隔n分钟拨打一次GetActivityTask。
解雇新的"发起人"任务与步骤函数中的活动并行。此启动器执行与#1相同的操作并进行GetActivityTask调用,唯一的区别是它是立即触发的,并且不需要轮询机制。 GetActivityTask调用将阻塞,直到新的活动任务可用,因此竞争条件没有问题。请注意,您可能会从另一个执行中获取活动,因此此启动程序只需要考虑活动的输入,而不是发起者自己接收的输入。
以下是步骤函数中的#2:
与 InitiateManualApprovalActivity 任务相关的基本代码示例:
import boto3
import time
client = boto3.client('stepfunctions')
activity = "arn:aws:states:us-east-1:123456789012:activity:ManualStep"
def lambda_handler(event, context):
print(event)
# This will block until an activity task becomes available
task = client.get_activity_task(activityArn=activity, workerName="test")
print(task)
# Perform your task here
# In this example we continue on in the same function,
# but the continuation could be a separate event,
# just as long as you can retrieve the task token
time.sleep(60)
response = client.send_task_success(taskToken=task['taskToken'], output=task['input'])
print(response)
return "done"