我可以使用 lambda 函数安排 lambda 函数的执行吗?

Posted

技术标签:

【中文标题】我可以使用 lambda 函数安排 lambda 函数的执行吗?【英文标题】:Can I schedule a lambda function execution with a lambda function? 【发布时间】:2017-07-10 12:21:09 【问题描述】:

我正在寻找以编程方式安排 lambda 函数与另一个 lambda 函数一起运行一次的能力。例如,我使用datetime 参数向myFirstFunction 发出请求,然后在那个日期和时间让mySecondFunction 执行。这是否仅适用于无状态 AWS 服务?我试图避免永远在线的 ec2 实例。

我发现调度 lambda 函数的大部分结果都与 cloudwatch 和定期安排的事件有关,而不是临时事件。

【问题讨论】:

【参考方案1】:

这是aws step functions 的完美用例。

使用Wait 状态和SecondsPathTimestampPath 在执行下一个状态之前添加所需的延迟。

【讨论】:

是的!在我问这个问题时,阶梯函数不可用,但它们现在肯定是答案!【参考方案2】:

您想要做的事情(从 Lambda 调度 Lambda)使用当前的 AWS 服务是不可能的。

因此,为了避免永远在线的 ec2 实例,还有其他选择:

1) 使用 AWS 默认或自定义指标。例如,您可以使用ApproximateNumberOfMessagesVisible 或CPUUtilization(如果您的应用程序在处理请求时触发大量 CPU 使用)。您还可以创建自定义指标并在您的实例空闲时触发它(取决于您的实例中运行的应用程序)。

此选项的问题是您会浪费已付费的分钟数(AWS 总是按一小时收费,无论您是否使用实例 15 分钟)。

2) 在我看来,更好的选择是每分钟运行一次 Lambda 函数来检查您的实例是否处于空闲状态,并仅在它们接近整小时时关闭它们。

import boto3
from datetime import datetime

def lambda_handler(event, context):
    print('ManageInstances function executed.')
    environments = [['instance-id-1', 'SQS-queue-url-1'], ['instance-id-2', 'SQS-queue-url-2'], ...]
    ec2_client = boto3.client('ec2')
    for environment in environments:
        instance_id = environment[0]
        queue_url = environment[1]
        print 'Instance:', instance_id
        print 'Queue:', queue_url
        rsp = ec2_client.describe_instances(InstanceIds=[instance_id])
        if rsp:
            status = rsp['Reservations'][0]['Instances'][0]
            if status['State']['Name'] == 'running':
                current_time = datetime.now()
                diff = current_time - status['LaunchTime'].replace(tzinfo=None)
                total_minutes = divmod(diff.total_seconds(), 60)[0]
                minutes_to_complete_hour = 60 - divmod(total_minutes, 60)[1]
                print 'Started time:', status['LaunchTime']
                print 'Current time:', str(current_time)
                print 'Minutes passed:', total_minutes
                print 'Minutes to reach a full hour:', minutes_to_complete_hour
                if(minutes_to_complete_hour <= 2):
                    sqs_client = boto3.client('sqs')
                    response = sqs_client.get_queue_attributes(QueueUrl=queue_url, AttributeNames=['All'])
                    messages_in_flight = int(response['Attributes']['ApproximateNumberOfMessagesNotVisible'])
                    messages_available = int(response['Attributes']['ApproximateNumberOfMessages'])
                    print 'Messages in flight:', messages_in_flight
                    print 'Messages available:', messages_available
                    if(messages_in_flight + messages_available == 0):
                        ec2_resource = boto3.resource('ec2')
                        instance = ec2_resource.Instance(instance_id)
                        instance.stop()
                        print('Stopping instance.')
            else:
                print('Status was not running. Nothing is done.')
        else:
            print('Problem while describing instance.')

【讨论】:

我会说 wuthor 试图避免安排诸如 1 分钟检查之类的事情。坦率地说,这看起来不像生产解决方案【参考方案3】:

更新 - 我不建议使用这种方法。当 TTL 删除发生并且它们不接近 TTL 时间时,情况发生了变化。唯一的保证是该项目将在 TTL 之后被删除。感谢@Mentor 强调这一点。

2 个月前,AWS 宣布了 DynamoDB 项目 TTL,它允许您插入项目并在您希望将其删除时进行标记。到时候会自动删除。

您可以将此功能与 DynamoDB Streams 结合使用来实现您的目标 - 您的第一个函数将一个项目插入到 DynamoDB 表中。记录 TTL 应该在您希望触发第二个 lambda 时。设置一个触发您的第二个 lambda 的流。在此 lambda 中,您将识别删除事件,如果这是删除,则运行您的逻辑。

加分项 - 您可以使用表格项作为第一个 lambda 向第二个 lambda 传递参数的机制。

关于 DynamoDB TTL: https://aws.amazon.com/blogs/aws/new-manage-dynamodb-items-using-time-to-live-ttl/

【讨论】:

As docs say:DynamoDB 通常会在过期后 48 小时内删除过期项目。 所以,这不是解决方案。 @Mentor - 你是对的。当我写这篇文章时,据我所知,文档并没有包含它。此外,我可以确认最近我注意到 TTL 删除确实比以前花费了更多时间。我不知道发生了什么变化,但我可以确认它不像以前那样接近 TTL 时间。感谢您的评论。【参考方案4】:

这确实取决于您的用例,但是您想在以后触发某些东西的想法是一种常见的模式。我做无服务器的方式是我有一个反应应用程序,它触发一个动作来存储未来的日期。我采用像 24-12-2020 这样的日期格式,然后使用 date() 对其进行转换,研究了提到的日期格式是正确的,所以我可能会尝试 12-24-2020 看看我得到了什么(!)。当我高兴时,我将它转换为 javascript React 中的 Unix 数字,我使用以下代码:

new Date(action.data).getTime() / 1000

其中 action.data 是日期,也可能是动作的时间。

我在 Amplify(无服务器)中运行 React,并将其存储到 dynamodb(无服务器)。然后我运行一个 Lambda 函数(无服务器)来检查我的 dynamodb 是否有任何日期(我现在实际上使用 Unix 时间)并时不时地比较两个 Unix 日期(存储),这两个日期都是数字,所以比较很容易。在我看来,这非常简单且非常可靠。

我只是根据所需的大致频率将 Lambda 上的 crontab 设置为所需的任何值,在大多数情况下,每五分钟运行一次 lambda 是相当不错的,尽管如果我只是在某个时区为企业运行它工作日应用程序我会控制 Lambda 多一点。 Lambda 对每月前 100 万个函数是免费的,并且每隔几分钟运行一次也不会产生任何费用。显然情况会发生变化,因此您需要在您所在的地区进行查找。

在这种情况下,您永远无法获得完美的时机。但是,对于绝大多数用例来说,它会根据 Lambda 函数的时间设置足够接近,您可以将其设置为每分钟检查一次或每天仅检查一次,这完全取决于您的应用程序。

或者,如果我想对事件做出即时反应,我可能会使用 SMS、SQS 或 Kinesis 即时流式传输消息,这完全取决于您的使用案例。

【讨论】:

【参考方案5】:

我会选择在 myFirstFunction 中使用 message timers 将延迟工作排入 SQS。

目前,您不能将 SQS 用作 Lambda 事件源,但您可以定期安排 mySecondFunction 通过安排的 CloudWatch 事件(有点类似于您找到的其他选项的变体)检查队列,或者使用 CloudWatch ApproximateNumberOfMessagesVisible 上的警报以将 SNS 消息发送到 Lambda,并避免对经常长时间不活动的队列进行持续轮询。

【讨论】:

以上是关于我可以使用 lambda 函数安排 lambda 函数的执行吗?的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 的Lambda函数式接口Stream,都安排上了!

aws lambda 上的保留并发不会阻止 lambda 进行更多扩展?

Lambda冷启动可能的解决方案?

为啥使用 lambda 函数?

如何使用 lambda 将 s3 中的最新代码部署到 lambda 函数

lambda 函数有啥用? [复制]