理解 FaaS:函数即服务的原理、成本与工程取舍
写一个接口,传统做法往往是先准备服务器、安装运行环境、启动进程,再考虑扩容、更新与监控。即使一天只有几十个请求,这套服务也可能需要一直开着。
FaaS 换了一种交付方式:把处理逻辑和运行配置交给平台,由请求或事件触发执行,实例的创建、复用与回收交给平台管理。
听起来像是“只写函数,剩下的不用管”。但真正用起来,最重要的理解恰恰是:FaaS 减少的是一部分基础设施工作,不是系统设计本身。
本文先讲清它怎么运行,再讨论什么时候适合用、什么时候不划算,以及上线前最值得处理的几个问题。文中的架构与计算示例为说明原理而设计,不代表某个线上项目的实际数据。
一、FaaS 到底是什么?
FaaS 是 Function as a Service,即“函数即服务”。开发者提交代码、依赖与配置,平台提供运行环境,并根据调用需求安排执行资源。Serverless 是更大的范畴,还可以包含托管数据库、消息服务等;FaaS 是其中的一种计算形态。参考:AWS 对 Serverless 与 FaaS 的说明
这里的“函数”也不一定只是十几行代码。它可以是处理一次上传事件的入口,也可以通过平台支持的自定义运行时或容器镜像承载一个 Web 应用。具体怎么启动、接收什么事件,需要遵循所选平台的接口约定。
因此,判断一个系统是不是 FaaS,不能只看它有没有 handler,更要看:运行环境的生命周期、扩缩容和资源调度,是不是主要由平台接管。
“Serverless”也不是没有服务器,而是开发者通常不直接维护那台底层服务器。
二、一次函数调用经历了什么?
可以把典型过程分成四步:
- 触发。 HTTP 请求、队列消息、对象上传事件或定时规则产生一次调用。
- 分配环境。 平台寻找可用实例;如果没有合适实例,就准备新的执行环境。
- 执行逻辑。 运行入口处理事件,访问数据库或其他服务,然后返回结果或确认处理完成。
- 复用或回收。 环境可能留下来处理后续调用,也可能被平台回收。
AWS 的执行环境文档明确区分了初始化与调用阶段,也说明了环境可能复用。因此,“每次调用都会重新启动一台机器”和“同一个函数永远在同一台机器上运行”,都是不准确的理解。参考:执行环境生命周期
一个直接的设计后果是:连接客户端可以尽量复用,业务数据却不能仅靠实例内存保存。缓存命中是性能收益,缓存丢失不能变成业务错误。
三、和云服务器、容器有什么区别?
| 选择 | 主要交付物 | 通常由你重点负责 | 更适合的情况 |
|---|---|---|---|
| 云服务器 | 系统环境与应用进程 | 系统、进程、容量、发布与业务 | 需要完整控制,或负载长期稳定 |
| 托管容器服务 | 镜像与服务配置 | 应用、镜像、资源和伸缩策略 | 多服务部署、复杂依赖、持续运行 |
| FaaS | 函数代码或受支持的镜像、触发配置 | 业务、依赖、权限、状态与调用边界 | 事件驱动、间歇负载、独立任务 |
这不是严格的技术分界线:容器也可以按需伸缩,函数平台也可以保留实例。有些产品还同时提供多种运行模式。
真正值得比较的是:为了达到同样的延迟、可靠性和吞吐量,你需要承担多少维护工作,支付多少总成本。
四、一个最小的 HTTP 函数长什么样?
下面是一个按 AWS Lambda HTTP API payload v2 返回格式编写的 Node.js 示例。保存为 index.mjs,入口为 index.handler。它说明入口与响应结构,不包含云端资源创建步骤;换成其他平台时,需要适配对应的触发器协议。
export async function handler(event) {
const name = event.queryStringParameters?.name ?? '访客';
const valid = typeof name === 'string'
&& name.trim().length > 0
&& name.length <= 40;
return {
statusCode: valid ? 200 : 400,
headers: { 'content-type': 'application/json; charset=utf-8' },
body: JSON.stringify(valid
? { message: `你好,${name.trim()}!` }
: { error: 'name 需要是 1—40 个字符的非空字符串' })
};
}
平台把 HTTP 请求转换成事件,再把函数返回值转换成 HTTP 响应;两者之间的字段约定不能随意混用。参考:HTTP API 的 Lambda 集成格式
本文示例已在本地验证默认参数、正常输入、空字符串、纯空白与超长输入五种情况;没有为此创建或部署云端函数。本地通过不等于云端接入完成,真正部署后,还要检查路由、鉴权、超时与日志。
五、冷启动:平均很快,不代表第一次也快
如果调用需要新建执行环境,运行时启动、依赖加载、应用初始化等工作会增加等待时间,这就是讨论冷启动时最常见的额外开销。它不是每次都会发生,也没有一个适用于所有项目的固定耗时。
优化时先拆开观察:慢在平台准备、应用初始化,还是数据库和外部接口?否则可能一直压缩代码包,真正的问题却是首次连接数据库。
我的处理顺序是:先减少无用依赖和启动工作,再测真实流量下的尾延迟;只有业务确实不能接受,才评估预置实例等方案。保留实例通常意味着额外成本,而且扩容超出已准备的容量时,仍可能遇到新的初始化等待。参考:阿里云实例并发与预置实例的取舍
后台图片处理能等一会儿,用户进入页面的首个请求则可能很敏感。冷启动是否严重,取决于它出现在什么位置。
六、自动扩容,也可能把数据库一起压垮
函数能扩容,不等于整个系统都能同步扩容。数据库连接数、第三方接口配额和消息积压,都可能先成为瓶颈。
用一个简化估算:稳定状态下,如果每秒进入 100 个请求,平均每个请求占用 0.2 秒,那么平均在途请求数大约是 20。这个估算使用的是平均值,不代表峰值容量,更不包含突发和重试预留。
在途请求数不等于实例数。 一个实例能承载多少并发,受平台模式、配置和应用能力共同影响。阿里云 FC 支持配置单实例并发,尤其适合评估等待外部 I/O 较多的任务;但增加并发并不会凭空增加 CPU 或下游服务容量。参考:FC 单实例并发
假设最多扩到 100 个实例,每个实例的连接池最多允许 10 个连接,理论连接上限就可能达到 1,000。实际是否建满取决于连接池行为,但这个量级已经值得事先检查。
因此,限并发并不是“浪费弹性”,而是在保护整条链路。峰值任务可以先排队,外部调用要设置超时,数据库连接池也应该按整体容量预算,而不是只看单实例。
七、重试不是免费保险:重复执行必须有答案
事件驱动系统里,一次业务事件可能被处理不止一次。以 Lambda 异步调用为例,函数错误默认会触发重试,即使函数没有返回错误,也可能收到重复事件。同步调用、异步调用与队列事件的重试机制不能混为一谈。参考:异步调用的错误处理
想象一个发奖励的任务:数据库已经写入成功,但确认响应在网络中丢失。上游不知道是否完成,于是重试。问题不再是“函数能不能再跑一次”,而是“奖励会不会再发一次”。
对于同一数据库里的写入,一种设计是:
- 为业务操作定义稳定的幂等键,例如“玩家 ID+任务 ID”,而不是每次调用新生成一个随机值。
- 用数据库唯一约束防止相同操作重复创建。
- 把幂等记录与奖励写入放在同一个事务中提交;失败就一起回滚。
- 遇到重复请求时,读取并返回之前的处理结果。
但数据库事务不能自动覆盖外部扣款、短信或邮件。涉及外部副作用时,还需要下游幂等接口、事务消息或 outbox 等机制,并考虑中断后的恢复。仅在内存里放一个“处理过”的标记,既挡不住多实例并发,也挡不住实例回收。
这里的关键不是追求“函数绝不会重复运行”,而是让重复运行不会产生重复的业务结果。
八、无状态,不等于应用不能有状态
在常见的无状态函数设计里,意思不是不能保存用户数据,而是不能把某个实例的内存或临时文件,作为唯一可信的数据来源。
| 内容 | 一般应该放在哪里 |
|---|---|
| 用户资料、订单与任务状态 | 持久数据库 |
| 上传文件、生成图片与结果文件 | 对象存储 |
| 限流计数与短期共享缓存 | 适合的共享存储 |
| 可重建的本地缓存与中间文件 | 实例内存或临时目录 |
不同平台也可能提供会话亲和、持久化挂载等能力,但它们需要单独理解生命周期和收费方式,不能把普通实例复用当成持久性承诺。
还要区分“技术上存在全局变量”和“业务上可以共享数据”。初始化好的 SDK 客户端通常可以复用,但把某个用户的请求内容留在可变全局对象里,可能造成跨请求污染。参考:Lambda 函数开发最佳实践
九、FaaS 便宜吗?先算完整账单
低频、波动大的任务,按需计算通常值得考虑;但“按调用收费”不是所有账单的完整描述,也不能直接推导出“没人访问就绝对没有费用”。
以阿里云 FC 的当前按量计费文档为例,活跃资源、不同休眠状态与调用等项目有各自规则。使用时应查看具体实例模式和地域,不能拿一条旧的请求单价代替完整计费模型。参考:FC 按量付费说明,核对于 2026 年 10 月 22 日
评估一个实际方案,我会至少分开看:
- 函数计算本身的资源占用与调用费用。
- 保留容量、附加存储等所选能力的费用。
- 网关、数据库、对象存储、日志与网络的费用。
- 失败重试、滥用请求和意外递归产生的放大成本。
- 团队为部署、排障、容量和迁移付出的时间。
例如,一条低频任务每次要等待外部服务几十秒,代码虽然只有几行,也不能只按“实际执行 JavaScript 的几毫秒”估算账单;具体怎么计费仍要回到平台规则。
比较 FaaS 与常驻服务时,应在相近的可用性与延迟要求下压测,然后计算每千次成功业务操作的总成本。只比较机器月租与函数调用单价,很容易比较错对象。
十、哪些任务适合,哪些需要谨慎?
下面是工程选型建议,不是平台能力的绝对边界。
| 场景 | 初步判断 | 主要原因 |
|---|---|---|
| Webhook、轻量业务 API | 通常适合评估 | 请求驱动,逻辑边界清楚 |
| 上传后生成缩略图、格式转换 | 通常适合评估 | 天然事件驱动,容易拆成任务 |
| 定时清理、日报与数据同步 | 通常适合评估 | 间歇执行,不必一直占用进程 |
| 用户生成内容的异步处理 | 适合做入口与部分工作节点 | 可排队、可限流、可追踪状态 |
| 长时间、高利用率计算 | 需要比较容器或批处理 | 常驻计算可能更经济,执行边界也不同 |
| 极敏感的交互延迟、复杂长连接 | 需要专项验证 | 冷启动、连接生命周期和平台限制会影响设计 |
对于 AI 任务,函数可以负责鉴权、创建任务、接收回调与更新状态。耗时较长的生成过程则应按模型和平台能力,选择外部生成 API、队列 Worker、容器任务或其他执行服务。
不要让一个用户请求同步等完整个长任务,仅仅因为“函数可以调用模型接口”。交互入口与后台执行分开,往往比把超时不断调大更容易维护。
十一、上线前,我会检查这几件事
- 输入与身份: 请求是否经过验证,触发源是否可信,权限是否只覆盖必要资源?
- 重复与失败: 相同事件来两次会怎样,中途失败后如何重试,最终失败在哪里查?
- 状态与连接: 实例被回收是否会丢业务数据,扩容后数据库能否承受连接数?
- 超时与容量: 上游、函数与下游的超时是否协调,峰值并发是否有上限?
- 监控与成本: 是否能按请求或任务 ID 查链路,是否关注错误率、尾延迟、队列积压和预算?
- 发布与回退: 配置能否重现,新版本出错时能否回到已知可用的版本?
不需要为一个小工具提前搭建庞大的平台。但涉及扣费、发奖或用户数据,重复执行和失败恢复就不是以后再补的细节。
十二、结论:FaaS 的价值,是把计算变成可调用的能力
FaaS 最有吸引力的地方,不是代码一定更少,也不是账单一定更低,而是让一部分计算可以围绕事件交付,减少团队维持空闲服务与管理执行实例的负担。
它没有取消状态管理、可靠性、容量规划与成本控制,只是改变了这些问题出现的位置。
对选型来说,比“要不要全面 Serverless”更有用的问题是:这项任务是否独立、是否间歇发生、能否接受当前的延迟与重试语义,以及把它交给平台之后,整个系统是否更容易维护。
适合的任务交给 FaaS,需要持续运行的服务保留合适的形态。技术架构的目标不是统一,而是让每一部分都承担它最擅长的工作。

留句话
正在连接评论服务…