3、Serverless 应用场景剖析:Web 后端、数据处理、定时任务、消息处理、IoT 后端等典型场景

说实话,很多朋友问我:“Serverless 到底能干啥?是不是只能跑个 Hello World?”

嗯,这个问题我太有感触了。我刚接触 Serverless 那会儿,也觉得它就是个“玩具”。直到我在一个电商项目中,用 Serverless 扛住了双十一的流量洪峰,才彻底改观。

今天,我就带你看看 Serverless 真正发力的几个典型场景。每个场景我都会结合自己的实战经验来讲,希望能帮你少走弯路。

3.1 Web 后端:从“搬砖”到“搭积木”

Web 后端是 Serverless 最成熟的应用场景之一。说白了,就是把传统的 Web 服务器拆成一个个函数。

我个人习惯把这类应用分为两种:

  • 轻量级 API 网关:比如用户注册、登录、获取商品列表。这些接口逻辑简单,但调用频率高。
  • BFF(Backend For Frontend):专门为前端定制的聚合层。把多个微服务的数据拼装一下,返回给前端。

举个例子,我之前帮一家创业公司做用户中心。他们原来的架构是 Spring Boot 单体应用,每次上线都要重启整个服务。后来我帮他们改造成了 Serverless:

// 一个典型的用户注册函数(Node.js)
exports.handler = async (event) => {
    const { username, password } = JSON.parse(event.body);
    
    // 参数校验
    if (!username || !password) {
        return { statusCode: 400, body: '参数不完整' };
    }
    
    // 调用数据库 SDK
    const user = await db.insert({ username, password });
    
    return {
        statusCode: 200,
        body: JSON.stringify({ userId: user.id })
    };
};
我的经验:别把整个 Express/Koa 应用直接塞进函数。我见过有人把整个框架打包进去,结果冷启动时间飙到 5 秒以上。正确的做法是:每个函数只做一件事,保持轻量。

你想想看,这样做的好处是什么?

  • 每个接口独立部署,互不影响
  • 自动扩缩容,流量再大也不怕
  • 按实际调用次数付费,没流量就不花钱

3.2 数据处理:让“脏活累活”自动化

数据处理是 Serverless 的另一个“舒适区”。尤其是那些需要定时跑、或者事件触发的批处理任务。

我记得有一次,客户需要每天凌晨 2 点处理前一天的用户行为日志。数据量大概 500GB,需要做清洗、去重、然后导入数据仓库。

传统做法是:租一台高配服务器,装个 Cron 任务,跑完关机。但问题是,这台机器大部分时间都在空转,浪费钱。

用 Serverless 怎么做?

  1. 用户上传日志到 OSS(对象存储)
  2. OSS 的 Put 事件自动触发函数
  3. 函数读取日志,做 ETL 处理
  4. 结果写入数据仓库

这里有个关键点:数据分片。如果单个文件太大(比如超过 1GB),函数可能会超时。我的做法是:

// 数据分片处理示例
exports.handler = async (event) => {
    const { bucket, key } = event;
    const fileSize = await getFileSize(bucket, key);
    
    // 如果文件超过 100MB,拆分成多个子任务
    if (fileSize > 100 * 1024 * 1024) {
        const chunks = splitFile(bucket, key, 100 * 1024 * 1024);
        // 异步调用多个函数并行处理
        await Promise.all(chunks.map(chunk => invokeFunction(chunk)));
    } else {
        // 直接处理
        await processFile(bucket, key);
    }
};
避坑指南:我曾经犯过一个错误——在函数里直接处理超大文件,结果函数运行了 15 分钟,超时被强制终止。后来我学乖了:要么用流式处理,要么拆分成小任务。记住,单个函数的执行时间通常不要超过 5 分钟。

3.3 定时任务:告别“Cron 噩梦”

定时任务,说白了就是“闹钟”。到了指定时间,自动执行一段代码。

传统做法是在服务器上写 Cron 表达式。但问题来了:

  • 服务器挂了怎么办?
  • 任务执行时间重叠了怎么办?
  • 怎么监控任务是否成功?

Serverless 的定时触发器完美解决了这些问题。我常用的场景包括:

场景 触发频率 说明
数据备份 每天凌晨 3 点 把数据库快照备份到对象存储
报表生成 每天早上 8 点 生成前一天的运营报表,发送邮件
缓存预热 每 10 分钟 把热点数据提前加载到 Redis
健康检查 每 5 分钟 检查各个服务是否正常运行

嗯,这里要注意一个细节:幂等性。定时任务可能会因为网络抖动被重复执行。我习惯在函数开头加一个“去重检查”:

// 幂等性检查示例
exports.handler = async (event) => {
    const taskId = event.time; // 用时间戳作为任务 ID
    
    // 检查这个任务是否已经执行过
    const executed = await redis.get(`task:${taskId}`);
    if (executed) {
        console.log('任务已执行,跳过');
        return;
    }
    
    // 执行实际逻辑
    await doBackup();
    
    // 标记任务已完成
    await redis.set(`task:${taskId}`, 'done', 'EX', 86400);
};

3.4 消息处理:异步解耦的“利器”

消息处理,说白了就是“生产者-消费者”模式。生产者发消息,消费者处理消息。

我参与过一个订单系统,用户下单后需要做一系列操作:扣库存、发短信、更新积分、通知物流。如果这些都在一个请求里完成,接口响应时间会很长。

用 Serverless 怎么解?

  1. 用户下单后,把订单信息发送到消息队列
  2. 消息队列触发多个函数并行处理
  3. 每个函数只负责一件事

这样做的好处很明显:

  • 主流程(下单)响应快,用户体验好
  • 各个任务独立,一个失败不影响其他
  • 可以根据消息积压情况自动扩缩容
核心要点:消息处理场景中,函数的“重试策略”很关键。我一般设置:最多重试 3 次,每次间隔 30 秒。如果 3 次都失败,就把消息发到“死信队列”,人工介入处理。

3.5 IoT 后端:海量设备的“守门人”

IoT 场景对 Serverless 来说,简直是“天作之合”。

你想想看,一个 IoT 平台可能有几十万台设备。每台设备每隔几秒就上报一次数据。如果用传统服务器,你得准备多少台机器才能扛住这种流量?

而且,设备上报的数据往往是“突发性”的。比如智能电表,每天 0 点会集中上报前一天的数据。这时候流量可能是平时的 100 倍。

Serverless 的自动扩缩容能力,正好解决了这个问题。我做过一个智能路灯项目,架构是这样的:

  1. 设备通过 MQTT 协议上报数据
  2. IoT 网关接收数据,转发到函数
  3. 函数做数据清洗、格式转换
  4. 清洗后的数据写入时序数据库
  5. 如果数据异常(比如电压过高),触发告警函数

这里有个坑,我踩过:设备认证。千万不能让非法设备接入你的平台。我建议使用设备证书或 Token 认证,而不是简单的设备 ID 校验。

// 设备数据上报处理示例
exports.handler = async (event) => {
    const { deviceId, timestamp, data } = event;
    
    // 1. 设备认证(检查设备是否在白名单中)
    const isValid = await checkDeviceAuth(deviceId);
    if (!isValid) {
        console.error(`非法设备: ${deviceId}`);
        return { statusCode: 403 };
    }
    
    // 2. 数据校验(检查数据格式是否正确)
    if (!validateData(data)) {
        console.error(`数据格式错误: ${deviceId}`);
        return { statusCode: 400 };
    }
    
    // 3. 数据清洗(去除异常值)
    const cleanData = cleanData(data);
    
    // 4. 写入时序数据库
    await writeToTSDB(deviceId, timestamp, cleanData);
    
    // 5. 检查是否需要告警
    if (isAbnormal(cleanData)) {
        await sendAlert(deviceId, cleanData);
    }
    
    return { statusCode: 200 };
};
我的建议:IoT 场景中,函数的内存和超时时间要设置得合理一些。设备上报的数据通常很小(几百字节),处理时间也很短(毫秒级)。所以,我一般设置 128MB 内存,超时时间 10 秒就够了。别浪费钱。

3.6 场景选择:一张表说清楚

最后,我整理了一张表,帮你快速判断某个场景是否适合 Serverless:

场景 适合程度 关键考量
Web 后端(轻量 API) ★★★★★ 冷启动延迟、函数超时限制
数据处理(ETL) ★★★★★ 数据分片、执行时间
定时任务 ★★★★☆ 幂等性、任务依赖
消息处理 ★★★★★ 重试策略、死信队列
IoT 后端 ★★★★☆ 设备认证、数据清洗
长时间运行任务 ★★☆☆☆ 函数超时限制(通常 15 分钟)
有状态应用 ★★☆☆☆ 需要额外引入外部存储

说白了,Serverless 不是万能的。但它确实能解决很多传统架构的痛点。关键是要选对场景,用对方法。

下一章,我会带你深入 Serverless 的“冷启动”问题,聊聊怎么把启动时间从 5 秒降到 100 毫秒。到时候见。