腾讯云SCF构建Serverless API实战
Serverless架构让开发者无需管理服务器即可运行代码,按实际调用计费,冷启动快,弹性无限。腾讯云SCF(Serverless Cloud Function)是国内最成熟的Serverless平台之一。本文将通过一个完整的RESTful API项目,带你实战SCF的开发与部署。
一、Serverless架构概述
1.1 核心理念
Serverless并非"无服务器",而是将服务器管理完全交给云平台:
| 传统架构 | Serverless架构 | |---------|--------------| | 需要购买/租用服务器 | 无需管理服务器 | | 持续运行,持续计费 | 按调用次数和执行时间计费 | | 需要处理扩缩容 | 自动弹性伸缩 | | 需要运维OS和运行时 | 只关注业务代码 | | 固定成本 | 可变成本(接近零空闲成本) |
1.2 SCF与其他平台对比
| 特性 | 腾讯云SCF | 阿里云 FC | AWS Lambda | |------|----------|-----------|------| | 运行时 | Node.js/Python/Go/Java/PHP | Node.js/Python/Go/Java/PHP | Node.js/Python/Go/Java/.NET | | 最大执行时间 | 900秒 | 600秒 | 900秒 | | 内存 | 128MB-3GB | 128MB-3GB | 128MB-10GB | | 冷启动时间 | 100-300ms | 100-500ms | 50-200ms | | 免费额度 | 100万次/月 | 100万次/月 | 100万次/月 | | 预置并发 | 支持 | 支持 | 支持(Provisioned Concurrency) |
二、项目设计
我们将构建一个Todo管理API,包含以下接口:
| 方法 | 路径 | 功能 | |------|------|------| | GET | /api/todos | 获取所有待办 | | GET | /api/todos/:id | 获取单个待办 | | POST | /api/todos | 创建待办 | | PUT | /api/todos/:id | 更新待办 | | DELETE | /api/todos/:id | 删除待办 |
架构设计
客户端 → API网关 → SCF函数 → 云数据库MongoDB
↓
云监控/日志
三、函数开发
3.1 创建函数
- 登录 腾讯云SCF控制台
- 点击「新建」→ 选择「从头开始」
- 配置:
| 配置项 | 值 | 说明 | |-------|-----|------| | 函数名 | todo-api | 项目名称 | | 运行环境 | Node.js 18 | LTS版本 | | 执行方法 | index.main | 入口函数 | | 内存 | 256MB | 中等配置 | | 超时时间 | 30秒 | API响应要求 |
3.2 代码实现
'use strict';
const MongoClient = require('mongodb').MongoClient;
const MONGO_URI = process.env.MONGO_URI;
let cachedDb = null;
async function connectToDatabase() {
if (cachedDb) return cachedDb;
const client = await MongoClient.connect(MONGO_URI);
const db = client.db('todo_app');
cachedDb = db;
return db;
}
exports.main = async (event) => {
const db = await connectToDatabase();
const collection = db.collection('todos');
const method = event.httpMethod || event.requestContext?.httpMethod;
const path = event.path || event.requestContext?.path;
const body = event.body ? JSON.parse(event.body) : {};
let response;
// 路由匹配
if (method === 'GET' && path === '/api/todos') {
const todos = await collection.find({}).toArray();
response = { code: 0, data: todos };
}
else if (method === 'GET' && path.match(/^\/api\/todos\/[a-f0-9]+$/)) {
const id = path.split('/').pop();
const todo = await collection.findOne({ _id: new require('mongodb').ObjectId(id) });
response = todo ? { code: 0, data: todo } : { code: 404, message: 'Not found' };
}
else if (method === 'POST' && path === '/api/todos') {
const result = await collection.insertOne({
title: body.title,
completed: false,
createdAt: new Date()
});
response = { code: 0, data: { id: result.insertedId } };
}
else if (method === 'PUT' && path.match(/^\/api\/todos\/[a-f0-9]+$/)) {
const id = path.split('/').pop();
await collection.updateOne(
{ _id: new require('mongodb').ObjectId(id) },
{ $set: { title: body.title, completed: body.completed, updatedAt: new Date() } }
);
response = { code: 0, message: 'Updated' };
}
else if (method === 'DELETE' && path.match(/^\/api\/todos\/[a-f0-9]+$/)) {
const id = path.split('/').pop();
await collection.deleteOne({ _id: new require('mongodb').ObjectId(id) });
response = { code: 0, message: 'Deleted' };
}
else {
response = { code: 404, message: 'Route not found' };
}
return {
statusCode: response.code === 404 ? 404 : 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(response)
};
};
3.3 依赖管理
创建 package.json:
{
"name": "todo-api",
"version": "1.0.0",
"dependencies": {
"mongodb": "^6.3.0"
}
}
在SCF控制台上传代码时,需先在本地安装依赖并一起打包:
npm install
zip -r todo-api.zip . -x "node_modules/.cache/*"
四、API网关配置
API网关是SCF对外暴露HTTP接口的桥梁:
4.1 创建API服务
- 进入 API网关控制台
- 创建服务 → 选择「与SCF同地域」
- 创建API:
| 配置 | 值 | 说明 | |------|-----|------| | API名称 | TodoAPI | 标识 | | 前端类型 | HTTP | HTTP协议 | | 路径 | /api/todos | 资源路径 | | 方法 | GET/POST/PUT/DELETE | 支持多方法 | | 后端类型 | 云函数SCF | 关联SCF函数 | | 函数名 | todo-api | 目标函数 |
4.2 启用CORS
如果前后端分离部署,需要启用CORS:
- Access-Control-Allow-Origin:
https://your-frontend.com - Access-Control-Allow-Methods:
GET, POST, PUT, DELETE, OPTIONS - Access-Control-Allow-Headers:
Content-Type, Authorization
4.3 鉴权配置
API网关支持多种鉴权方式:
| 鉴权方式 | 适用场景 | 配置复杂度 | |---------|---------|----------| | 密钥对 | 服务间调用 | 低 | | OAuth 2.0 | 第三方授权 | 中 | | 自定义鉴权 | JWT等Token验证 | 中 | | 无鉴权 | 公开API | 无 |
推荐使用自定义鉴权函数验证JWT Token:
// 鉴权函数
exports.main = async (event) => {
const token = event.headers?.authorization?.replace('Bearer ', '');
try {
const decoded = require('jsonwebtoken').verify(token, process.env.JWT_SECRET);
return {
isAuthorized: true,
context: { userId: decoded.userId }
};
} catch (e) {
return { isAuthorized: false };
}
};
五、数据库集成
5.1 云数据库MongoDB
腾讯云提供完全托管的MongoDB服务:
| 规格 | 存储 | 月费参考 | 适用场景 | |------|------|---------|---------| | 1C2G | 20GB SSD | ¥320 | 开发测试 | | 2C4G | 50GB SSD | ¥650 | 小型生产 | | 4C8G | 100GB SSD | ¥1,200 | 中型生产 |
5.2 连接优化
SCF冷启动时建立数据库连接较慢,建议:
- 连接复用:将数据库连接对象存储在全局变量中
- 内网访问:MongoDB实例与SCF在同一VPC内
- 连接池:配置合理的连接池大小(10-20个连接)
5.3 替代方案:云开发TCB
如果不想管理MongoDB,可使用腾讯云开发(TCB):
const cloudbase = require('@cloudbase/node-sdk');
const app = cloudbase.init();
const db = app.database();
// 无需连接字符串,自动鉴权
const result = await db.collection('todos').get();
TCB提供免费额度,适合MVP和轻量级应用。
六、部署与CI/CD
6.1 Serverless Framework部署
# 安装Serverless Framework
npm install -g serverless
# 创建serverless.yml
cat > serverless.yml << 'EOF'
component: scf
name: todo-api
inputs:
src:
src: ./
exclude:
- node_modules/**
functionName: todo-api
runtime: Nodejs18
handler: index.main
memorySize: 256
timeout: 30
environmentVariables:
MONGO_URI: mongodb://...
events:
- apigw:
name: todo-api-gw
parameters:
protocols:
- http
- https
serviceName: todo-api-service
endpoints:
- path: /api/todos
method: ANY
EOF
# 部署
serverless deploy
6.2 GitHub Actions自动部署
name: Deploy SCF
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm install
- run: npm install -g serverless
- run: serverless deploy
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
七、性能与成本分析
7.1 性能指标
| 指标 | 预期值 | 优化后 | |------|-------|-------| | 冷启动延迟 | 200-500ms | <200ms(预置并发) | | 热启动延迟 | 10-50ms | <10ms | | 并发处理 | 自动弹性 | 无上限 | | 单次执行时间 | 20-100ms | 优化数据库查询 |
7.2 费用计算
以月调用100万次、平均执行时间100ms、256MB内存为例:
| 计费项 | 用量 | 月费 | |-------|------|------| | 调用次数 | 100万次 | ¥1.33(超出免费额度部分) | | 执行时间 | 100万×100ms×0.25GB | ¥2.33 | | 免费额度 | 抵扣 | -¥1.33 | | API网关 | 100万次 | ¥7.00 | | 合计 | | ¥9.33/月 |
与 GCP Cloud Functions相比,SCF在国内访问延迟更低,且免费额度覆盖了更多低流量场景。
7.3 预置并发配置
冷启动是Serverless最大的痛点。SCF预置并发保持函数实例常驻:
# 配置预置并发
tccli scf PutProvisionedConcurrentConfig \
--FunctionName todo-api \
--VersionValue '{"VersionProvisionedConcurrentNums": [{"Version": "1", "Capacity": 5}]}'
| 配置 | 费用影响 | 效果 | |------|---------|------| | 预置并发=0 | 最低成本 | 有冷启动 | | 预置并发=5 | 增加约¥50/月 | 消除前5个并发冷启动 | | 预置并发=10 | 增加约¥100/月 | 适合稳定流量 |
八、最佳实践
- 单一职责:一个函数处理一个API端点,或使用路由分发
- 冷启动优化:减小包体积、使用预置并发、保持函数精简
- 错误处理:统一错误格式、重试机制、死信队列
- 日志规范:使用结构化日志,关联requestId
- 安全:环境变量存储密钥、启用函数鉴权、限制调用来源
- 监控告警:配置错误率>5%告警、P99延迟>500ms告警
总结
腾讯云SCF让API开发摆脱了服务器管理的负担,从开发到上线只需关注业务代码。结合API网关和云数据库,一个完整的Serverless API月成本可低至几十元,是中小项目和MVP的最佳选择。
多云(Duoyun Cloud) 作为腾讯云合作伙伴,提供SCF和API网关的架构咨询和专属折扣。通过多云采购腾讯云资源,可享受更优的价格和专业的中文技术支持,帮你以最低成本构建Serverless应用。访问 duoyun.io 了解详情。