前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版

Serverless 入门实战 从运行原理到四个落地案例

首页2021-04-16 15:24:08Front-End
serverlessFaaSNode.js

一个只有几十人在用的内部工具,每天的调用量撑死几百次,却要为它单独挂一台常驻云主机,还得管系统升级、证书续期、磁盘日志清理。机器九成时间在空转,运维那摊活儿一样都没少。这类场景就是 Serverless 最典型的入口,把常驻服务拆成按需执行的函数,没请求的时候不占资源,自然也就没有账单和值班。

这篇是我把 Serverless 从概念到落地重新梳理一遍的笔记。前半部分讲清它到底解决什么问题、函数是怎么被拉起来的、冷启动为什么能压到百毫秒,后半部分是四个可以照着敲的完整案例,代码和部署命令都在。

在本篇文章中,我们将从浅入深,和大家一起学习以下知识:

  • Serverless 的两种定义,以及 FaaS 和 BaaS 各自负责哪一块
  • 写第一个函数,触发器、事件对象和日志分别该怎么处理
  • 函数的调用链路、冷启动与热启动、以及 FaaS 的三层结构
  • 用一份 YAML 描述整个应用,把开发调试部署的效率提上去
  • 阿里云函数计算、腾讯云函数、Vercel 三种上手方式的差别
  • 四个完整案例,登录注册、Restful 内容管理 API、音视频处理、React 服务端渲染

先说一句时效性的话。这篇写于 2021 年 4 月,各家云厂商的控制台界面、免费额度和计费单价这几年都调整过好几轮。文里的截图和数字保留当时的样子,方便你对照着理解流程,真要动手掏钱之前请以官方文档和控制台的最新页面为准。

# 一、什么是 Serverless

理清 Serverless 要解决的问题其实很简单,把这个词从字面上拆开看就够了。Server 指服务端,它划定了 Serverless 解决问题的边界;less 可以理解成较少关心,这是它的目的。两个词组合在一起,就是「较少关心服务端」。

注意是较少关心,不是没有服务器。你的代码最后还是跑在实实在在的机器上,只不过这台机器的采购、扩容、打补丁、装监控,全都从你的工作清单里挪走了。这一点很关键,很多人第一次听到 Serverless 会以为是某种新的运行时黑科技,其实它更像是一次责任边界的重新划分。

顺着这个思路,Serverless 在业界一般有两种口径:

  • 第一种:狭义 Serverless(最常见)= Serverless computing 架构 = FaaS 架构 = Trigger(事件驱动)+ FaaS(函数即服务)+ BaaS(后端即服务,持久化或第三方服务)= FaaS + BaaS
  • 第二种:广义 Serverless = 服务端免运维 = 具备 Serverless 特性的云服务

狭义那条是我们平时写代码时打交道最多的,也是这篇的主线。广义那条更像一种标准,比如对象存储 OSS、CDN、云数据库这些你从来不用管机器的产品,都算具备 Serverless 特性的云服务。BaaS 里的那些第三方服务,说的就是它们。

Serverless 的两种定义与 FaaS BaaS 组成关系图

这张图把两条口径的关系画在了一起。你可以这么记:FaaS 负责放你的业务代码,BaaS 负责放你的状态,Trigger 负责把外面的事件送进来。三者凑齐,才是一个能跑的 Serverless 应用,光有 FaaS 是写不出完整业务的。

那什么时候值得上 Serverless?我自己的判断标准很粗暴,看两件事:请求量是不是明显有波峰波谷,以及这段逻辑能不能做成无状态。定时任务、图片视频处理、Webhook 回调、活动页接口、小程序后端,这几类基本都能对上。反过来,需要长连接、需要常驻内存缓存、单次执行动辄几十分钟的活儿,Serverless 目前并不划算。

# 二、编写你的第一个 Serverless 应用

各家 FaaS 平台的产品名不一样,AWS 叫 Lambda,阿里云叫函数计算,腾讯云叫云函数,但你真正要写的东西高度一致。挑平台之前先看下面这张横向对比,能省掉不少纠结。

主流 FaaS 平台在语言、触发器和计费上的横向对比

图里的信息可以归成三条:

  • FaaS 平台都支持 Node.js、Python 、Java 等编程语言;
  • FaaS 平台都支持 HTTP 和定时触发器(这两个触发器最常用)。此外各厂商的 FaaS 支持与自己云产品相关的触发器,函数计算支持阿里云表格存储等触发器;
  • FaaS 的计费都差不多,且每个月都提供一定的免费额度。其中 GB-s 是指函数每秒消耗的内存大小,比如1G-s 的含义就是函数以 1G 内存执行 1 秒钟。超出免费额度后,费用基本都是 0.0133元/万次,0.00003167元/GB-s。所以,用 FaaS 整体费用非常便宜,对一个小应用来说,几乎是免费的。

上面这两个单价是 2021 年当时的公开报价,写在这里是想让你对量级有个感觉,一万次调用一分多钱。这几年各家的计费模型陆续加了预留实例、按 CPU 计量等新维度,免费额度也调整过,实际花多少钱请以你所在区域的官方定价页为准。不过量级结论到现在还成立,个人项目和内部工具跑在 FaaS 上,基本就是免费。

选语言这块有个小建议。如果你的函数是给 HTTP 请求兜底的,也就是用户会在页面上等结果,优先选 Node.js,冷启动最快,后面讲生命周期时会说原因。如果是离线跑批、数据处理,语言就随意了。

以阿里云函数为例

// logic.js
exports.sayHello = function (name) {
  return `Hello, ${name}!`;
}
@前端进阶之旅: 代码已经复制到剪贴板

这个文件里只有纯业务逻辑,一个 name 进去,一句问候出来,跟运行在哪儿完全无关。它在本地能用 Jest 直接测,也能原样搬到 Express 应用里。

// index.js
const logic = require('./logic');
exports.handler = (request, response, context) => {
  // 从 request 中获取
  const { name } = request.queries;

  // 处理业务逻辑
  const message = logic.sayHello(name)

  // 设置 HTTP 响应
  response.setStatusCode(200);
  response.setHeader("Content-Type", "application/json");
  response.send(JSON.stringify({ message })); 
}
@前端进阶之旅: 代码已经复制到剪贴板

这段是入口函数,它只干三件事:从 request 里取参数、调 logic.js 拿结果、把结果按 HTTP 的格式写回 response。业务逻辑一行都没有。

把业务逻辑拆分到入口函数之外,这是写 FaaS 函数的第一条纪律。理由很实在,handler 的签名是平台定的,阿里云是 (request, response, context),AWS 是 (event, context, callback),你把逻辑写在里面,就等于把业务代码焊死在了某一家云上。逻辑在外面,换平台时只需要重写那十几行胶水代码。

# 触发器及事件对象

函数自己不会跑,得有东西把它叫醒,这个东西就是触发器。触发器决定了三件事:谁能叫醒这个函数、事件长什么样、以及函数的返回值最后去哪儿。下面三种是日常用得最多的。

  1. HTTP 触发器

在众多 FaaS 平台中,函数计算直接提供了 HTTP 触发器,HTTP 触发器通过发送 HTTP 请求来触发函数执行,一般都会支持 POST、GET、PUT、HEAD 等方法。所以你可以用 HTTP 触发器来构建 Restful 接口或 Web 系统。

HTTP 触发器把 HTTP 请求转成事件并调用函数的流程

HTTP 触发器会根据 HTTP 请求和请求参数生成事件,然后以参数形式传递给函数。那么 HTTP 触发器的入口函数参数中的 request 和 response 参数具体有哪些属性呢?

其实 request 和 response 参数跟 Express.js 框架里那两个对象很像,request.queries 取查询串,request.headers 取请求头,response.setStatusCode()、response.setHeader()、response.send() 写响应。所以上手几乎没有学习成本,后面案例里用 @webserverless/fc-express 直接把它们转成 Express 的 req 和 res,也正是因为这层相似。

  1. API 网关触发器

API 网关触发器与 HTTP 触发器类似,它主要用于构建 Web 系统。区别在于中间多了一层 API 网关来接收 HTTP 请求,然后再产生事件,将事件传递给 FaaS。FaaS 将函数执行完毕后将函数返回值传递给 API 网关,API 网关再将返回值包装为 HTTP 响应返回给用户。

API 网关触发器接收请求并转发给 FaaS 的调用链路

多这一层网关值不值?看你要不要那些附加能力。参数校验、路径参数、IP 黑白名单、限流、鉴权、自定义域名和证书,这些网关都能做,函数里就不用写了。后面第六节那个内容管理系统就是走 API 网关的,因为它有 /article/detail/[article_id] 这种带路径参数的路由,用 HTTP 触发器自己解析会很啰嗦。反过来,如果只是一个孤零零的回调接口,HTTP 触发器更省事,也少一份网关的钱。

  1. 定时触发器

定时触发器就是定时执行函数,它经常用来做一些周期任务,比如每天定时查询天气并给自己发送通知、每小时定时处理分析日志等等。

定时触发器按 Cron 表达式周期性调用函数

定时触发器是我认为最容易被低估的一个。以前想跑个定时任务,要么开台机器写 crontab,要么在业务服务里塞个 node-schedule,前者浪费机器,后者一旦服务多实例部署就会重复执行。定时触发器这两个坑都没有,配置一个 Cron 表达式就完事,平台保证同一时刻只触发一次。

这里有个坑要注意,定时触发器是异步调用的,函数抛错了外面没人接。所以定时函数里一定要自己 try/catch 并把失败结果记下来,否则任务默默失败你根本不知道。

# 日志输出

无论你用什么编程语言开发 Serverless 应用,都要在合适的时候输出合适的日志信息,方便调试应用、排查问题。在 Serverless 中,日志输出和传统应用的日志输出没有太大区别,只是日志的存储和查询方式变了。

变在哪儿?传统应用你可以 ssh 上机器 tail -f 看日志文件,函数没有这个机会。函数实例执行完就可能被销毁,本地磁盘上的东西跟着一起没,所以日志必须实时打到外部的日志服务里。这也是为什么很多人第一次写函数会觉得「怎么调试这么难」,其实是习惯还停在有机器可登的年代。

以函数计算为例,如果你在控制台创建函数,则函数计算默认会使用日志服务来为你存储日志。在「日志查询」标签下可以查看函数调用日志。日志服务是一个日志采集、分析产品,所以如果你要实现业务监控,则可以将日志输出到日志服务,然后在日志服务中对日志进行分析,并设置报警项。

函数计算控制台的日志查询标签页

实践中我习惯在每个函数入口打一行结构化日志,把 requestId、入参摘要、耗时都记上。函数是分散的,出问题时没有一条完整的调用栈可看,requestId 就是你把几个函数的日志串起来的唯一线索。

# 三、Serverless 应用是怎么运行的

Serverless 应用是由一个个 FaaS 函数组成的,每一次运行其实就是单个或多个函数的运行。所以搞懂 Serverless 的运行原理,等于搞懂函数的运行原理。

这一节可能是全篇最值得花时间的部分。你不理解函数怎么被拉起来,就没法解释线上那些「为什么第一次请求特别慢」「为什么全局变量有时候还在有时候没了」的怪现象。

FaaS 是怎么运行的

FaaS 平台接收事件并调度函数实例的整体流程

FaaS 与传统服务端架构的职责对比

上面两张图放在一起看更清楚。第一张是 FaaS 内部的调度流程,第二张是把 FaaS 和传统服务端的职责摆在一起对照,你会发现负载均衡、进程守护、扩缩容这些原来要你操心的环节,在 FaaS 这一侧全被平台吃掉了。

# 函数调用链路:事件驱动函数执行

对于 FaaS 函数来说,一方面可以通过事件来触发执行,另一方面也可以直接调用 API 来执行。FaaS 平台都提供了执行函数的 API。

函数的两条调用链路,事件触发与 API 直接调用

fe
  • 一、什么是 Serverless
  • 二、编写你的第一个 Serverless 应用
    • 触发器及事件对象
    • 日志输出
  • 三、Serverless 应用是怎么运行的
    • 函数调用链路:事件驱动函数执行
    • 函数生命周期:冷启动与热启动
    • FaaS 是怎么分层的
    • 这一节的三个要点
  • 四、如何提高应用开发调试和部署效率
    • 应用管理
    • 应用开发
  • 五、几种上手 Serverless 的方式
    • 阿里云函数计算
    • 腾讯云函数
    • 使用 vercel 部署你的应用(推荐)
  • 六、场景案例
    • 1 使用 Serverless 实现登录注册功能
      • 1.1 身份认证的技术方案
      • 1.2 从 0 到 1 实现一个登录注册应用
      • 1.3 实现注册功能
      • 1.4 实现登录功能
      • 1.5 验证用户身份
    • 2 基于 Serverless 构建弹性可扩展的 Restful API
      • 2.1 内容管理系统的架构设计
      • 2.2 内容管理系统的实现
      • 2.3 用户注册
      • 2.4 用户登录
      • 2.5 身份认证
      • 2.6 发布文章
      • 2.7 查询文章
      • 2.8 更新文章
      • 2.9 删除文章
      • 2.10 这个案例的三点经验
    • 3 基于 Serverless 开发高可用音视频处理系统
      • 3.1 传统音视频处理方案
      • 3.2 基于 Serverless 的音视频处理系统
      • 3.3 获取视频时长函数的实现
      • 3.4 音视频处理系统的部署
    • 4 使用 React.js 开发 Serverless 服务端渲染应用
    • 5 基于 Serverless 的服务端渲染架构
      • 5.1 实现一个 Serverless 的服务端渲染应用
      • 5.2 首页接口的实现
      • 5.3 首页页面的实现
      • 5.4 详情页接口的实现
      • 5.5 详情页页面的实现
      • 5.6 应用部署
      • 5.7 SSR 案例的几点结论
  • 总结
  • 参考

← 微信小程序开发实践 从双线程模型到云开发的完整笔记虚拟DOM原理分析 Snabbdom源码与diff算法拆解 →