Aws lambda AWS Step功能-等待事件发生

Aws lambda AWS Step功能-等待事件发生,aws-lambda,aws-step-functions,Aws Lambda,Aws Step Functions,我有一个用例,其中我有一个AWS Step函数,当文件上传到S3时会触发该函数,从那里开始,第一步运行ffprobe,从外部服务(如transloadit)获取文件的持续时间,其中输出被写回S3 我可以从该事件创建一个新的step函数,但考虑到ffprobe可能需要更长的时间才能恢复,我想知道是否有可能在原始step函数中有一个等待承诺,然后继续下一步 对于如何解决这一问题,我们非常感谢您的任何建议。无法提出简单的解决方案,只有很少的方向需要探索 首先,Step函数有一种处理长时间运行的后台工作

我有一个用例,其中我有一个AWS Step函数,当文件上传到S3时会触发该函数,从那里开始,第一步运行ffprobe,从外部服务(如transloadit)获取文件的持续时间,其中输出被写回S3

我可以从该事件创建一个新的step函数,但考虑到ffprobe可能需要更长的时间才能恢复,我想知道是否有可能在原始step函数中有一个等待承诺,然后继续下一步


对于如何解决这一问题,我们非常感谢您的任何建议。

无法提出简单的解决方案,只有很少的方向需要探索

首先,Step函数有一种处理长时间运行的后台工作的特定方法:活动。它基本上是一个队列

如果您想要100%无服务器,这将是复杂或丑陋的

  • 或者,如您所说,为每个文件创建新的步骤函数
  • 或者,使用自定义错误代码和
    Retry
    子句在状态机中执行S3轮询循环
如果您可以为后台工作人员分配“1/8微”实例,这并不优雅,但很容易,并且可以通过即时反应来实现。低硬件要求暗示我们将只使用机器进行同步

定义StepFunction活动,例如命名为
视频持续时间
。 为即时反应定义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)
if filename is video:
  SF.StartExecution(...)
else if filename is duration:
  content = S3.GetObject(filename)
  SQS.SendMessage(queue, content)
后台工作人员伪代码:

{
  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)
if filename is video:
  SF.StartExecution(...)
else if filename is duration:
  content = S3.GetObject(filename)
  SQS.SendMessage(queue, content)
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)
if filename is video:
  SF.StartExecution(...)
else if filename is duration:
  content = S3.GetObject(filename)
  SQS.SendMessage(queue, content)

好吧,我会从


您可以使用AWS Lambda函数替换此中的API网关,例如由S3事件触发(文档:)。只需确保您的任务具有适当的超时。

当您发送transloadit请求时,请根据上载的文件密钥将步骤的taskToken保存在s3中的可预测密钥处。例如,如果媒体文件位于“s3://my media bucket/foobar/media-001.mp3”,则可以创建一个包含当前步骤的任务标记的JSON文件,并使用相同的密钥将其存储在不同的bucket中,例如“s3://ffprobe tasks/foobar/media-001.mp3.JSON”。在将媒体发送到transloadit的步骤结束时,不要调用该步骤的成功或失败,让它继续运行


然后,当您收到transloadit结果就绪的s3通知时,您可以确定s3键以获取任务令牌('s3://ffprobe tasks/foobar/media-001.mp3'),加载JSON(并从s3中删除它),然后发送该任务的成功消息。step函数将继续执行到下一个状态。

当我尝试组合SFN来编排AWS批处理作业时,我也遇到了这个问题。 上面建议的实践是有问题的,因为您应该传递taskToken,所以您需要从状态机内部的lambda轮询队列中的taskToken,并将其传递给S3或其他地方,另一个lambda将提交活动状态

问题是:当您轮询taskToken时,您无法知道它是否属于您的状态机实例。 您可以在同一台sate机器的另一个实例上获取令牌。
就我个人而言,我认为如果AWS能够支持这一功能,那将是一件好事,因为他们很容易做到这一点。

您通常希望将异步任务作为一个步骤功能活动来启动。这里的关键字是initiate——换句话说,一旦您的活动有一个挂起的操作,即触发您的异步操作。原因是您需要与挂起活动关联的任务令牌-然后,只要您的“未来”可以以某种方式包含此令牌(例如,您可以将其设置为引用或请求ID),那么您就可以使用or调用“完成”活动,成功或失败

启动任务有两种方法:

  • 为一项新活动投票。您将设置一个CloudWatch计划事件,以便每n分钟进行一次呼叫

  • 与step函数中的活动同时启动新的“启动器”任务。此启动器执行与#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"
    

    AWS Step函数现在支持将长时间运行的步骤作为第一类进行异步回调

    这与上面@mixja的答案相似,但简化了。工作流中的单个状态可以直接调用Lambda、SNS、SQS或ECS,并等待调用
    sendstasksucture

    有一个为SQS编写的文档,其中一个step函数发送消息并暂停工作流执行,直到有东西提供回调。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 script/CLI的aws stepfunctions send task success--task token$token,或者类似的API调用