完整文档
您可以使用 AWS Lambda 运行代码以响应事件,例如更改 Amazon S3 存储桶或 Amazon DynamoDB 表中的数据;以及使用 Amazon API Gateway 运行代码以响应 HTTP 请求;或者使用通过 AWS SDK 完成的 API 调用来调用您的代码。
异步调用 当您异步调用函数时,Lambda 会将事件发送到队列。一个单独的进程会从队列中读取事件并运行您的函数。将事件添加到队列后,Lambda 将返回成功响应,而不返回其他信息。要异步调用函数,请将调用类型参数设置为 Event。
Lambda 管理函数的异步调用队列,并尝试自动重试失败的事件。如果函数返回错误,Lambda 会尝试再运行两次,前两次尝试之间等待一分钟,第二次与第三次之间等待两分钟。函数错误包括函数代码返回的错误,以及函数运行时返回的错误,例如超时。如果所有 3 次尝试都失败,Lambda 会将事件发送到死信队列(如果已配置)。
如果该函数没有足够的并发可用于处理所有事件,则其他请求将受到限制。对于限制错误 (429) 和系统错误(500 系列),Lambda 会将事件返回到队列并尝试再次运行该函数长达 6 小时。重试间隔从第一次尝试后的 1 秒呈指数级增加到最多 5 分钟,但如果队列已备份,则可能会更长。Lambda 还降低了从队列中读取事件的速率,并且可能无法读取队列中进一步返回的事件。事件可以位于队列中的最长时间是 4 天。
库 aws-lambda-java-core – 此库提供了 Context 对象、RequestStreamHandler 接口和 RequestHandler 接口。Context对象 (Java 中的 AWS Lambda 上下文对象) 提供有关您的 Lambda 函数的运行时信息。预定义接口提供一种定义 Lambda 函数处理程序的方法。有关更多信息,请参阅利用预定义接口创建处理程序 (Java)。
aws-lambda-java-events – 此库提供一些预定义类型,供您在编写 Lambda 函数以处理由 Amazon S3、Kinesis、Amazon SNS 和 Amazon Cognito 发布的事件时使用。这些类可帮助您处理事件,而不必自行编写自定义序列化逻辑。
Custom Appender for Log4j2.8 – 您可使用由 AWS Lambda 提供的自定义 Log4j(请参阅 Apache Log4j 2)Appender 从您的 Lambda 函数记录日志。每个对 Log4j 方法的调用(如 log.info() 或 log.error())都将生成一个 CloudWatch Logs 事件。自定义 Appender 称为 LambdaAppender 并且必须在 log4j2.xml 文件中使用。您必须在部署程序包 (.jar 文件) 中包含 aws-lambda-java-log4j2 项目 (artifactId:aws-lambda-java-log4j2)。有关更多信息,请参阅Java 中的 AWS Lambda 函数日志记录。
Custom Appender for Log4j1.2 – 您可以使用由 AWS Lambda 提供的自定义 Log4j(请参阅 Apache Log4j 1.2)Appender 从您的 Lambda 函数记录日志。有关更多信息,
编程模型 您将使用 AWS Lambda 支持的语言之一为 Lambda 函数编写代码。无论您选择哪种语言,都可以采用一个通用模式,即为包含以下核心概念的 Lambda 函数编写代码:
处理程序 – 处理程序是 AWS Lambda 调用的用于开始执行 Lambda 函数的函数。您在创建 Lambda 函数时标识处理程序。在调用 Lambda 函数时,AWS Lambda 将通过调用处理程序函数来开始执行您的代码。AWS Lambda 将任何事件数据作为第一个参数传递给处理程序。您的处理程序应处理此传入事件数据,并且可调用您的代码中的任何其他函数/方法。
上下文 – AWS Lambda 还会将上下文对象作为第二个参数传递给处理程序函数。通过此 context 对象,您的代码可与 AWS Lambda 交互。例如,您的代码可在 AWS Lambda 终止 Lambda 函数前发现剩余的执行时间。
此外,对于 Node.js 等语言,有一个使用回调的异步平台。AWS Lambda 提供了有关此 context 对象的其他方法。您将使用这些 context 对象方法指示 AWS Lambda 终止 Lambda 函数并将值返回到调用方(可选)。
日志记录 – 您的 Lambda 函数可包含日志记录语句。AWS Lambda 将这些日志写入 CloudWatch Logs。特定语言语句将生成日志条目,具体取决于您编写 Lambda 函数代码所用的语言。
日志记录需要遵循 CloudWatch Logs 限制。日志数据会由于限制而丢失;在某些情况下,当执行上下文终止时也会丢失。
异常 – 您的 Lambda 函数需要将函数执行的结果传递给 AWS Lambda。根据您编写 Lambda 函数代码所用的语言,成功结束请求或向 AWS Lambda 通知执行期间出现了错误有几种不同的方式。如果您同步调用该函数,则 AWS Lambda 会将结果转发回客户端。
并发 – 当您的函数的调用速度快于函数的单个实例可处理事件的速度时,Lambda 会通过运行其他实例来扩展您的函数。您的函数的每个实例每次仅处理一个请求,因此您无需担心同步线程或进程的问题。不过,您可以使用异步语言功能并行处理事件批次,并将数据保存到 /tmp 目录以便在同一实例上的未来调用中使用。
必须以无状态风格编写 Lambda 函数代码,且与底层的计算基础设施不存在关联。您的代码应该要求将本地文件系统访问权限、子流程和类似项目限制为请求的生命周期。持久状态应存储在 Amazon S3、Amazon DynamoDB 或其他云存储服务中。 要求函数无状态可使 AWS Lambda 根据需要启动合适数量的函数副本,以根据传入事件和请求的速率调整规模。对于不同的请求,这些函数可能不会始终运行在同一个计算实例上,且 AWS Lambda 可能会多次使用 Lambda 函数的给定实例。有关更多信息,请参阅 使用 AWS Lambda 函数的最佳实践。
最佳实践 利用执行上下文重用来提高函数性能。 确保您的代码检索到的外部化配置或依赖关系在初次执行后在本地存储和引用。限制变量/对象在每次调用时的重新初始化,而是使用静态初始化/构造函数、全局/静态变量以及单例。保持活动状态并重复使用上一次调用中建立的连接(HTTP、数据库等)。
使用 AWS Lambda 环境变量 将操作参数传递到您的函数。 例如,您在写入 Amazon S3 存储桶时,不应对要写入的存储桶名称进行硬编码,而应将存储桶名称配置为环境变量。
控制函数部署程序包中的依赖关系。AWS Lambda 执行环境中包括若干库,例如适用于 Node.js 和 Python 运行时的 AWS 开发工具包(完整列表位于此处:AWS Lambda 运行时)。Lambda 会定期更新这些库,以支持最新的功能组合和安全更新。这些更新可能会使 Lambda 函数的行为发生细微变化。要完全控制您的函数所用的依赖关系,建议在部署程序包中包装所有依赖关系。
将部署程序包大小精简为只包含运行时必要的部分。 这样会减少调用前下载和解压缩部署程序包所需的时间。对于用 Java 或 .NET Core 编写的函数,请不要将整个 AWS 开发工具包库作为部署程序包的一部分上传,而是要根据所需的模块有选择地挑选开发工具包中的组件(例如 DynamoDB、Amazon S3 开发工具包模块和 Lambda 核心库)。
将依赖关系 .jar 文件置于单独的 /lib 目录中,可减少 Lambda 解压缩部署程序包(用 Java 编写)所需的时间。这样比将函数的所有代码置于具有大量 .class 文件的同一 jar 中要快。有关说明,请参阅 Java 中的 AWS Lambda 部署程序包。
将依赖关系的复杂性降至最低。 首选执行上下文启动时可以快速加载的更简单的框架。例如,首选更简单的 Java 依赖关系注入 (IoC) 框架,如 Dagger 或 Guice,而不是更复杂的 Spring Framework。
避免在 Lambda 函数中使用递归代码,因为如果使用递归代码,函数便会自动调用自身,直到满足某些任意条件为止。这可能会导致意想不到的函数调用量和升级成本。如果您意外地执行此操作,请立即将函数并发执行数限制设置为 0 来限制对函数的所有调用,同时更新代码。
监控 cloudwatch Lambda 监控图表
调用数 – 每 5 分钟内调用函数的次数。
持续时间 – 平均、最小和最大执行时间。
错误数和成功率 (%) – 错误的数量,以及完成且没有错误的执行的百分比。
限制 – 由于并发限制而导致执行失败的次数。
IteratorAge – 对于流事件源,当 Lambda 接收该源并调用函数时,批处理中的最后一个项的存在时间。
DeadLetterErrors – Lambda 尝试写入死信队列但失败的事件数。
X-ray Lambda 作为 AWS X-Ray 跟踪
您可以在服务地图中进行放大,查看您的 Lambda 函数的跟踪视图。跟踪将显示与函数调用相关的深度信息,以分段和子分段表示。
Lambda 服务分段 – 此分段表示不同的信息,具体取决于调用函数的事件源:
同步和流事件源 – 此服务分段计算从 Lambda 服务接收请求/事件,直到请求离开 Lambda 服务(完成请求的最终调用)所经历的时间。
异步 – 此服务分段表示响应时间,即 Lambda 服务向客户端返回 202 响应所需的时间。
Lambda 服务分段可以包含两种类型的子分段:
停留时间(仅限异步调用)– 表示函数在被调用之前在 Lambda 服务中花费的时间。这个子分段在 Lambda 服务接收请求/事件时开始,在首次调用 Lambda 函数时结束。
尝试 – 表示单次调用尝试,包括 Lambda 服务引起的任何开销。开销示例包括初始化函数代码所花费的时间和函数执行时间。
Lambda 函数分段 – 表示函数针对给定调用尝试的执行时间。该分段于函数处理程序启动时开始,函数终止时结束。该分段可以包含三种类型的子分段:
初始化 – 运行函数 initialization 代码(定义为 Lambda 函数处理程序或静态初始化程序的外部代码)所花费的时间。
下游调用 – Lambda 函数的代码对其他 AWS 服务的调用。
自定义子分段 – 自定义子分段或用户注释,可以使用 X-Ray 开发工具包添加到 Lambda 函数分段中。
当您跟踪 Lambda 函数时,X-Ray 守护程序会自动在 Lambda 环境中运行,收集跟踪数据并发送到 X-Ray。进行跟踪时,X-Ray 守护程序会占用最多 16 MB 或 3% 的函数内存分配。例如,如果您为 Lambda 函数分配了 128 MB 的内存,X-Ray 守护程序会占用 16 MB 的函数内存分配。如果您为 Lambda 函数分配了 1024 MB 内存,分配给 X-Ray 守护程序的内存为 31 MB (3%)。有关更多信息,请参阅 AWS X-Ray 守护程序。
Lambda 将尝试终止 X-Ray 守护程序,以免超出函数的内存限制。例如,假设您为 Lambda 函数分配了 128 MB 内存,这就意味着 X-Ray 守护程序将获得 16 MB。这样您的 Lambda 函数获得的内存分配为 112 MB。但是,如果您的函数超过 112 MB,X-Ray 守护程序将被终止,以避免引发内存不足错误。
Java 输入输出
原始类型
POJO(不应依赖其他序列化框架)
流类型
输出直接返回Obj,不需要依赖JSON框架
上下文 当 Lambda 运行您的函数时,它会将上下文对象传递到处理程序。此对象提供的方法和属性包含有关调用、函数和执行环境的信息。
上下文方法
getRemainingTimeInMillis() – 返回在执行超时以前剩余的毫秒数。
getFunctionName() – 返回 Lambda 函数的名称。
getFunctionVersion() – 返回函数的版本。
getInvokedFunctionArn() – 返回用于调用函数的 Amazon 资源名称 (ARN)。指示调用方是否已指定版本或别名。
getMemoryLimitInMB() – 返回为函数分配的内存量。
getAwsRequestId() – 返回调用请求的标识符。
getLogGroupName() – 返回函数的日志组。
getLogStreamName() – 返回函数实例的日志流。
getIdentity() – (移动应用程序)返回有关授权请求的 Amazon Cognito 身份的信息。
getClientContext() – (移动应用程序)返回客户端应用程序向 Lambda 提供的客户端上下文。
getLogger() – 返回函数的记录器对象。
函数日志 您的 Lambda 函数带有一个 CloudWatch Logs 日志组,其中包含您的函数的每个实例的日志流。运行时会将每个调用的详细信息发送到该日志流,然后中继日志和来自您的函数代码的其他输出。
要从函数代码输出日志,您可以使用 java.lang.System 的方法或使用写入到 stdout 或 stderr 的任何日志记录模块。下面的示例使用了 System.out.println。
LambdaLogger LambdaLogger logger = context.getLogger();
适用于 Log4j 的自定义 Appender static final Logger logger = LogManager.getLogger(Hello.class);
异常错误 如果 Lambda 函数引发异常,AWS Lambda 会识别失败并将异常信息序列化为 JSON 并将其返回。下面是一个错误消息示例:
Event 调用类型(即异步执行):在这种情况下,AWS Lambda 不返回任何信息。相反,它将错误信息记录到 CloudWatch Logs 和 CloudWatch 指标中。
函数错误处理 您可以创建自定义错误处理机制,直接从您的 Lambda 函数引发异常,并直接在 AWS Step Functions 状态机中进行处理 (重试或捕获)。有关更多信息,请参阅使用状态机处理错误情况。
请将 CreateAccount 状态看作使用 Lambda 函数将客户的详细信息写入数据库的任务。
如果任务成功,会创建账户并发送欢迎电子邮件。
如果用户尝试创建的账户用户名已存在,Lambda 函数将出错,状态机会建议其他用户名,并重试账户创建过程。
您可以配置 Step Functions,使用 Catch 规则捕获错误。Lambda 会自动将错误名称设为运行时异常的完全限定类名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { "StartAt": "CreateAccount", "States": { "CreateAccount": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:CreateAccount", "Next": "SendWelcomeEmail", "Catch": [ { "ErrorEquals": ["com.example.AccountAlreadyExistsException"], "Next": "SuggestAccountName" } ] }, … } }
跟踪 在 Java 中,您可以让 Lambda 向 X-Ray 发送子分段,显示您的函数对其他 AWS 服务进行的下游调用的相关信息。要利用此功能,请在您的部署程序包中包括适用于 Java 的 AWS X-Ray 开发工具包。无需更改代码。只要您使用的 AWS 开发工具包版本为 1.11.48 或更高版本,则不需要添加任何其他代码行,就可以跟踪您的函数的下游调用。
AWS 开发工具包将动态导入 X-Ray 开发工具包,针对您的函数进行的下游调用发送子分段。您可以使用适用于 Java 的 X-Ray 开发工具包检测代码,从而发送自定义子分段和/或在 X-Ray 分段中添加注释。
以下示例使用适用于 Java 的 X-Ray 开发工具包检测 Lambda 函数,以发送自定义子分段并将自定义注释发送到 X-Ray:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package uptime;import java.io.IOException;import java.time.Instant;import java.util.HashMap;import java.util.Map;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import com.amazonaws.regions.Regions;import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;import com.amazonaws.services.dynamodbv2.model.AttributeValue;import com.amazonaws.services.lambda.runtime.Context;import com.amazonaws.xray.AWSXRay;import com.amazonaws.xray.proxies.apache.http.HttpClientBuilder;public class Hello { private static final Log logger = LogFactory.getLog(Hello.class); private static final AmazonDynamoDB dynamoClient; private static final HttpClient httpClient; static { dynamoClient = AmazonDynamoDBClientBuilder.standard().withRegion(Regions.US_EAST_1).build(); httpClient = HttpClientBuilder.create().build(); } public void checkUptime (Context context) { AWSXRay.createSubsegment("makeRequest" , (subsegment) -> { HttpGet request = new HttpGet("https://aws.amazon.com/" ); boolean is2xx = false ; try { HttpResponse response = httpClient.execute(request); is2xx = (response.getStatusLine().getStatusCode() / 100 ) == 2 ; subsegment.putAnnotation("responseCode" , response.getStatusLine().getStatusCode()); } catch (IOException ioe) { logger.error(ioe); } Map<String, AttributeValue> item = new HashMap<>(); item.put("Timestamp" , new AttributeValue().withN("" + Instant.now().getEpochSecond())); item.put("2xx" , new AttributeValue().withBOOL(is2xx)); dynamoClient.putItem("amazon-2xx" , item); }); } }
组合 AWS Lambda 会自动将开发工具包所需的凭证设置为与您的函数关联的 IAM 角色的凭证,您无需采取任何其他步骤。
SQS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 package example;import com.amazonaws.services.lambda.runtime.Context;import com.amazonaws.services.lambda.runtime.RequestHandler;import com.amazonaws.services.lambda.runtime.events.SQSEvent;import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage;public class handler implements RequestHandler <SQSEvent , Void > { @Override public Void handleRequest (SQSEvent event, Context context) { for (SQSMessage msg : event.getRecords()){ System.out.println(new String(msg.getBody())); } return null ; } } ``` ### SNS ```java package example; import java.text.SimpleDateFormat;import java.util.Calendar; import com.amazonaws.services.lambda.runtime.RequestHandler;import com.amazonaws.services.lambda.runtime.Context;import com.amazonaws.services.lambda.runtime.events.SNSEvent; public class LogEvent implements RequestHandler <SNSEvent , Object > { public Object handleRequest (SNSEvent request, Context context) { String timeStamp = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss" ).format(Calendar.getInstance().getTime()); context.getLogger().log("Invocation started: " + timeStamp); context.getLogger().log(request.getRecords().get(0 ).getSNS().getMessage()); timeStamp = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss" ).format(Calendar.getInstance().getTime()); context.getLogger().log("Invocation completed: " + timeStamp); return null ; } } ``` ### S3 ```java package example;import java.awt.Color;import java.awt.Graphics2D;import java.awt.RenderingHints;import java.awt.image.BufferedImage;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.regex.Matcher;import java.util.regex.Pattern;import javax.imageio.ImageIO;import com.amazonaws.AmazonServiceException;import com.amazonaws.services.lambda.runtime.Context;import com.amazonaws.services.lambda.runtime.RequestHandler;import com.amazonaws.services.lambda.runtime.events.S3Event;import com.amazonaws.services.s3.AmazonS3;import com.amazonaws.services.s3.event.S3EventNotification.S3EventNotificationRecord;import com.amazonaws.services.s3.model.GetObjectRequest;import com.amazonaws.services.s3.model.ObjectMetadata;import com.amazonaws.services.s3.model.S3Object;import com.amazonaws.services.s3.AmazonS3ClientBuilder;public class handler implements RequestHandler <S3Event , String > { private static final float MAX_WIDTH = 100 ; private static final float MAX_HEIGHT = 100 ; private final String JPG_TYPE = (String) "jpg" ; private final String JPG_MIME = (String) "image/jpeg" ; private final String PNG_TYPE = (String) "png" ; private final String PNG_MIME = (String) "image/png" ; public String handleRequest (S3Event s3event, Context context) { try { S3EventNotificationRecord record = s3event.getRecords().get(0 ); String srcBucket = record.getS3().getBucket().getName(); String srcKey = record.getS3().getObject().getUrlDecodedKey(); String dstBucket = srcBucket + "resized" ; String dstKey = "resized-" + srcKey; if (srcBucket.equals(dstBucket)) { System.out .println("Destination bucket must not match source bucket." ); return "" ; } Matcher matcher = Pattern.compile(".*\\.([^\\.]*)" ).matcher(srcKey); if (!matcher.matches()) { System.out.println("Unable to infer image type for key " + srcKey); return "" ; } String imageType = matcher.group(1 ); if (!(JPG_TYPE.equals(imageType)) && !(PNG_TYPE.equals(imageType))) { System.out.println("Skipping non-image " + srcKey); return "" ; } AmazonS3 s3Client = AmazonS3ClientBuilder.defaultClient(); S3Object s3Object = s3Client.getObject(new GetObjectRequest( srcBucket, srcKey)); InputStream objectData = s3Object.getObjectContent(); BufferedImage srcImage = ImageIO.read(objectData); int srcHeight = srcImage.getHeight(); int srcWidth = srcImage.getWidth(); float scalingFactor = Math.min(MAX_WIDTH / srcWidth, MAX_HEIGHT / srcHeight); int width = (int ) (scalingFactor * srcWidth); int height = (int ) (scalingFactor * srcHeight); BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = resizedImage.createGraphics(); g.setPaint(Color.white); g.fillRect(0 , 0 , width, height); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(srcImage, 0 , 0 , width, height, null ); g.dispose(); ByteArrayOutputStream os = new ByteArrayOutputStream(); ImageIO.write(resizedImage, imageType, os); InputStream is = new ByteArrayInputStream(os.toByteArray()); ObjectMetadata meta = new ObjectMetadata(); meta.setContentLength(os.size()); if (JPG_TYPE.equals(imageType)) { meta.setContentType(JPG_MIME); } if (PNG_TYPE.equals(imageType)) { meta.setContentType(PNG_MIME); } System.out.println("Writing to: " + dstBucket + "/" + dstKey); try { s3Client.putObject(dstBucket, dstKey, is, meta); } catch (AmazonServiceException e) { System.err.println(e.getErrorMessage()); System.exit(1 ); } System.out.println("Successfully resized " + srcBucket + "/" + srcKey + " and uploaded to " + dstBucket + "/" + dstKey); return "Ok" ; } catch (IOException e) { throw new RuntimeException(e); } } } ``` ### DDB ```java package example;import com.amazonaws.services.lambda.runtime.Context;import com.amazonaws.services.lambda.runtime.LambdaLogger;import com.amazonaws.services.lambda.runtime.RequestHandler;import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;import com.amazonaws.services.lambda.runtime.events.DynamodbEvent.DynamodbStreamRecord;public class DDBEventProcessor implements RequestHandler <DynamodbEvent , String > { public String handleRequest (DynamodbEvent ddbEvent, Context context) { for (DynamodbStreamRecord record : ddbEvent.getRecords()){ System.out.println(record.getEventID()); System.out.println(record.getEventName()); System.out.println(record.getDynamodb().toString()); } return "Successfully processed " + ddbEvent.getRecords().size() + " records." ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Parameters: NotificationEmail: Type: String Resources: CheckWebsitePeriodically: Type: AWS::Serverless::Function Properties: Handler: LambdaFunctionOverHttps.handler Runtime: runtime Policies: AmazonDynamoDBFullAccess Events: CheckWebsiteScheduledEvent: Type: Schedule Properties: Schedule: rate(1 minute) AlarmTopic: Type: AWS::SNS::Topic Properties: Subscription: - Protocol: email Endpoint: !Ref NotificationEmail Alarm: Type: AWS::CloudWatch::Alarm Properties: AlarmActions: - !Ref AlarmTopic ComparisonOperator: GreaterThanOrEqualToThreshold Dimensions: - Name: FunctionName Value: !Ref CheckWebsitePeriodically EvaluationPeriods: 1 MetricName: Errors Namespace: AWS/Lambda Period: 60 Statistic: Sum Threshold: '1' ``` ## 持续集成 您可以使用 AWS CodePipeline 为 Lambda 应用程序创建持续交付管道。CodePipeline 结合了源代码控制、构建和部署资源,以创建一个在您更改应用程序源代码时运行的管道。 存储库 – AWS CodeCommit 中的 Git 存储库。当您推送更改时,管道将源代码复制到 Amazon S3 存储桶并将其传递给构建项目。 构建项目 – 从管道获取源代码并打包应用程序的 AWS CodeBuild 构建。源包括构建规范,其中包含用于安装依赖项和为部署准备 AWS 无服务器应用程序模型 (AWS SAM) 模板的命令。 部署配置 – 管道的部署阶段定义一组操作,这些操作从构建输出获取 AWS SAM 模板,在 AWS CloudFormation 中创建更改集,并执行更改集以更新应用程序的 AWS CloudFormation 堆栈。 AWS CloudFormation 堆栈 – 部署阶段使用模板在 AWS CloudFormation 中创建堆栈。模板是 YAML 格式的文档,用于定义 Lambda 应用程序的资源。应用程序包括一个 Lambda 函数和一个调用它的 Amazon API Gateway API。 角色 – 管道、构建和部署各有一个允许其管理 AWS 资源的服务角色。在创建这些资源时,控制台会创建管道和构建角色。您负责创建允许 AWS CloudFormation 管理应用程序堆栈的角色。 #### CloudFormation ```json { "Statement": [ { "Action": [ "apigateway:*", "codedeploy:*", "lambda:*", "cloudformation:CreateChangeSet", "iam:GetRole", "iam:CreateRole", "iam:DeleteRole", "iam:PutRolePolicy", "iam:AttachRolePolicy", "iam:DeleteRolePolicy", "iam:DetachRolePolicy", "iam:PassRole", "s3:GetObjectVersion", "s3:GetBucketVersioning" ], "Resource": "*", "Effect": "Allow" } ], "Version": "2012-10-17" }
SAM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Outputs the time Resources: TimeFunction: Type: AWS::Serverless::Function Properties: Handler: index.handler Runtime: nodejs10.x CodeUri: ./ Events: MyTimeApi: Type: Api Properties: Path: /TimeResource Method: GET
AWS CodeBuild 构建规范 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 version: 0.2 phases: install: runtime-versions: nodejs: 10 build: commands: - npm install time - export BUCKET=lambda-deployment-artifacts-123456789012 - aws cloudformation package --template-file template.yaml --s3-bucket $BUCKET --output-template-file outputtemplate.yaml artifacts: type: zip files: - template.yaml - outputtemplate.yaml
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 wshten@gmail.com