各位观众老爷,大家好!今天咱们聊点儿时髦的,说说 JavaScript 在 Serverless 架构里的那些事儿。特别是 AWS Lambda 和 Google Cloud Functions,这两个大佬手里的 JS 运行时,到底是怎么玩的。
开场白:Serverless,这货到底是什么?
Serverless,直译过来就是“无服务器”。但注意,这可不是真的不用服务器了! 而是说,你不用再操心服务器的配置、维护、扩展这些破事儿了。这些都交给云服务商去搞定,你只管写代码,然后让它跑起来就行。
想象一下:你写了一个简单的函数,比如一个计算器,你不用买服务器、装操作系统、配置 Web 服务器…直接把代码丢给 AWS Lambda 或者 Google Cloud Functions,它就能跑起来,而且自动伸缩,按需付费。是不是很爽?
JavaScript + Serverless = 珠联璧合?
为啥要用 JavaScript 搞 Serverless?原因很简单:
- 前端开发者的福音: 大部分前端开发者都熟悉 JavaScript,用它来写后端逻辑,学习成本低,上手快。
- Node.js 的流行: Node.js 让 JavaScript 也能在服务器端运行,生态系统庞大,各种库和框架应有尽有。
- 事件驱动: Serverless 架构本身就是事件驱动的,而 JavaScript 的异步特性非常适合处理各种事件。
AWS Lambda 的 JavaScript 运行时
AWS Lambda 支持多种语言的运行时,JavaScript(Node.js)是其中之一。
1. Lambda 函数的结构
一个 Lambda 函数,本质上就是一个 JavaScript 函数,它接收两个参数:
event
: 触发函数的事件数据。比如,如果是 HTTP 请求,event
里就包含了请求的 headers、body、query parameters 等信息。context
: 提供了关于函数执行、环境和调用信息的对象。比如,你可以用context.log()
来记录日志,或者用context.getRemainingTimeInMillis()
来查看剩余的执行时间。
下面是一个简单的 Lambda 函数的例子:
exports.handler = async (event, context) => {
console.log('Received event:', JSON.stringify(event, null, 2));
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'Hello from Lambda!',
input: event,
}),
};
return response;
};
这个函数接收一个事件,然后返回一个包含 "Hello from Lambda!" 消息的 JSON 响应。
2. 如何部署 Lambda 函数?
部署 Lambda 函数,通常有几种方式:
- AWS 控制台: 最简单的方式,可以直接在 AWS 控制台里上传你的代码(zip 文件),或者在线编辑代码。
- AWS CLI: 通过命令行工具,可以更方便地自动化部署流程。
- AWS CloudFormation/Serverless Framework/Terraform: 这些基础设施即代码(IaC)工具,可以让你用配置文件来定义和管理你的 Lambda 函数和其他 AWS 资源。
这里我们简单介绍一下用 AWS CLI 部署 Lambda 函数的步骤:
-
打包代码: 把你的 JavaScript 代码和
node_modules
目录打包成一个 zip 文件。zip -r my-lambda-function.zip .
-
创建 Lambda 函数: 使用
aws lambda create-function
命令。aws lambda create-function --function-name my-lambda-function --runtime nodejs18.x --role arn:aws:iam::YOUR_ACCOUNT_ID:role/lambda_basic_execution --handler index.handler --zip-file fileb://my-lambda-function.zip
--function-name
: Lambda 函数的名字。--runtime
: 使用的 Node.js 运行时版本。--role
: Lambda 函数的 IAM 角色,用于授权函数访问其他 AWS 资源。--handler
: 入口函数,格式为文件名.函数名
。--zip-file
: 包含代码的 zip 文件。
-
更新 Lambda 函数代码: 如果你需要更新代码,可以使用
aws lambda update-function-code
命令。aws lambda update-function-code --function-name my-lambda-function --zip-file fileb://my-lambda-function.zip
3. Lambda 函数的触发器
Lambda 函数需要被触发才能执行。AWS 提供了多种触发器:
- API Gateway: 将 Lambda 函数暴露为 HTTP API。
- S3: 当 S3 桶里的对象发生变化时(比如上传、删除),触发 Lambda 函数。
- DynamoDB: 当 DynamoDB 表里的数据发生变化时,触发 Lambda 函数。
- CloudWatch Events: 按照预定的时间间隔触发 Lambda 函数 (Cron job)。
- SQS: 当 SQS 队列里有消息时,触发 Lambda 函数。
- SNS: 当 SNS 主题收到消息时,触发 Lambda 函数。
4. 实际案例:一个简单的 API Gateway + Lambda 函数
假设我们要创建一个简单的 API,当用户访问 /hello
路径时,返回 "Hello from Lambda!"。
-
Lambda 函数代码:
exports.handler = async (event, context) => { const response = { statusCode: 200, headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message: "Hello from Lambda!" }) }; return response; };
-
API Gateway 配置:
- 在 API Gateway 里创建一个新的 API。
- 创建一个新的 Resource,路径设置为
/hello
。 - 创建一个新的 Method,类型设置为
GET
。 - 将这个 Method 与你的 Lambda 函数关联起来。
- 配置 Integration Request,选择 Lambda Function,并选择你的 Lambda 函数。
- 配置 Integration Response,将 Lambda 函数的输出映射到 HTTP 响应。
- 部署 API。
-
测试:
部署完成后,你可以通过 API Gateway 提供的 URL 访问
/hello
路径,应该能看到 "Hello from Lambda!" 的 JSON 响应。
Google Cloud Functions 的 JavaScript 运行时
Google Cloud Functions 也支持 JavaScript(Node.js)运行时,与 AWS Lambda 类似,但也有一些区别。
1. Cloud Functions 的结构
一个 Cloud Function 也是一个 JavaScript 函数,它接收两个参数:
req
: HTTP 请求对象。 包含了请求的 headers、body、query parameters 等信息。如果是其他类型的触发器,req
会包含不同的数据。res
: HTTP 响应对象。 可以用它来发送响应给客户端。
下面是一个简单的 Cloud Function 的例子:
exports.helloWorld = (req, res) => {
console.log('Received request:', req.body);
res.status(200).json({
message: 'Hello from Cloud Functions!',
input: req.body,
});
};
这个函数接收一个 HTTP 请求,然后返回一个包含 "Hello from Cloud Functions!" 消息的 JSON 响应。
2. 如何部署 Cloud Functions?
部署 Cloud Functions,可以使用 Google Cloud CLI (gcloud
)。
-
编写代码: 编写你的 JavaScript 代码,并创建一个
package.json
文件,指定依赖项。 -
部署函数: 使用
gcloud functions deploy
命令。gcloud functions deploy helloWorld --runtime nodejs18 --trigger-http --allow-unauthenticated
helloWorld
: Cloud Function 的名字。--runtime
: 使用的 Node.js 运行时版本。--trigger-http
: 指定触发器类型为 HTTP。--allow-unauthenticated
: 允许未经身份验证的访问。
3. Cloud Functions 的触发器
Google Cloud Functions 提供了多种触发器:
- HTTP: 将 Cloud Function 暴露为 HTTP API。
- Cloud Storage: 当 Cloud Storage 桶里的对象发生变化时,触发 Cloud Function。
- Cloud Pub/Sub: 当 Cloud Pub/Sub 主题收到消息时,触发 Cloud Function。
- Cloud Firestore: 当 Cloud Firestore 数据库里的数据发生变化时,触发 Cloud Function。
- Cloud Scheduler: 按照预定的时间间隔触发 Cloud Function (Cron job)。
4. 实际案例:一个简单的 HTTP Cloud Function
假设我们要创建一个简单的 HTTP API,当用户访问 /hello
路径时,返回 "Hello from Cloud Functions!"。
-
Cloud Function 代码:
exports.helloWorld = (req, res) => { res.status(200).json({ message: "Hello from Cloud Functions!" }); };
-
部署:
gcloud functions deploy helloWorld --runtime nodejs18 --trigger-http --allow-unauthenticated
-
测试:
部署完成后,你可以通过 Google Cloud Platform 提供的 URL 访问
/hello
路径,应该能看到 "Hello from Cloud Functions!" 的 JSON 响应。
AWS Lambda vs. Google Cloud Functions: 简单对比
特性 | AWS Lambda | Google Cloud Functions |
---|---|---|
运行时支持 | Node.js, Python, Java, Go, Ruby, .NET, 自定义运行时 | Node.js, Python, Go, Java, .NET, Ruby, PHP |
触发器 | API Gateway, S3, DynamoDB, CloudWatch Events, SQS, SNS 等 | HTTP, Cloud Storage, Cloud Pub/Sub, Cloud Firestore, Cloud Scheduler 等 |
部署方式 | AWS 控制台, AWS CLI, CloudFormation, Serverless Framework, Terraform | Google Cloud CLI |
身份验证 | IAM Roles, API Gateway 身份验证 | IAM, Identity Platform, API Keys |
监控与日志 | CloudWatch | Cloud Logging |
本地开发与测试 | SAM CLI, Serverless Framework | Functions Framework |
价格 | 基于函数执行时间和内存使用量 | 基于函数执行时间和内存使用量 |
冷启动时间 | 通常情况下,Node.js 相对较快。 | 通常情况下,Node.js 相对较快。 |
最大执行时间 | 15 分钟 | 9 分钟 |
进阶话题:最佳实践和注意事项
- 代码组织: 使用模块化的方式组织你的代码,方便维护和测试。
- 依赖管理: 使用
package.json
管理你的依赖项,确保环境一致性。 - 错误处理: 完善的错误处理机制,避免程序崩溃。
- 日志记录: 使用
console.log()
或context.log()
记录关键信息,方便调试。 - 安全性: 注意权限控制,避免未授权访问。
- 性能优化: 避免长时间运行的任务,优化代码性能,减少冷启动时间。
- 环境配置: 使用环境变量来配置你的函数,避免硬编码敏感信息。
真实案例演示:图片处理服务
假设我们要创建一个图片处理服务,当用户上传图片到 S3 桶时,Lambda 函数会自动生成缩略图。
-
Lambda 函数代码:
const AWS = require('aws-sdk'); const sharp = require('sharp'); const s3 = new AWS.S3(); exports.handler = async (event) => { const bucket = event.Records[0].s3.bucket.name; const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/+/g, ' ')); const params = { Bucket: bucket, Key: key, }; try { const image = await s3.getObject(params).promise(); const buffer = image.Body; const resizedImage = await sharp(buffer) .resize(200, 200) .toBuffer(); const newKey = `thumbnails/${key}`; const uploadParams = { Bucket: bucket, Key: newKey, Body: resizedImage, ContentType: 'image/jpeg', // 根据实际情况修改 }; await s3.putObject(uploadParams).promise(); console.log('Thumbnail created successfully:', newKey); return { statusCode: 200, body: JSON.stringify({ message: 'Thumbnail created successfully!' }), }; } catch (error) { console.error('Error creating thumbnail:', error); return { statusCode: 500, body: JSON.stringify({ message: 'Error creating thumbnail!', error: error.message }), }; } };
-
配置 S3 触发器:
- 在 S3 桶里配置一个事件通知,当对象创建时,触发 Lambda 函数。
-
测试:
- 上传一张图片到 S3 桶,Lambda 函数会自动生成一个缩略图,并保存在
thumbnails
目录下。
- 上传一张图片到 S3 桶,Lambda 函数会自动生成一个缩略图,并保存在
总结:Serverless 的未来
Serverless 架构正在变得越来越流行,它简化了应用开发、部署和运维流程,降低了成本,提高了效率。 JavaScript 作为一种流行的编程语言,在 Serverless 领域有着广泛的应用前景。
希望今天的分享能帮助大家更好地理解 JavaScript 在 Serverless 架构里的应用。 谢谢大家!
(下课! 欢迎大家提问。)