在云计算的浪潮中,“无服务器”(Serverless)已经从一个时髦的流行词演变为一种主流的软件架构范式。它彻底改变了开发者构建和部署应用程序的方式,将他们从繁琐的服务器管理工作中解放出来。而在这场变革的核心,正是 Amazon Web Services (AWS) 推出的旗舰级无服务器计算服务——AWS Lambda。本文将作为一份详尽的指南,不仅会引导您完成第一个无服务器应用的构建,更会深入探讨其核心概念、最佳实践、成本优化策略以及企业级应用中的高级模式,帮助您真正掌握 AWS Lambda 并构建高效、可扩展且经济的无服务器架构。
无服务器革命:为何选择 AWS Lambda?
在深入技术细节之前,我们必须理解这场架构革命的动因。传统的应用部署模式,无论是物理服务器、虚拟机(VMs)还是容器(Containers),都要求开发者或运维团队承担大量的底层设施管理责任。这包括:
- 服务器采购与配置: 预测流量,选择合适的硬件规格,进行物理安装或虚拟化配置。
- 操作系统管理: 安装、更新、打补丁,处理安全漏洞。
- 容量规划与扩展: 监控资源使用率,在流量高峰前手动或通过复杂的自动化脚本进行扩容,在流量低谷时缩容以节省成本。这往往导致资源浪费或响应延迟。
- 高可用性与容错: 配置负载均衡器,设置跨可用区的冗余部署,处理硬件故障。
这些工作繁琐、耗时且与核心业务逻辑无关。无服务器计算的出现,正是为了解决这些痛点。其核心理念是:开发者只需关注代码,将服务器管理的一切事宜交由云服务商处理。
AWS Lambda 正是这一理念的完美实践者。它是一个事件驱动的计算服务,允许您在无需预置或管理服务器的情况下运行代码。它的核心优势体现在以下几个方面:
- 零服务器管理: 您永远不需要登录到任何服务器。AWS 会自动处理计算资源的预置、扩展、补丁更新和操作系统维护。
- 按实际使用付费: 计费模型极其精细。您只需为代码实际运行的时间(以毫秒为单位)和发出的请求次数付费。代码不运行时,不产生任何费用。这与始终运行的服务器相比,成本效益极高。
- 自动弹性伸缩: Lambda 会根据收到的事件或请求数量,在毫秒级别内自动、精确地扩展您的应用程序。无论是每秒几次请求还是数千次请求,Lambda 都能平稳应对,无需任何手动干预。
- 事件驱动模型: Lambda 函数可以由超过200种 AWS 服务(如 Amazon S3, DynamoDB, API Gateway, SQS)以及自定义事件源触发,使其成为构建微服务、数据处理管道和自动化工作流的理想粘合剂。
从本质上讲,AWS Lambda 让开发者能够将精力百分之百地投入到创造业务价值的代码上,而不是消耗在维护基础设施的“无差别重活”上。这不仅仅是技术上的进步,更是生产力模式的根本性变革。
AWS Lambda 核心概念深度剖析
要精通 Lambda,必须理解其内部的工作机制。这些概念是设计高效、可靠的无服务器应用的基础。
1. 函数(Function)与运行时(Runtime)
函数是您上传到 Lambda 的代码单元。它可以是一个简单的脚本,也可以是一个包含依赖项的复杂应用。每个 Lambda 函数都有一个处理程序(Handler),这是函数代码中作为执行起点的特定方法或函数。
运行时则为您的代码提供了一个语言特定的执行环境。AWS 官方支持多种主流语言的运行时,如 Node.js, Python, Java, Go, Ruby, .NET Core 等。您也可以通过自定义运行时(Custom Runtime)来运行几乎任何语言编写的代码。选择合适的运行时不仅关系到开发效率,也对性能和冷启动时间有一定影响。
2. 事件(Event)与触发器(Trigger)
Lambda 的世界是事件驱动的。函数不会凭空运行,它总是由某个事件触发。这个事件可以是一个 HTTP 请求、一个上传到 S3 存储桶的新文件、一条写入 DynamoDB 表的新记录,或者一条进入 SQS 队列的消息。
触发器是您配置的、用于连接事件源和 Lambda 函数的桥梁。例如,您可以创建一个触发器,将 Amazon API Gateway 的某个 REST API 端点连接到您的 Lambda 函数。当该端点收到 HTTP 请求时,API Gateway 就会将请求的详细信息(如路径、请求头、请求体)打包成一个 JSON 格式的事件对象,然后调用您的 Lambda 函数,并将此事件对象作为输入参数传递给处理程序。
// Node.js 处理程序示例,接收来自 API Gateway 的事件
exports.handler = async (event) => {
// 'event' 对象包含了所有关于 HTTP 请求的信息
console.log('Received event:', JSON.stringify(event, null, 2));
const response = {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
message: 'Hello from Lambda!',
input: event,
}),
};
return response;
};
3. 执行上下文(Execution Context)
执行上下文是 Lambda 性能优化的关键所在。它是一个临时的运行时环境,包含了函数运行所需的一切,例如内存、CPU 资源以及函数代码及其所有依赖项。
当一个函数首次被调用或在闲置一段时间后再次被调用时,Lambda 需要创建一个新的执行上下文。这个过程被称为冷启动(Cold Start)。冷启动包括以下步骤:
- 下载您的代码包。
- 在一个安全的微型虚拟机(Firecracker)中启动执行环境。
- 加载运行时(如 Node.js 引擎)。
- 运行函数代码中位于处理程序之外的初始化代码(Init Phase)。
冷启动会带来额外的延迟,通常在几十毫秒到几秒不等,具体取决于代码包大小、语言和初始化逻辑的复杂性。
一旦执行上下文被创建,Lambda 会在一定时间内重用它来处理后续的调用。这被称为热启动(Warm Start)。在热启动中,执行上下文已经准备就绪,Lambda 只需直接调用处理程序(Handler Phase),因此延迟极低。
优化技巧: 开发者应该充分利用执行上下文的重用特性。将数据库连接、SDK 客户端初始化、大型配置文件的加载等昂贵操作放在处理程序之外的全局作用域中。这样,这些操作只会在冷启动时执行一次,后续的热启动调用可以共享这些已初始化的资源,从而显著提升性能。
// Node.js 优化示例
const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient(); // 在处理程序外部初始化,只在冷启动时执行一次
exports.handler = async (event) => {
// 在这里直接使用已初始化的 dynamoDB 客户端
const params = {
TableName: 'MyTable',
Key: { id: event.pathParameters.id }
};
const data = await dynamoDB.get(params).promise();
// ...
return { statusCode: 200, body: JSON.stringify(data.Item) };
};
4. 无状态性(Statelessness)
Lambda 函数被设计为无状态的。这意味着您不能假设两次连续的调用会落在同一个执行上下文中。AWS 可能会为了扩展或替换旧环境而随时创建新的执行上下文。因此,任何需要在多次调用之间持久化的状态信息,都必须存储在外部服务中,如 Amazon DynamoDB(键值数据库)、Amazon S3(对象存储)或 Amazon RDS(关系数据库)。
将状态外部化是构建可扩展、容错的无服务器应用的核心原则。它使得您的函数可以无限扩展,因为每个函数实例都是独立和可互换的。
5. IAM 角色与权限
安全是 AWS 的最高优先级。每个 Lambda 函数都必须关联一个 IAM (Identity and Access Management) 角色。这个角色定义了函数在执行期间拥有的权限。
遵循最小权限原则(Principle of Least Privilege)至关重要。您应该只授予函数完成其任务所必需的最小权限集。例如,如果一个函数只需要从一个特定的 DynamoDB 表中读取数据,那么它的 IAM 角色策略应该只允许 `dynamodb:GetItem` 和 `dynamodb:Query` 操作,并且仅限于那张表的 ARN (Amazon Resource Name)。这极大地减小了潜在安全漏洞的影响范围。
实战演练:构建一个动态待办事项 API
现在,让我们通过一个实际项目来巩固这些概念。我们将构建一个简单的待办事项(To-Do)应用的后端 API。这个 API 将支持创建、读取、更新和删除待办事项,并将数据持久化到 Amazon DynamoDB 中。我们将使用 AWS Lambda 和 Amazon API Gateway。
我们将分三步完成:
- 第一部分: 设置基础环境,包括 DynamoDB 表和 IAM 角色。
- 第二部分: 编写并部署处理不同 HTTP 方法(POST, GET, PUT, DELETE)的 Lambda 函数。
- 第三部分: 配置 API Gateway,将 API 端点路由到我们的 Lambda 函数。
这是一个典型的无服务器架构图:
+-------------+ +-----------------+ +----------------+ +-----------------+
| Client |----->| API Gateway |----->| AWS Lambda |----->| Amazon DynamoDB |
| (Web/Mobile)| | (REST API) | | (Function) | | (Table) |
+-------------+ +-----------------+ +----------------+ +-----------------+
| | ^ | ^ |
| HTTP Request | | (Proxy Event) | | (SDK Call) |
|---------------->| | | | |
| |------->| |------->| |
| | | | | |
| | | (JSON Response)| | (Data) |
| <---------------| | | <------| |
| HTTP Response | | | | |
+----------------------------------------------------------------------+
第一部分:环境准备
1. 创建 DynamoDB 表
- 登录 AWS 管理控制台,导航到 DynamoDB 服务。
- 点击“创建表”。
- 表名:
TodoTable - 分区键:
id(类型选择 字符串) - 其他设置保持默认,点击“创建表”。
这个表将用于存储我们的待办事项,每个事项通过一个唯一的 `id` 来标识。
2. 创建 IAM 角色
- 导航到 IAM 服务。
- 选择“角色”,然后点击“创建角色”。
- 选择可信实体类型: 选择 “AWS 服务”。
- 使用案例: 在下拉菜单中选择 “Lambda”,然后点击“下一步”。
- 添加权限: 搜索并选中 `AWSLambdaBasicExecutionRole` 策略。这个策略授予 Lambda 函数将日志写入 Amazon CloudWatch Logs 的权限,这对于调试至关重要。
- 点击“下一步”。
- 现在,我们需要添加与 DynamoDB 交互的权限。点击“创建策略”。
- 在新打开的窗口中,选择 “JSON” 选项卡,并粘贴以下策略文档。请确保将 `YOUR_AWS_ACCOUNT_ID` 和 `YOUR_AWS_REGION` 替换为您的实际值。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Scan"
],
"Resource": "arn:aws:dynamodb:YOUR_AWS_REGION:YOUR_AWS_ACCOUNT_ID:table/TodoTable"
}
]
}
- 点击“下一步: 标签”,再点击“下一步: 审核”。
- 策略名称:
TodoTableDynamoDBPolicy - 点击“创建策略”。
- 返回到创建角色的浏览器窗口,刷新权限策略列表,搜索并选中刚刚创建的 `TodoTableDynamoDBPolicy`。
- 点击“下一步”。
- 角色名称:
TodoLambdaRole - 点击“创建角色”。
现在我们有了一个具备写入日志和操作 `TodoTable` 所需最小权限的 IAM 角色。
第二部分:编写 Lambda 函数
为了简化,我们将使用一个 Lambda 函数来处理所有的 API 请求,并通过 `event.httpMethod` 来区分是创建、读取、更新还是删除操作。
- 导航到 AWS Lambda 服务。
- 点击“创建函数”。
- 选择“从头开始创作”。
- 函数名称:
todoApiHandler - 运行时: 选择 `Node.js 18.x` 或更高版本。
- 架构: 保持 `x86_64` 默认值。
- 权限: 展开“更改默认执行角色”,选择“使用现有角色”,然后从下拉列表中选择我们刚刚创建的 `TodoLambdaRole`。
- 点击“创建函数”。
函数创建后,您会进入函数代码编辑器。将以下 Node.js 代码粘贴到 `index.mjs` (或 `index.js`) 文件中,并点击 "Deploy" 保存更改。
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
DynamoDBDocumentClient,
ScanCommand,
PutCommand,
GetCommand,
DeleteCommand,
} from "@aws-sdk/lib-dynamodb";
import { randomUUID } from "crypto";
const client = new DynamoDBClient({});
const dynamo = DynamoDBDocumentClient.from(client);
const tableName = "TodoTable";
export const handler = async (event, context) => {
let body;
let statusCode = 200;
const headers = {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*", // 允许跨域请求
};
try {
switch (event.httpMethod) {
case "DELETE":
await dynamo.send(
new DeleteCommand({
TableName: tableName,
Key: {
id: event.pathParameters.id,
},
})
);
body = `Deleted item ${event.pathParameters.id}`;
break;
case "GET":
if (event.pathParameters != null) {
// 获取单个 item
body = await dynamo.send(
new GetCommand({
TableName: tableName,
Key: {
id: event.pathParameters.id,
},
})
);
body = body.Item;
} else {
// 获取所有 items
body = await dynamo.send(new ScanCommand({ TableName: tableName }));
body = body.Items;
}
break;
case "POST":
const requestJSON = JSON.parse(event.body);
const newItem = {
id: randomUUID(),
task: requestJSON.task,
completed: false,
createdAt: new Date().toISOString(),
};
await dynamo.send(
new PutCommand({
TableName: tableName,
Item: newItem,
})
);
body = newItem;
statusCode = 201;
break;
case "PUT":
const idToUpdate = event.pathParameters.id;
const updateData = JSON.parse(event.body);
await dynamo.send(
new PutCommand({
TableName: tableName,
Item: {
id: idToUpdate,
task: updateData.task,
completed: updateData.completed,
},
})
);
body = `Updated item ${idToUpdate}`;
break;
default:
throw new Error(`Unsupported method "${event.httpMethod}"`);
}
} catch (err) {
statusCode = 400;
body = err.message;
} finally {
body = JSON.stringify(body);
}
return {
statusCode,
body,
headers,
};
};
这段代码:
- 使用 AWS SDK for JavaScript v3 与 DynamoDB 交互。
- 通过 `switch` 语句根据 `event.httpMethod`(由 API Gateway 提供)来执行不同的数据库操作。
- GET /todos: 使用 `Scan` 操作获取所有待办事项。
- GET /todos/{id}: 使用 `GetCommand` 获取指定 ID 的事项。
- POST /todos: 解析请求体,生成一个唯一的 ID 和时间戳,使用 `PutCommand` 创建新事项。
- DELETE /todos/{id}: 使用 `DeleteCommand` 删除指定 ID 的事项。
- PUT /todos/{id}: 解析请求体,使用 `PutCommand` 更新(或创建)指定 ID 的事项。
- 实现了基本的错误处理,并返回符合 API Gateway 代理集成格式的响应对象。
第三部分:配置 API Gateway
现在我们需要创建一个公共的 HTTP 端点,并将请求转发到我们的 Lambda 函数。
- 导航到 Amazon API Gateway 服务。
- 在 REST API 部分,点击“构建”。
- 选择“新建 API”。
- API 名称: `TodoApi`
- 端点类型: `区域性`
- 点击“创建 API”。
API 创建后,我们需要定义资源(Resources)和方法(Methods)。
1. 创建 `/todos` 资源
- 在“资源”树中,选中根资源 `/`。
- 点击“操作”下拉菜单,选择“创建资源”。
- 资源名称: `todos` (资源路径会自动填充为 `/todos`)。
- 点击“创建资源”。
2. 在 `/todos` 上创建 GET 和 POST 方法
- 选中刚刚创建的 `/todos` 资源。
- 点击“操作” > “创建方法”。
- 从下拉列表中选择 `GET`,然后点击对勾。
- 集成类型: 选择“Lambda 函数”。
- 勾选“使用 Lambda 代理集成”。
- Lambda 函数: 开始输入 `todoApiHandler` 并从列表中选择它。
- 点击“保存”。API Gateway 会提示您授予调用 Lambda 函数的权限,点击“确定”。
- 重复以上步骤,为 `/todos` 资源创建一个 `POST` 方法,同样配置为与 `todoApiHandler` 函数的代理集成。
3. 创建 `/todos/{id}` 资源
- 选中 `/todos` 资源。
- 点击“操作” > “创建资源”。
- 资源名称: `id`
- 资源路径: 填写 `{id}`。这创建了一个路径参数。
- 点击“创建资源”。
4. 在 `/todos/{id}` 上创建 GET, PUT, DELETE 方法
- 选中 `/todos/{id}` 资源。
- 按照与之前相同的方式,分别为其创建 `GET`, `PUT`, 和 `DELETE` 方法。
- 将这三个方法都配置为与 `todoApiHandler` 函数的 Lambda 代理集成。
5. 部署 API
我们已经定义了 API 的结构,现在需要将其部署到一个公共的 URL 上。
- 点击“操作” > “部署 API”。
- 部署阶段: 选择“[新阶段]”。
- 阶段名称: `prod` (或 `v1`)
- 点击“部署”。
部署成功后,控制台会显示一个“调用 URL”。这个 URL 就是您 API 的根端点,类似 `https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/prod`。
6. 测试 API
您可以使用 Postman、curl 或任何 HTTP 客户端来测试您的 API。
- 创建事项 (POST):
curl -X POST \ 'https://YOUR_API_ID.execute-api.REGION.amazonaws.com/prod/todos' \ -H 'Content-Type: application/json' \ -d '{"task": "Learn Serverless"}' - 获取所有事项 (GET):
curl 'https://YOUR_API_ID.execute-api.REGION.amazonaws.com/prod/todos' - 获取单个事项 (GET): (将 `{id}` 替换为上一步返回的 ID)
curl 'https://YOUR_API_ID.execute-api.REGION.amazonaws.com/prod/todos/{id}' - 删除事项 (DELETE):
curl -X DELETE 'https://YOUR_API_ID.execute-api.REGION.amazonaws.com/prod/todos/{id}'
恭喜!您已经成功构建并部署了一个功能完备的、基于 AWS Lambda 和 API Gateway 的无服务器 REST API。
超越基础:迈向生产级无服务器应用
在控制台中手动点击创建资源对于学习和原型设计来说非常棒,但对于生产环境,我们需要更强大、可重复和可版本化的方法。这引出了基础设施即代码(Infrastructure as Code, IaC)的概念。
基础设施即代码 (IaC) 与 AWS SAM
IaC 允许您使用代码(如 YAML 或 JSON 模板)来定义和管理您的云资源。这带来了诸多好处:
- 自动化: 一键部署整个应用栈,包括 Lambda 函数、API Gateway、DynamoDB 表和 IAM 角色。
- 版本控制: 将您的基础设施定义与应用代码一起存储在 Git 中,可以跟踪变更、进行代码审查和轻松回滚。
- 可重复性: 在不同环境(开发、测试、生产)中创建完全相同的资源配置,避免“在我机器上能跑”的问题。
AWS Serverless Application Model (SAM) 是 AWS 官方推出的一个开源框架,专门用于简化无服务器应用的定义和部署。它是 AWS CloudFormation 的一个扩展,提供了一种更简洁的语法来声明无服务器资源。
下面是我们刚才手动创建的待办事项 API 的 SAM 模板 (`template.yaml`) 示例:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
A simple serverless API for a To-Do application
Globals:
Function:
Timeout: 10
Runtime: nodejs18.x
MemorySize: 128
Resources:
TodoApiHandlerFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: index.handler
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref TodoTable
Events:
GetTodos:
Type: Api
Properties:
Path: /todos
Method: get
CreateTodo:
Type: Api
Properties:
Path: /todos
Method: post
GetTodoById:
Type: Api
Properties:
Path: /todos/{id}
Method: get
UpdateTodo:
Type: Api
Properties:
Path: /todos/{id}
Method: put
DeleteTodo:
Type: Api
Properties:
Path: /todos/{id}
Method: delete
TodoTable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: id
Type: String
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
这个模板清晰地定义了:
- 一个名为 `TodoApiHandlerFunction` 的 Lambda 函数,其代码位于 `src/` 目录,处理程序是 `index.handler`。
- 通过 SAM 的策略模板,自动为函数附加了对 `TodoTable` 的 CRUD 权限。
- 通过 `Events` 属性,SAM 会自动创建 API Gateway 并将指定的路径和方法路由到该函数。
- 一个名为 `TodoTable` 的 DynamoDB 表,分区键为 `id`。
使用 AWS SAM CLI,您可以通过简单的命令 `sam build` 和 `sam deploy` 来打包和部署整个应用,极大地提高了开发和运维效率。
监控、日志与可观测性
在无服务器世界中,由于没有可登录的服务器,强大的监控和日志记录能力变得至关重要。AWS 提供了完善的工具来确保您的应用是可观测的。
- Amazon CloudWatch Logs: Lambda 函数中的所有 `console.log` 输出(以及其他标准输出)都会自动发送到 CloudWatch Logs。您可以在这里搜索、过滤和分析日志,以诊断问题。为日志组设置合理的保留策略以控制成本。
- Amazon CloudWatch Metrics: Lambda 自动为每个函数发布一系列关键指标,如调用次数(Invocations)、错误数(Errors)、持续时间(Duration)、并发执行数(ConcurrentExecutions)等。您应该为关键指标(如错误率)设置 CloudWatch 警报,以便在出现问题时及时收到通知。
- AWS X-Ray: 对于由多个 Lambda 函数和 AWS 服务组成的复杂微服务架构,X-Ray 提供了分布式跟踪能力。它能帮助您可视化请求的完整路径,识别性能瓶颈和错误发生的具体环节。在 Lambda 函数和 API Gateway 中启用 X-Ray 跟踪通常只需在控制台或 IaC 模板中勾选一个选项。
采用结构化日志(如 JSON 格式)是一个很好的实践,它能让您在 CloudWatch Logs Insights 中使用类似 SQL 的查询语言对日志进行更强大的分析。
// 结构化日志示例
console.log(JSON.stringify({
level: "INFO",
message: "User successfully created",
userId: "user-123",
requestId: context.awsRequestId // 包含请求 ID,便于跟踪
}));
成本优化策略
AWS Lambda 的按用量付费模式使其本身就具有很高的成本效益,但通过一些策略,您可以进一步优化开销。
- 内存与 CPU 的权衡: Lambda 的 CPU 能力与您为其分配的内存大小成正比。有时,为一个计算密集型任务分配更多内存,虽然每毫秒的单价更高,但因为执行时间大幅缩短,总成本反而会下降。使用 AWS Lambda Power Tuning 等工具可以帮助您找到函数的最优内存配置。
- 选择合适的架构: AWS Lambda 支持 x86 和基于 ARM 的 AWS Graviton2 处理器。Graviton2 实例通常能提供高达 20% 的性价比提升。对于大多数运行时(如 Node.js, Python),切换到 ARM 架构(`arm64`)通常无需修改代码,是一个简单有效的降本方式。
- 管理冷启动: 对于延迟敏感的应用,可以使用预置并发(Provisioned Concurrency)来预热一定数量的执行环境,确保它们始终处于准备就绪状态,从而消除冷启动。这会产生额外的成本,因此需要权衡性能需求和预算。对于非关键任务,接受冷启动是更经济的选择。
- 善用事件过滤: 对于由 SQS, Kinesis, DynamoDB Streams 等事件源触发的 Lambda,可以使用事件源映射的过滤功能。这样,Lambda 函数只会被符合特定条件的事件调用,避免了不必要的调用和成本。
结论:无服务器是一种思维模式
AWS Lambda 及其周边的无服务器生态系统不仅仅是一套技术工具,它更代表了一种全新的应用构建和运维思维。它要求开发者从传统的、以服务器为中心的视角,转向以事件为中心、以业务逻辑为核心的视角。
掌握 AWS Lambda,意味着您学会了如何利用云的极致弹性来构建响应迅速、高度可用且成本效率极高的应用程序。从简单的 API 后端、数据处理管道,到复杂的事件驱动微服务架构,Lambda 都是现代云原生应用中不可或缺的基石。
本文为您提供了一个坚实的起点,从核心概念到动手实践,再到生产环境的最佳实践。云计算的世界在不断演进,但无服务器所倡导的“关注价值,而非设施”的核心思想将持续引领未来的技术潮流。现在,是时候开始您的无服务器构建之旅了。
0 개의 댓글:
Post a Comment