跳到主要内容

优化微信小程序云函数冷启动性能

一句话定义:针对微信小程序后端使用的 Cloudbase 云函数,用初始化代码外移、裁依赖、调内存规格、开请求多并发四个手段降低冷启动延迟,附完整的 before/after 代码。

预计耗时:30 分钟 | 难度:进阶

适用场景

  • 适用:小程序首屏或登录链路上的云函数,冷启动延迟影响用户进入体感
  • 不适用:批处理或定时任务,那类场景优化吞吐比优化延迟更有价值

云函数冷启动分两层——容器拉起(Cloudbase 平台负责,开发者控制不了)和用户代码初始化(执行 require + 模块级代码,这是开发者可以优化的部分)。这篇 recipe 的四个手段都集中在用户代码层。

环境要求

依赖版本
@cloudbase/node-sdk3.18.1
@cloudbase/cli3.0.4
Node.js≥ 16.13

需要一个已经跑起来的云函数。建议用 add-auth-wechat-miniprogram 里的 getLoginTicket 做优化对象。

优化 1:把 SDK 初始化提到模块顶层

原理:Cloudbase 云函数运行在 Node.js 进程里。容器首次拉起时,Node.js 加载整个模块(执行所有顶层代码)。后续同一容器实例处理新请求时,模块级代码不会再次执行——这是 Node.js 的模块缓存机制。把 requireinit 放在模块顶层,就能让容器复用时跳过这部分开销。

优化前:

// ❌ 每次请求都重新 require + init
exports.main = async (event) => {
const cloudbase = require('@cloudbase/node-sdk');
const app = cloudbase.init({
env: process.env.TCB_ENV,
credentials: require('./tcb_custom_login.json'),
});
const ticket = app.auth().createTicket(event.openid);
return { ticket };
};

优化后:

// ✅ require 和 init 放在模块顶层
const cloudbase = require('@cloudbase/node-sdk');

const app = cloudbase.init({
env: process.env.TCB_ENV,
credentials: require('./tcb_custom_login.json'),
});
const auth = app.auth();

exports.main = async (event) => {
const ticket = auth.createTicket(event.openid);
return { ticket };
};

注意:只有纯内存操作(require、对象初始化)适合放模块级。如果 init 过程涉及网络 IO(比如建立数据库连接池),放模块级反而会拖慢冷启动——因为容器第一次拉起时 IO 和代码加载是串行的。

优化 2:裁剪 node_modules,减少 require 文件量

原理:冷启动时 Node.js 的 require() 需要从磁盘读取并解析 JS 文件。@cloudbase/node-sdk 完整包含数据库、存储、AI 等所有模块,解包后 20+ MB。如果你的函数只用了 auth 模块,其余的加载全是浪费。

检查当前依赖体积:

cd cloudfunctions/getLoginTicket
du -sh node_modules/ | head -1 # 总体积
du -sh node_modules/*/ | sort -h | tail -10 # 前 10 大依赖

裁剪原则:

  • 只装 dependencies,不装 devDependenciesnpm install --production 或确保 package.json 里 devDependencies 不混入 dependencies
  • 用原生模块替代大库getLoginTicket 里调微信 jscode2session 接口只需要一个 HTTP GET,用 Node.js 内置的 https 模块就够了,不需要装 axios(3MB+)
  • npm ls --prod --depth=1 列出运行时实际依赖,逐个评估能否去掉

优化 3:调大内存规格

原理:Cloudbase 云函数的 CPU 按内存比例分配(官方文档原文:"根据指定的内存分配函数运行时可用的计算资源,CPU 按跟随内存的大小,按比例自动分配")。冷启动阶段 Node.js 解析 JS 文件是 CPU 密集操作,分到更多 CPU 这一步自然会更快。

配置方式:控制台 → 云函数 → 函数详情 → 基础配置 → 内存。默认 256 MB,可以调到 512 MB 或更高(具体可选范围以控制台实际选项为准)。

成本权衡:内存翻倍,单次调用单价也大致翻倍。对登录、鉴权这类「每日调用量不高但延迟敏感」的函数,涨价的绝对金额很小,换来的体感改善是值得的。对高 QPS 的批处理函数,保持默认即可。

优化 4:开启请求多并发(HTTP 云函数)

原理:默认每个云函数实例同一时刻只处理一个请求。突发流量来了 10 个并发请求,平台会拉起 10 个新实例——每个都要经历冷启动。开启「请求多并发」后,一个实例可以同时处理多个请求,突发流量下需要拉起的新实例数量大幅减少,冷启动发生的概率也随之降低。

配置方式:控制台 → 云函数 → 函数详情 → 基础配置 → 请求多并发 → 开启,设置最大并发数(具体范围以控制台实际选项为准)。

注意:该配置是否仅限 HTTP 云函数,请以控制台实际展示为准。不同函数类型(HTTP 触发 vs 事件触发)的可配置项可能不同。

副作用:开启多并发后,模块顶层的变量会被多个请求共享。如果代码里有「以为每次调用都是全新进程」的假设(比如用模块级变量缓存某个用户的状态),并发场景下会出现数据串扰。做法是:不可变常量可以放模块级,请求相关的状态必须在 handler 函数内部定义。

// ❌ 模块级变量在并发下会串
let currentUserId = null;

exports.main = async (event) => {
currentUserId = event.uid; // 并发时 A 请求设置,B 请求覆盖
// ...
};

// ✅ 请求相关状态放 handler 内
exports.main = async (event) => {
const currentUserId = event.uid; // 每个请求独立
// ...
};

运行验证

四个优化做完后,用这个埋点脚本确认效果:

const COLD_START_AT = Date.now();
let isCold = true;

exports.main = async (event, context) => {
const latency = Date.now() - COLD_START_AT;
console.log(JSON.stringify({
coldStart: isCold,
latencyMs: isCold ? latency : 0,
}));
isCold = false;
// ... 业务逻辑
};

部署后用 CLI 触发几十次采样,从日志里筛 coldStart: true 的记录看延迟分布。建议每做一项优化就跑一遍,单项对比才知道哪个手段贡献最大。

常见错误

现象原因修复
模块级 init 导致部署成功但所有调用失败模块级代码里 throw 了(比如 require('./tcb_custom_login.json') 文件不存在),容器起不来把可能 throw 的 require 改成 try/catch,错误延迟到 handler 里返回
开多并发后出现数据串扰模块顶层变量在多个请求间被共享审计所有模块级状态,请求相关的改到 handler 内
调大内存后冷启动没变快依赖很小或业务是 IO 密集(CPU 本来不是瓶颈)用埋点脚本看 latencyMs 里有多少是 Node.js 加载 + 解析的时间,如果本来就很短,说明瓶颈在别处
裁依赖后本地跑通,云端报 Cannot find module依赖被放进了 devDependencies,tcb fn deploy 不打包 dev 依赖检查 package.json,运行时用到的全部放 dependencies
调大内存后成本明显上涨内存翻倍单价翻倍,高 QPS 函数绝对金额会大只对延迟敏感且 QPS 低的函数调大内存,批处理函数保持默认

相关文档

下一步

  • 登录报错逐条修复:fix-auth-wechat-miniprogram
  • 对接企业微信群机器人:connect-wecom-webhook-cloud-function