函数计算(FaaS)快速上手:创建第一个函数、运行模式(事件触发/HTTP触发)、冷启动与并发、执行环境与生命周期

说到 Serverless,最核心的实操就是函数计算。我一向认为,光看文档不写代码,永远学不会。今天就带你亲手创建第一个函数,搞定两种触发模式,再聊聊冷启动和并发这些绕不开的坎。

1. 创建第一个函数:三步走

选一个平台,阿里云或 AWS Lambda 都行。我这里用阿里云 FC 举例,但逻辑通用。

  1. 新建服务和服务:在控制台创建一个服务(Service),名字随意。服务是函数的分组单元。
  2. 创建函数:选择“事件函数”类型,运行时选 Node.js 16 或 Python 3.9。代码直接写一个简单的 HTTP 响应。
  3. 部署并测试:控制台直接点测试,或绑一个 HTTP 触发器后访问 URL。

下面是一个最小可用的函数代码(Node.js):

// index.js
exports.handler = async (event, context, callback) => {
  const body = JSON.parse(event.toString());
  return {
    statusCode: 200,
    body: `Hello, ${body.name || 'World'}!`
  };
};

部署后,你就能在日志里看到输出。我第一次跑通时,整整花了五分钟——不是因为代码难,而是忘了给角色授权。嗯,这里要注意:函数执行需要 RAM 角色 来访问日志、OSS 等其他服务,默认不配会报 403。

个人习惯:我每次新建函数都会先配一个最小权限角色,只写日志和读配置。后面需要什么再加,避免权限过大。

2. 运行模式:事件触发 vs HTTP 触发

函数计算支持两种主流触发方式,本质都是“事件驱动”,但使用场景差异很大。我做了个表格,帮你快速对比:

触发类型典型场景调用方式响应要求同步/异步
事件触发OSS 上传、定时任务、消息队列消费平台自动投递事件无返回(或回调)异步为主
HTTP 触发API 网关、Webhook、前后端交互外部 HTTP 请求必须返回 HTTP 响应同步

你想想看,如果你的函数需要对外提供 RESTful 接口,就用 HTTP 触发器;如果是后台处理文件或日志,就用事件触发。我曾经在一个项目中把 OSS 触发器错配成 HTTP,结果文件上传后函数没反应,排查了半天才发现触发器类型搞反了……

避坑指南:HTTP 触发器的函数必须显式设置超时时间,默认 3 秒,实际 API 网关转发还会有额外超时。我曾经遇到过一个处理图片压缩的函数,跑了 10 秒,结果网关直接 504。后来把超时调到 30 秒才解决。

3. 冷启动与并发:你躲不开的性能问题

冷启动,说白了就是函数实例第一次启动时,要加载代码、初始化运行环境、运行全局代码。这个过程通常在几百毫秒到几秒之间。为什么会有冷启动?因为平台为了节省资源,会在一段时间没有请求后销毁实例。

影响冷启动时长的几个关键因素:

  • 运行时语言:Node.js 最快(<100ms),Python 和 Java 较慢(1-2s)。
  • 代码包大小:每多 1MB,启动时间增加约 50-100ms。
  • 依赖加载:npm install 或 pip install 的模块越多,启动越慢。
  • VPC 配置:如果函数接入 VPC,每次冷启动需要额外花 1-3s 创建 ENI(弹性网卡)。

我记得有一次做日志巡检函数,每天凌晨跑一次,每次都等 3 秒的冷启动。后来我把函数预热方案加上去了——用一个定时触发器每 5 分钟调一次,保持实例热乎。你想想看,冷启动只在实例首次创建时发生,后续请求复用同一个实例,所以预热有用。

特别注意:并发数不等于实例数。平台会自动扩容,但冷启动的实例 不参与 已存在的并发。如果你有 100 个并发请求,但只有 1 个热实例,那 99 个请求都会经历冷启动。我曾经遇到过一个电商秒杀场景,瞬间并发 2000,结果大部分请求都冷启动,导致接口平均耗时从 200ms 飙升到 3 秒。

优化冷启动的常见手段:

  • 预留并发(Reserved Concurrency):为关键函数预留固定数量的实例,避免冷启动。但要按量付费。
  • 精简代码包:只打包必要依赖,使用打包工具(如 webpack、esbuild)做 tree-shaking。
  • 使用轻量运行时:Node.js、Python 比 Java、.NET 快得多。
  • 减少全局初始化:把数据库连接、HTTP 客户端声明在 handler 外部,只初始化一次。

4. 执行环境与生命周期

函数实例的生命周期分为三个阶段:初始化 → 调用 → 销毁。你可以理解为一个“微容器”。

  • 初始化:平台拉取代码包、创建容器、执行全局代码(如 require 依赖、建立连接池)。这个阶段只做一次,除非实例被回收。
  • 调用:每次事件或 HTTP 请求,平台都会调用你的 handler 函数。这个过程是并行的——同一个实例可以同时处理多个请求(并发),但注意代码必须是线程安全的。Node.js 单线程没问题,但 Python 要小心全局变量。
  • 销毁:实例空闲超过阈值(一般 5-15 分钟,各平台不同),平台会停止容器。销毁前可能触发 Shutdown 回调,可以用来优雅关闭连接。

执行环境的一些重要约束:

属性典型值(以阿里云 FC 为例)说明
最大内存3 GB(部分平台 10 GB)函数峰值使用不能超限
最大执行时长600 秒(可申请提升)同步调用建议保持在 30s 内
临时磁盘空间10 GB(/tmp)挂载后可读写,但实例销毁后数据丢失
并发实例数默认 300(可提升)超过后请求会排队或降级
环境变量大小 4 KB敏感信息建议用密钥管理
我的经验:如果你在函数里需要访问数据库,一定要把连接池声明在 handler 外部。这样同一个实例内的多次调用可以复用连接,省去每次建立连接的开销。我写日志巡检函数时就是这样做的——初始化时连一次 Redis,后续每条日志处理都走同一个连接。

下图是我整理的一个 FaaS 核心概念关系图,帮你把这几块串起来:

函数计算(FaaS)核心概念关系图 函数 (Function) 事件触发器 HTTP 触发器 执行环境 (运行时/资源/沙箱) 冷启动 / 并发 生命周期 (初始化→调用→销毁) 日志与监控 说明:触发器传入事件 → 函数执行,平台管理实例冷启动和并发,生命周期决定资源复用,日志监控用于排障。

总结一下:创建第一个函数最快只需 5 分钟,但真正理解冷启动与并发的相互关系需要实战。我建议你今晚就动手:先写一个最简单的 HTTP 函数,然后故意配一个很大的依赖包看看冷启动时间怎么变。嗯,这种“破坏性实验”往往比看书管用十倍。