3、核心概念详解:函数、事件源、触发器、冷启动与热启动、执行环境

说实话,搞 Serverless 的人如果把这几个概念理不清,后面做项目肯定会掉坑里。我刚开始接触的时候也觉得不就是写个函数嘛,结果被冷启动、触发器这些词绕晕过好几次。这一章咱们就把它们掰开揉碎,一个个讲明白。

3.1 函数(Function)—— 真正干活的东西

函数,是 Serverless 的核心计算单元。你写的业务逻辑全塞在这里。

但“函数”不只是你写的那段代码。它还包含运行环境、依赖包、配置信息(内存、超时时间、环境变量等)。我习惯把函数想象成一个“菜谱”——平台拿到菜谱,准备好锅碗瓢盆(执行环境),才能炒出菜来(处理事件)。

函数三要素:
  • 代码 + 依赖(Runtime + 第三方库)
  • 资源配置(内存大小、超时时间、并发上限)
  • 入口方法(Handler,平台调用你的函数就从这个方法进)

举个例子,一个简单的 Node.js 函数:

exports.handler = async (event, context) => {
    // event 里装着触发源的数据
    // context 提供运行时信息(请求ID、剩余时间等)
    console.log('收到事件:', JSON.stringify(event));
    return { statusCode: 200, body: 'OK' };
};

你在平台上配置运行时(比如 Node.js 18)、内存(128MB)、超时(3s),再把这段代码上传,它就变成一个可调用的函数。记住,函数本身是无状态的——我踩过坑,在函数里存本地变量以为下次调用还在,结果被平台的容器复用机制坑得很惨。

3.2 事件源(Event Source)与触发器(Trigger)

函数不是自己跑起来的,它得有人叫它。谁叫它?就是事件源和触发器。

  • 事件源:产生事件的服务。比如对象存储(S3 上传文件)、消息队列(Kafka/RabbitMQ/ SQS)、API 网关(HTTP 请求)、定时器(Cron 调度)等。
  • 触发器:事件源和函数之间的映射关系。你配置“当 S3 有 PUT 操作时,调用 myFunction”,这整条规则就算一个触发器。

说白了,事件源是“谁制造了麻烦”,触发器是“谁负责通知函数去解决麻烦”。我见过有人把两者混为一谈,结果在控制台里配了半天发现函数根本收不到事件——原来是漏了创建触发器的那个“连接”步骤。

小提示: 一个函数可以挂多个触发器,一个事件源也可以触发不同函数。但要注意事件格式可能不同——你的函数需要做好兼容,或者用配置做路由。我自己习惯在函数入口加一个 switch 判断 event.source 类型,再分发到不同处理逻辑。

来看一个常见的事件格式(API Gateway 触发):

{
    "httpMethod": "POST",
    "path": "/order",
    "body": "{\"product\":\"book\"}",
    "headers": { "Content-Type": "application/json" }
}

换成定时器触发,事件对象又完全不一样。这就是为什么我建议你写函数时,先用单元测试把各种可能的 event 格式跑一遍。

3.3 冷启动与热启动

这两个词,可以说是 Serverless 入门的第一道坎。

热启动:函数刚被调用过,平台还保留着它的容器(运行时、代码、全局变量)。下次再调用,直接复用这个容器,飞快。(通常几十毫秒)

冷启动:函数第一次调用,或者距离上次调用太久,容器被回收了。平台需要重新拉取代码、初始化运行时、执行你的全局初始化逻辑。这个过程可能长达几百毫秒甚至几秒。

为什么会这样?因为平台为了节省资源,会把空闲的容器“杀掉”。你想想看,如果用户的函数半小时才被调用一次,平台还一直留着容器,那得多费钱?

避坑指南: 我曾经负责过一个支付回调函数,冷启动导致接口超时(客户端设置了 3 秒等待)。排查半天发现是函数里加载了超大的 AI 模型库,每次冷启动要下载模型参数。解决办法:把模型预热到持久化存储(比如 NAS),函数启动时直接挂载读取,而不是从网络下载。后来还加了一个“保持活跃”的定时触发器,每 5 分钟调用一次函数,避免容器被回收。

用一张表格总结冷热启动的差异:

对比维度 热启动 冷启动
时间 10~50 ms 500 ms ~ 若干秒
资源 复用已有容器 重新创建容器,加载运行时和代码
全局变量 保留上次值 重新初始化
常见场景 高并发、连续调用 间歇性调用、扩展新实例

3.4 执行环境(Execution Environment)

函数跑在什么地方?就是一个隔离的沙箱,叫做执行环境。平台(AWS Lambda、阿里云函数计算等)为每个函数实例分配一个轻量级容器或微虚拟机。

这个环境有几个特点:

  • 隔离性:不同函数的执行环境互相不可见,安全边界。同一个函数的多个并发实例,各自有独立的环境。
  • 复用性:前面说的热启动,就是复用同一个执行环境。但环境里的 /tmp 临时存储、进程状态等可能残留——所以你写代码时不要假设每次调用都是新环境。
  • 生命周期:环境启动 -> 执行 Handler -> 空闲 -> 被回收。平台会在执行环境启动时初始化全局代码(比如数据库连接池),然后每次调用只运行 Handler 函数。
注意: 执行环境与函数的并发数密切相关。每个并发实例会创建一个新的执行环境。如果你的函数需要处理大量请求,平台会自动扩展出多个环境实例,但冷启动也会被放大。我曾经遇到过一个定时任务,突然并发量翻倍,结果冷启动了一堆容器,执行时间从 200ms 暴增到 3s——后来通过预留并发实例解决了。

来看一个完整的数据流,我把这五个概念串起来画了张图:

Serverless 核心概念关系图 事件源 触发器 函数 冷执行环境 热执行环境 冷启动:容器重建 热启动:容器复用 事件源产生事件 触发器决定调用哪个函数 执行环境处理业务

可以简单梳理一下整体流程:

  1. 事件源(S3、API Gateway、定时器等)产生事件。
  2. 触发器把事件路由到对应的函数。
  3. 平台检查该函数是否有可复用的热执行环境。
  4. 有则使用热环境(热启动),无则创建新环境(冷启动)。
  5. 环境运行函数 Handler,返回结果。

明白了这五个概念,Serverless 函数应用的基本骨架就搭起来了。很多高阶用法(比如预留并发、预置初始化、异步调用等)都是围绕这些核心元素优化的。你动手做第一个函数时,多观察日志里的冷启动时长,留个心眼,后面碰到性能问题就淡定多了。