调用 AWS Lambda 时如何传送字节数组

本文主要验证用 Python 写的 AWS Lambda 与 Java 客户端之间如何双向传递二进制数据,这里不涉及到 Lambda 流输入输出的问题。比如一个 Python AWS Lambda 的处理方法声明是

def lambda_handler(event, context):
    pass  # or do something

通过我们用 Lambda 调用时会传给 event 一个 JSON 格式的字符串,反应到 AWS Lambda 时 event 就是一个字典。但当要传递二进制数据如何做呢?直觉的做法就是用 base64 编码二进制字节为普通的字符串,比如要节约网络传输的数据量,需要对文本进行压缩,格式可以是这样

{"input": base64Encode(gzipCompress("text content......"))}

然后在 Lambda 端取出 input 的值作相应的 base64 解码再解压缩。

对于大文本,即使是压缩后再编码为 base64 也比直接传送原始文本数据要节约网络带宽。

这种方案实际也是可行的,然而我们在实际使用 Java AWS Lambda SDK 时有些动作会自动帮我们实现的,那就是二进制数据自动 base64 编码。 阅读全文 >>

用 C++ 写一个 AWS Lambda Hello World

AWS 自 2014 年推出 Lambda 时仅支持 Node.js,而后添加了对 Python, Ruby, Java, C#, F#, PowerShell 的支持,再来到 2018 年可以自定义运行时了,比如用性能较好的 C, C++, Rust, Go 等语言。见 AWS Lambda Now Supports Custom Runtimes and Enables Sharing Common Code Between Functions.

如果使用 Python, Java 写 Lambda 时觉得还不得快,不想要明显的预热过程,也许 1000 毫秒的任务只想要 600 毫秒就能完成,内存还希望再压缩一些,那着实能在每月千百万次 Lambda 调用的情况下节省一笔可观的支出,那么可以试一试 C, C++, Rust, Go 等编译成了机器指令的语言,况且前三者没有 GC, 执行效率会更高。

本日志记录一下如何用 C++ 创建一个 AWS Lambda, 以及可如何应付 Lambda 的复用。本文主要参考自下面两处

  1. Introducing the C++ Lambda Runtime
  2. GitHub 项目 aws-lambda-cpp

自定义运行时可选择 X86_64 或 arm64 的 Amazon Linux 2023 或 Amazon Linux 2。部署时可选择的 runtime 相应有 provided.al2023, provided.al2, 推荐使用 provided.al2023。runtime provided 不被支持了。

C++ 代码可选择用 GCC 或 Clang 来编译,既然 AWS Lambda 实际的运行时会用到 Amazon Linux 2023,那我们就直接选择 Docker 镜像 amazonlinux:2023 作为我们的编译环境。 阅读全文 >>

AWS Step Function 异步动态调用 Lambda 后汇集结果

分布式计算有这么一个需求,主进程准备好输入数据,然后根据输入中某个 Items 动态调用若干计算进程,待到所有计算完成后再汇集结果。这一需求移植到 AWS 上就像是下面这样子

但在一个 Lambda 中同步调用其他 Lambda 时就有个费时费钱的问题,虽然我们采用线程池来调用 Lambda2, 由于每个同步调用的耗时不相同, Lambda1 最终要等待最慢的那个调用结束后才能对所有结果进行聚集处理。这就是著名的“长板效应”, Lambda1 多数时候是在无谓的等待当中消耗着你的钱财。 阅读全文 >>

应用 AWS Lambda 部署 FastAPI

前两年用 AWS Lambda 搭配 API Gateway 使用是为了省钱,因为没有请求时不花钱。又由于是 Rest API, 所以实现部分用了 FastAPI 的装饰器,但不实际启动 FastAPI 的 Web 服务,Lambda 的 handler 方法根据 routeKey 手动映射到 FastAPI 的装饰方法。大概实现是

def lambda_handler(event: dict, context):
    fastapi_function = locate_fastapi_function(event['routeKey'])
    return fastapi_function(<extract parameters from event>)

当时也思考着能不能把 Lambda 的请求与 FastAPI 的 Web 服务桥接起来,却又不能真正启动一个  Web 服务,否则 Lambda 调用不能结束。比如说 AWS Lambda 收到请求时快速启动 FastAPI 服务,该服务绑定到  TCP 端口或 Socket 文件都行,然后 Lambda 请求代理到 FastAPI 服务,最后关闭 FastAPI 服务,但是想来都不那么容易实现。 阅读全文 >>

配置 AWS Lambda Python Logging

通常在 Python 应用中简单的配置使用内置的 logging 是这样的

假如文件名为 test.py, 用 python test.py 执行后输出

2022-01-25 21:02:47,231 - INFO - test(<module>:6) - hello world

在 Lambda 中的现象

可是这同样的代码放到 AWS Lambda Python 代码中却不灵验了,logging.info() 将得不到任何输出。 阅读全文 >>

构建 AWS Lambda Python Docker 镜像

AWS 的 Lambda 在 2020-12-01 开始支持用 Docker 镜像存放代码,见 New for AWS Lambda - Container Image Support。AWS Lambda 最初的对发布包的限制是 50M, 解压后(因为执行前需要解压缩)不能超过 250M,对于压缩比小于 1/5 的包来说,要突破 50M 部署包的限制就要用 2018-11-29 推出的层(layer), 即把 Lambda 的依赖可以组织为层,每个 Lambda 可引用最多 5 个层,但最终 Lambda 加上层所解压后的大小仍然有 250 M 的限制。

对于使用了大量依赖的 Lambda,比如 Python 中用了 Pandas 之类的数学分析包,250M 的大小是不够的,所以才有了 Docker 镜像化的 Lambda, 镜像的大小限制一下蹦到 10G,要构建出一个 10G Lambda 用的 Linux 镜像, 那绝对是个巨兽,至少目前是超越我的想像力,除非往里面塞入大量的业务数据。关于 Lambda 有哪些限制,请参阅 Lambda quotas

介绍完 Lambda 引入 Docker 镜像的背景后,本文接下来只关注如何构建一个 Python Lambda 镜像,对于如何部署 Docker 化的 Lambda, 不在本文的范围之内。主要的参考文档为 AWS Lambda 官方的 Deploy Python Lambda functions with container images. 阅读全文 >>

DynamoDB Stream 应用及触发 Lambda 函数

DynamoDB Stream 的实质就是一个依附于表的流,对表的增删改像关系型数据的触发器一样,以日志形式按顺记录到该流中。我们可以用 API 去读取其中的记录,或用来触发一个 Lambda。DynamoDB Stream 非常类似于 Kinesis 的 Stream, 它们都是有 Shard 的概念,但是 DynamoDB Stream 的 Shard 数目是不太确定的。而且还能用 KCL(Kinesis Client Library) 来操作 DynamoDB Stream。

DynamoDB Stream 和 Kinesis Stream 有几个通用的 API 操作,像 list_streams(), describe_stream(), get_shard_iterator() 和 get_records() 函数。同时呢,在设置 Lambda 的触发器时,选择 DynamoDB Stream 与 Kinesis Stream 时可配置的参数几乎是一样的,有 Batch size, Batch window, Starting position 以及重试策略。而且也是一个 Shard 只能同时启动一个 Lambda 实例,由于 DynamoDB Stream 的 Shard 数目不太确定,所以它能同时启动几个 Lambda 实例也不确定的。

另外, 一个 DynamoDB Stream 只能最多被两个 Consumer 消费,而可用来消费 Kinesis Stream 的 Consumer 数目是不受限的。DynamoDB Stream 中的记录保存时间为 24 小时, Kinesis Stream 中记录保存时间也是可配置的。我们创建一个 DynamoDB 表时还能启用 Amazon Kinesis data stream details, 即把对 DynamoDB 的操作记录直接发送到 Kinesis Stream 中去,这样就能操作熟悉的 Kinesis Stream,Shard 数目可设定,坏处就是 Kinesis Stream 较费钱。 阅读全文 >>

AWS Python Lambda 使用 Layer

使用 Python 书写 AWS Lambda 的一个好处就是能够在控制台中直接编辑源代码,非常方便进行快速验证测试 AWS 环境相关的。这只限于使用 AWS 为 Python Lambda 运行时提供的默认组件(比如 boto3),尚若需要在自己的 Python Lambda 中使用其他的组件(如 redis), 就不得不把自己的代码及依赖打成一个 zip 包再部署,这时候就无法在控制台直接编辑代码了,也只能坠入本地修改代码,重新打包上传测试的循环当中。

欲了解 Python Lambda 中除了 boto3 外还能直接使用别的什么组件,可点击此链接 https://gist.github.com/gene1wood/4a052f39490fae00e0c3 查看当前。该 gist 也还提供了代码 code to run in Lambda 来获得所有依赖。试了下在 Python Lambda 中,用通常的

help('modules')    # 或
help('modules package')

竟然连大名鼎鼎的 boto3 都无法列出来。

回到正题来,如果既想用第三方的依赖,又想要在控制台中直接编辑代码进行测试,是否有他法呢?有,那就是 AWS 在 2018 年 11 月推出的 Lambda 层。见 AWS Lambda Now Supports Custom Runtimes and Enables Sharing Common Code Between Functions, 这里的层除了能用来提供 Python 依赖,还许自定义运行时,如 C++ 或 Rust 等写 Lambda 都不是梦。

AWS 的服务就像个大口袋,何时偷偷的加添了什么服务,或出了什么新的我,不时关注它的 What's New with AWS 必是个好习惯。 阅读全文 >>

Amazon SQS 触发 AWS Lambda 及重试/DLQ

Amazon 在 2018 年 6 月份宣布可以设置用 SQS 来触发 Lambda,SQS 不再是单纯用于 ECS 服务中,或用于伸缩控制的。这儿就来亲自尝试一下用 SQS 驱动的 Lambda,以及要注意的要素。

首先使用 Java 编写 Lambda 的话,AWS 在 com.amazonaws:aws-lambda-java-events:2.20 版本开始加入了 com.amazonaws.services.lambda.runtime.events.SQSEvent 类,可是这个版本的 aws-lambda-java-events 是有所限的,因为 SQSEvent.SQSMessage 类是私有的,这就造成不能获取到 SQSEvent 中的记录数据。

//下面的操作代码无法编译,因为 SQSEvent.SQSMessage 是私有的,不可访问
SQSEvent.SQSMessage sqs = sqsEvent.getRecords().get(0); 
sqsEvent.getRecords().get(0).getBody();

Java 使用 SQS 来驱动 Lambda 的话,至少需要 com.amazonaws:aws-lambda-java-events:2.2.1 版本,从此 SQSEvent.SQSMessage 变成 public 了。该版本是于  2018 年 6 月传到 Maven 官方中央仓库的,这就是那时才能真正用来写 Java 的 SQS 触发的 Lambda.

同时此篇也是作为上文 AWS Lambda 重试与死信队列(DLQ) 的一个很重要的补充。在此也会验证 SQS 触发的 Lambda 的重试机制以及 DLQ 相关的内容。 阅读全文 >>

AWS Lambda 重试与死信队列(DLQ)

AWS Lambda 允许设置 Debugging and error handling, 在 Lambda 出现异常,达到最大的重试次数后,把以下信息放到选择的 SNS 或 SQS 主题作为死信队列(DLQ - Dead Letter Queue),包括

  1. 原始 Lambda 接收到的消息(基于 SNS 和 SQS 消息的总大小,可能会被截取,本人猜测,尤其是 Kinesis 的消息会比较大)
  2. 原始 Lambda 的 RequestId
  3. ErrorCode(三位数字的 HTTP 错误码)
  4. ErrorMessage, 即原 Lambda 抛出 Exception 的 getMessage() 信息,截取 1 KB 字符串

并且 Lambda 要使用 DLQ 的话还必须设置当前 Lambda 的 IAM role 有对于 SNS/SQS 主题相应的 sns:Publish 和 sqs:SendMessage 权限。

AWS Lambda 基本重试规则:对于 Kinesis 消息会无限重试直至消息过期,对于 SNS 或 SQS 的消息出现异常后会再重试两次。参考:AWS Lambda Retry Behavior

而在重试次数用完后仍然失败,并且设置了 DLQ 的话就会发送消息到 DLQ 中去。 阅读全文 >>