
AWS Lambda 允许设置
Debugging and error handling, 在 Lambda 出现异常,达到最大的重试次数后,把以下信息放到选择的 SNS 或 SQS 主题作为死信队列(DLQ - Dead Letter Queue),包括- 原始 Lambda 接收到的消息(基于 SNS 和 SQS 消息的总大小,可能会被截取,本人猜测,尤其是 Kinesis 的消息会比较大)
- 原始 Lambda 的 RequestId
- ErrorCode(三位数字的 HTTP 错误码)
- 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 中去。 Read More
当 AWS Lambda 由 Kinesis 消息来触发时,一个 Kinesis Shard 会相应启动一个 Lambda 实例,比如说 Kinesis Stream 有 5 个 Shards, 那同时只会启动 5 个 Lambda 实例。那么把多条消息发送到同一个 Kinesis Shard 中去,这些消息会被如何消费呢?答案是按顺消息,不管这些消息是否被不同的 Lambda 实例处理。本文就是关于怎么去理解 https://aws.amazon.com/lambda/faqs/ 的下面那段话的内容:Q: How does AWS Lambda process data from Amazon Kinesis streams and Amazon DynamoDB Streams?
可以做几个试验,下面的代码可以保证消息总是被发送到同一个 Kinesis Shard,因为 PartitionKey 参数是一个常量 Read More
AWS Lambda 如何处理来自于 Amazon Kinesis 和 DynamoDB 的数据 The Amazon Kinesis and DynamoDB Streams records sent to your AWS Lambda function are strictly serialized, per shard. This means that if you put two records in the same shard, Lambda guarantees that your Lambda function will be successfully invoked with the first record before it is invoked with the second record. If the invocation for one record times out, is throttled, or encounters any other error, Lambda will retry until it succeeds (or the record reaches its 24-hour expiration) before moving on to the next record. The ordering of records across different shards is not guaranteed, and processing of each shard happens in parallel. 从 Kinesis 和 DynamoDB 单个 Shard 上的记录会被 Lambda 严格的按序处理。这意味着如果你送两条记录到相同的 Shard, Lambda 将会保证第一条记录成功处理后才会处理第二条记录。假如处理第一条记录时超时,或超过资源使用上限,或碰到任何错误, Lambda 将会不断重试直到成功(或记录在 24 小时后过期), 而后才会去处理下一条记录。跨 Shard 的记录不保证到达顺序,且是并行处理多个 Shard 来的记录。
一句话概要:对 Lambda 环境变量的任何改动都会引起一次 Lambda 的冷启动,大可放心在 handleRequest(...) 方法外使用环境变量。
从 AWS 上 Java Lambda 应用记要 中,我学到了 Lambda 的实例是跨请求共享的,所以为使用 Lambda 配置的环境变量时曾写出了下面复杂而多余的 AWS Lambda 代码:这段代码看起来很在理,既然 Lambda 实例是共享的,那么在必变环境变量之后就可能不会重新初始始化实例,所以在每次的请求方法中对比如果环境变量值改动了就重新用最新的配置值来初始化线程池。然而上面的代码结结实实是多余的,真是把 Lambda 想得太简单了,如果是很多环境变量岂不是逐一判断。 Read More1public class Handler implements RequestHandler<SNSEvent, String> { 2 3 private int threadPoolSize = getThreadPoolSizeFromEnv(); 4 private ExecutorService threadPool = Executors.newFixedThreadPool(threadPoolSize); 5 6 @Override 7 public String handleRequest(SNSEvent snsEvent, Context context) { 8 int configuredThreadPoolSize = getThreadPoolSizeFromEnv(); 9 if(configuredThreadPoolSize != threadPoolSize) { 10 threadPoolSize = configuredThreadPoolSize; 11 threadPool = Executors.newFixedThreadPool(threadPoolSize); 12 } 13 14 return "Hello Lambda"; 15 } 16 17 private int getThreadPoolSizeFromEnv() { 18 return Integer.parseInt(System.getenv().getOrDefault("threadpool_size", "50")); 19 } 20}
直接一句话:去掉 Log4J 的依赖,把 Slf4J, Logback, 和 log4j-over-slf4j 依赖加进来就行了,配置文件换成 logback.xml,这就完了,不要往下看了,都是些废话。当我们用 Serverless 命令sls create -t aws-java-maven -p hello-lambda创建的示例项目中直接用的是 Log4J 日志组件,而且也没用像 Slf4j, 或 Apache Common Logging 更上一层的通用日志框架。查看了几个 AWS 本身的组件 S3, SNS, 和 Kinesis 的 SDK, 它们内部是用的 Apache Common Logging 声明的日志变量import org.apache.commons.logging.LogFactory;
而我们自己的组件中通用日志组件是 Slf4j, 底层实现为 Logback, 所以我们希望在 Lambda 中使用 Logback 来写日志。
import org.apache.commons.logging.Logger;
private static final Log log = LogFactory.getLog(AmazonKinesis.class)
选用一个通用日志框架总是明智之举,因为一个项目经常杂糅了多种日志实现,使用 Slf4J 或 Apache Common Logging 可以把它们(Log4J, Logback, 或更多)输出到共同的目的地,并且有统一的日志输出接口。而我们认为通用日志框架还是 Slf4J 要先进些,所以我们在 Java Lambda 中的日志方案是 Slf4J + Logback,还需要把 Log4J 的日志桥接到 Slf4J 上来,再经由 Logback 输出。
回到前面创建的hello-lambda项目,看其中怎么用的 Log4J,先瞧瞧pom.xml文件怎么引入的 Log4J, 它是间接通过一个 AWS 定义的 Log4J Appender 引入的 Read More
AWS 的 Lambda 给了那些不想自己管理 EC2 服务器和配置负载人员很大的便利,所以 Lambda 被描述为 Serverless。真正的只关注业务就行,怎么调度,同时有多少个实例运行交给亚马逊去处理就是了。运行 Lambda 的环境也是亚马逊内部的 EC2 服务器,镜像是 Amazon Linux, 所以如果想运行系统命令,那是 Linux 的。Lambda 支持多种语言 Node.js, Python, C#(.net core), 还有 Java 8,我们就选择了 Java 8, 一开始还担心它与别的语言比起来会多大劣势,其实不然。而且所谓的 Java 8, 并非单指 Java 语言,而是指 JVM 平台,所以也可以用 Scala, Clojure, Groovy, Kotlin 来写。
Java 与脚本语言如 Node.js, Python 相比给人一个明显的感觉是启动慢,还有人用统计数据来比划 AWS Lambda cold start(pseudeo-)benchmark. 不过真不用担心,人家说的是冷启动,也就发生在部署后第一次执行启动会比较慢。要是我们的 Lambda 经常被调用,或每天触发比较集中,Lambda 在任务到来之前处理待续状态,就不会有冷启动的耗时过程。或者是每次任务要执行 3 分钟左右,又何必在乎毫秒级的冷启动时间。
说到底就是别理会下面的数据20ms startup time for Python ~ $0.04167540ms startup time for Node.js ~ $0.0833580ms startup time for Java ~ $0.1667
Lambda 实例重用
Java 的 Lambda 就是一个微服务,在首次触发时微服务冷启动有些慢,但一旦启动之后就可以用这个微服务实例接受后续的请求,只有在比较长的一段时间内未被触发 AWS 才会把这个微服务杀掉。 Read More