在开发无服务器应用的时候,通过纯手工方式创建各种资源的话,效率会非常低,因此需要借助一些框架来提高开发效率。下面介绍如何在serverless中开发API(Lambda)。

一个简单的Lambda
首先创建文件:api/common/Responses.js。这个文件把每个API调用都需要的返回信息封装了一下,以便重用。
const common_headers = {
'Content-Type': 'application/json',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Origin': '*',
};
const Responses = {
HTTP_RESPONSE(http_code, response_data = {}) {
return {
headers: common_headers,
statusCode: http_code,
body: JSON.stringify(response_data)
}
}
}
module.exports = Responses
接下来创建Lambda的相应函数,添加api/students/getStudent.js:
const Responses = require('../common/Responses');
const student_data = {
9901: {name: "Anna Smith", age: 18, subject: "Economics"},
9902: {name: "Jack Chen", age: 20, subject: "Math"},
9903: {name: "Lucy Male", age: 19, subject: "History"}
};
exports.handler = async event => {
console.log('event', event);
if (!event.pathParameters || !event.pathParameters.ID) {
// 如果没有ID参数,报错
return Responses.HTTP_RESPONSE(400, {message: 'missing the ID from the path.'});
}
let ID = event.pathParameters.ID;
if(student_data[ID]) {
// 返回数据
return Responses.HTTP_RESPONSE(200, student_data[ID]);
}
HHT
// 如果有ID参数,但找不到相应记录,也报错
return Responses.HTTP_RESPONSE(500, {message: 'cannot find this ID.'})
}
至此,需要的JS文件都有了,下面在serverless.yml中进行定义:
service: service-05-api
provider:
name: aws
region: eu-west-1
runtime: nodejs14.x
lambdaHashingVersion: 20201221
profile: default
functions:
getStudent:
handler: api/students/getStudent.handler
events:
- http:
path: get-student/{ID}
method: GET
cors: true
进行发布:
sls deploy
其输出结果类似这样:
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service service-05-api.zip file to S3 (39.26 kB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
....................................
Serverless: Stack update finished...
Service Information
service: service-05-api
stage: dev
region: eu-west-1
stack: service-05-api-dev
resources: 13
api keys:
None
endpoints:
GET - https://xxxxxx8.execute-api.eu-west-1.amazonaws.com/dev/get-student/{ID}
functions:
getStudent: service-05-api-dev-getUser
layers:
None
对API的payload进行合法性验证
在serverless中,可以定义一个json格式的schema,然后将其与API相关联,后续接收到请求时,AWS都会根据给定的schema进行数据的合法性验证。
比如定义user.json:
{
"definitions": {},
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"title": "Object metadata Schema",
"required": ["email", "firstName", "lastName"],
"properties": {
"email": { "type": "string" },
"firstName": { "type": "string" },
"lastName": { "type": "string" }
}
}
然后在serverless.yml中定义API时,通过这个json文件对request中的payload进行验证:
addUser:
handler: src/functions/user/addUser.handler
description: 'add user'
memorySize: 256
events:
- http:
path: api/user/new
method: post
cors: true
request:
schemas:
application/json:
schema: ${file(serverless/schemas/user/user.json)}
name: addUserRequestValidation
description: 'Validate user'
这样在通过/api/user/new调用这个API时,就需要传递如下格式的参数:
{
"email": "xxx",
"firstName": "xxx",
"lastName": "xxx"
}
需要注意的是,在使用serverless-offline插件进行开发的时候,目前版本的插件不支持http.request.schemas(不会进行验证),但仍支持旧版的http.request.schema。
使用serverless webpack对Lambda进行优化
待解决的问题
当使用serverless开发多个Lambda时,由于每个JS都可能有自己的依赖库,于是发布Lambda的时候,serverless会把所有相关的资源都打包并发布,因此会导致文件过大。甚至在有些时候会出现无法进行发布的问题。如果登入到AWS控制台,有些Lambda会有这样的提示:
The deployment package of your Lambda function “xxxx” is too large to enable inline code editing. However, you can still invoke your function.
这个时候,就需要使用webpack对Lambda进行优化了。最终的目标是:使用webpack来确保我们只部署需要的代码到AWS。当我们的代码量增加时,针对某一个lambda,只部署与其相关的代码。
安装依赖库
npm install --save serverless-webpack
npm install --save webpack
修改serverless.yml
在serverless.yml中,添加插件:
plugins:
- serverless-webpack
同时添加设置:
package:
individually: true
添加webpack.config.js
添加webpack配置文件: /webpack.config.js
module.exports = {
target: 'node',
mode: 'production',
}
测试
重新部署:
sls deploy
可以看到lambda瘦身成功,同时其代码被压缩了。
这是在使用webpack之前的.serverless目录,可以看到,所有的Lambda会被压缩成一个zip:
ls -alh .serverless\
total 84K
drwxr-xr-x 1 lscoding lscoding 0 Nov 26 11:42 ./
drwxr-xr-x 1 lscoding lscoding 0 Nov 26 11:56 ../
-rw-r--r-- 1 lscoding lscoding 2.1K Nov 26 11:42 cloudformation-template-create-stack.json
-rw-r--r-- 1 lscoding lscoding 12K Nov 26 11:42 cloudformation-template-update-stack.json
-rw-r--r-- 1 lscoding lscoding 19K Nov 26 11:42 serverless-state.json
-rw-r--r-- 1 lscoding lscoding 39K Nov 26 11:42 service-05-api.zip
这是在使用webpack之后的目录,可以看到,getStudent这个Lambda被单独打包成一个zip:
ls -alh .serverless\
total 48K
drwxr-xr-x 1 lscoding lscoding 0 Nov 26 11:58 ./
drwxr-xr-x 1 lscoding lscoding 0 Nov 26 11:58 ../
-rw-r--r-- 1 lscoding lscoding 2.1K Nov 26 11:58 cloudformation-template-create-stack.json
-rw-r--r-- 1 lscoding lscoding 11K Nov 26 11:58 cloudformation-template-update-stack.json
-rw-r--r-- 1 lscoding lscoding 716 Nov 26 11:58 getStudent.zip
-rw-r--r-- 1 lscoding lscoding 19K Nov 26 11:58 serverless-state.json
如果把webpack.config.js中的”production”改为”none”并再次部署,
sls deploy function -f getStudent
可以看到,webpack把getUser这个函数以及所需要的文件Responses.js打包成了一个单一文件,并发布到了Lambda。
其输出结果类似这样:
sls deploy function -f getStudent
Serverless: Bundling with Webpack...
asset api/students/getStudent.js 1.04 KiB [emitted] [minimized] (name: api/students/getStudent)
./api/students/getStudent.js 846 bytes [built] [code generated]
./api/common/Responses.js 757 bytes [built] [code generated]
webpack compiled successfully in 335 ms
Serverless: Copying existing artifacts...
Serverless: Packaging function: getStudent...
Serverless: Uploading function: getStudent (716 B)...
Serverless: Successfully deployed function: getStudent
Serverless: Configuration did not change. Skipping function configuration update.
进一步的依赖库问题
如果按照上面的方法分开打包,每个Lambda对应的zip文件都会变得很小,但有些Lambda在运行时会报错:
"errorType": "Runtime.ImportModuleError",
"errorMessage": "Error: Cannot find module 'ms'
Require stack:
- /var/task/node_modules/@opensearch-project/opensearch/lib/Transport.js
这时就需要单独为该Lambda指定依赖库了:
search:
handler: src/functions/OpenSearch.handler
package:
patterns:
- 'node_modules/@opensearch-project/opensearch/**'
- 'node_modules/debug/**'
- 'node_modules/ms/**'
- 'node_modules/hpagent/**'
- 'node_modules/secure-json-parse/**'
Lambda存储空间限制的问题
在默认情况下,Lambda只能使用512MB临时存储空间,但在2022年的时候,AWS允许Lambda使用最多10 GB的空间:
https://aws.amazon.com/blogs/compute/using-larger-ephemeral-storage-for-aws-lambda/
在serverless中可以这样:
https://www.serverless.com/framework/docs/providers/aws/guide/functions
functions:
helloEphemeral:
handler: handler.handler
ephemeralStorageSize: 10240
Graviton2 Powered AWS Lambda Functions
基于Graviton2的Lambda说白了就是更快,更省钱。要想在serverless中使用Graviton2 Lambda,需要:
安装/配置插件
npm i [email protected]:teamgenie/serverless-aws-lambda-arch.git
或者:
npm install @cloudwiry/[email protected]
更新serverless.yml:
plugins:
- serverless-aws-lambda-arch
# ......
functions:
dummyFn2_RunningInARM64:
handler: handler
description: "Test function running on Graviton2"
architectures: arm64