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

好,咱们今天来啃函数计算最核心的几个概念。说实话,这些概念你如果只是看文档,会觉得每个字都认识,但串起来就懵了。我当年刚接触无服务器时也是这样,尤其是冷启动,踩坑踩得我头皮发麻。咱们一个一个来拆。

3.1 函数:一切的基础

函数,说白了就是一段代码。你写一个处理逻辑,打包上传,平台帮你跑。但这里的「函数」和我们平时写的函数不太一样——它是一个完整的、可独立部署的服务单元。

我个人习惯把函数想象成一个「微小的微服务」。它接收输入,处理,然后输出。只不过你不用管服务器、不用管操作系统、不用管运行时环境。你只管写代码。

核心要点:函数是计费的最小单位。你只为你实际运行的时间付费,不跑就不花钱。这是无服务器最诱人的地方。

举个例子,一个简单的图片缩略图函数:

# 伪代码,实际用哪个平台都类似
def handler(event, context):
    # event 里放着上传的图片信息
    image = download_from_s3(event['bucket'], event['key'])
    thumbnail = resize(image, width=200)
    upload_to_s3(thumbnail, 'thumbnails/' + event['key'])
    return {'status': 'ok'}

嗯,这里要注意:函数不能有状态。你想想看,如果函数里存了个全局变量,下次调用时这个变量还在吗?不一定。因为平台可能用不同的实例来跑你的函数。所以,函数天然就是无状态的

3.2 触发器:谁叫醒了函数?

函数不会自己跑。它需要被「触发」。触发器就是那个敲门的人。

我在项目中遇到过最典型的场景:用户上传一个文件到对象存储,然后触发函数去做处理。这个「上传文件」的动作,就是触发器在起作用。

常见的触发器类型:

  • HTTP 触发器:你发一个 HTTP 请求,函数就执行。最常用,适合做 API。
  • 定时触发器:像闹钟一样,每天凌晨三点跑一次。适合做数据清理、报表生成。
  • 对象存储触发器:文件上传、删除时触发。适合做图片处理、视频转码。
  • 消息队列触发器:队列里有消息就消费。适合做异步任务、削峰填谷。

我的建议:刚开始学的时候,先用 HTTP 触发器。它最直观,调试也方便。等你熟悉了,再玩其他的。

3.3 事件源:触发器背后的推手

触发器和事件源,很多人搞混。我简单解释一下:

事件源是产生事件的地方。比如对象存储、消息队列、API 网关。触发器是连接事件源和函数的桥梁。事件源产生了一个事件,触发器把这个事件「喂」给函数。

你想想看,没有触发器行不行?也行,但你需要自己写代码去轮询事件源,那就不「无服务器」了。触发器帮我们做了这个脏活累活。

事件源 触发器类型 典型场景
API 网关 HTTP 触发器 RESTful API、Web 应用后端
对象存储 存储触发器 图片处理、文件转码
消息队列 队列触发器 异步任务、日志处理
定时器 定时触发器 数据备份、定时报表

3.4 执行环境:函数跑在哪儿?

函数不是凭空跑的。它需要一个「容器」来装它。这个容器就是执行环境。

每个函数运行时,平台会分配一个沙箱环境。这个环境包括:

  • 操作系统:通常是 Linux,但你不用管具体版本。
  • 运行时:Node.js、Python、Java、Go 等,你选一个。
  • 资源限制:内存、CPU、磁盘、网络。这些都是有限额的。
  • 环境变量:你可以在配置里设置,函数启动时就能读到。

注意:执行环境是共享的。同一个函数的不同调用,可能会复用同一个环境。这意味着你不能在代码里假设「每次启动都是全新的」。我曾经因为这个踩过坑——在函数里写了一个全局计数器,结果发现计数不对。后来才明白,环境被复用了。

3.5 冷启动与热启动:性能的关键

好,终于到了最让人头疼的部分。冷启动和热启动,说白了就是函数实例的「生」与「熟」。

冷启动:函数第一次被调用,或者很久没被调用,平台需要创建一个新的执行环境。这个过程包括:

  1. 分配容器
  2. 下载你的代码包
  3. 初始化运行时
  4. 执行你的初始化代码
  5. 开始处理请求

这个过程很慢,可能几百毫秒甚至几秒。对于用户来说,就是「卡了一下」。

热启动:函数刚被调用过,环境还在。平台直接复用这个环境,跳过初始化步骤,直接执行你的处理逻辑。速度飞快,通常几毫秒。

为什么会这样?因为平台为了节省资源,会在函数空闲一段时间后销毁实例。下次再来请求,就得重新创建。你想想看,如果平台一直保留所有函数的实例,那成本得多高?

关键数据:冷启动延迟通常在 200ms - 2s 之间,取决于运行时和代码包大小。热启动延迟通常在 1ms - 10ms。差距巨大。

我曾经在一个生产项目里,因为冷启动问题被用户投诉。那个函数是处理用户登录后的个性化推荐,冷启动时用户要等 3 秒才能看到推荐内容。后来怎么解决的?我用了两个技巧:

  • 预留并发:让平台一直保持几个热实例,别销毁。花钱买性能。
  • 代码瘦身:把不必要的依赖去掉,代码包从 50MB 减到 5MB,冷启动时间从 2s 降到 400ms。

避坑指南:我曾经把数据库连接池的初始化放在函数外面(全局作用域),以为这样能复用。结果发现冷启动时连接池没建好,请求就进来了,直接报错。正确的做法是:初始化代码要能处理「还没准备好」的情况,或者用懒加载。

总结一下冷启动和热启动的区别:

特性 冷启动 热启动
触发条件 首次调用或长时间空闲后 频繁调用,实例被复用
延迟 高(200ms - 几秒) 低(1ms - 10ms)
资源消耗 高(初始化开销) 低(直接执行)
优化方向 减小代码包、使用轻量运行时、预留并发 保持实例活跃、避免全局状态污染

嗯,这些概念你理解了,函数计算的基本功就算打牢了。下一章咱们会深入讲如何设计一个生产级的函数,包括错误处理、日志、监控这些实战内容。到时候我会分享更多踩坑经验。