跳到主要内容

实例复用

对于一次调用,云函数存在两种执行方式(以下以 Node.js 运行时为例,其他运行时如 Python、PHP、Go、Java 等机制一致):

  • 冷启动:需要全新启动运行时进程,加载依赖并执行初始化代码。
  • 热启动:函数实例、执行进程都被复用(即下文提到的「实例复用」)。

如果云函数一段时间内没有被调用,平台会根据访问情况回收分配的计算资源;下次被调用时会重新分配,此时发生冷启动。如果对云函数发起连续的请求,已启动的实例会得到复用,可以在很短时间投入计算,此时即热启动。

提示

CloudBase 会根据您云函数长期的访问情况,自动调度、调配实例的数量,保证足够好的性能的同时,节省您的资源。

实例复用带来的状态残留

考虑下面这个云函数,它演示了实例复用带来的状态残留问题:

let i = 0;
exports.main = async (event = {}) => {
i++;
console.log(i);
return i;
};

在第一次调用该云函数的时候函数返回值为 1,这是符合预期的。

但如果连续调用这个云函数,其返回值有可能是从 2 递增,也有可能变成 1,这便是实例复用的结果:

  • 热启动时,执行函数的进程被复用,进程的上下文也得到了保留,所以变量 i 自增
  • 冷启动时,进程是全新的,代码会从头完整地执行一遍,此时返回 1

所以,开发者在编写云函数时,应注意保证云函数是无状态的、幂等的,即当次云函数的执行不依赖上一次云函数执行过程中在运行环境中残留的信息。

利用实例复用优化性能

实例复用不只是"注意事项",也是一种性能优化手段。将只需要执行一次的初始化逻辑放到 handler 函数外,可以让后续热启动的请求直接复用已建立的连接、已加载的资源,显著降低响应耗时。

// ✅ 推荐:在 handler 外初始化,热启动时复用
const mysql = require("mysql2/promise");

const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
connectionLimit: 5,
});

exports.main = async (event) => {
const [rows] = await pool.query("SELECT * FROM users WHERE id = ?", [
event.id,
]);
return rows;
};
// ❌ 不推荐:每次调用都重建连接,冷热启动都要付出连接开销
exports.main = async (event) => {
const conn = await mysql.createConnection({
/* ... */
});
const [rows] = await conn.query("SELECT * FROM users WHERE id = ?", [
event.id,
]);
await conn.end();
return rows;
};

同理,HTTP client、SDK 客户端、只读配置的加载等都建议在 handler 外初始化。

常见问题:getWXContext() 返回结果不可信

微信小程序·云开发场景下,一个由实例复用引起的常见问题是:cloud.getWXContext() 返回的 openId 等信息有时不准确。

// index.js
const cloud = require("wx-server-sdk");
exports.main = async (event, context) => {
// 这里获取到的 openId、appId 和 unionId 是可信的,注意 unionId 仅在满足 unionId 获取条件时返回
let { OPENID, APPID, UNIONID } = cloud.getWXContext();

return {
OPENID,
APPID,
UNIONID,
};
};

如果云函数始终从小程序端调用,结果是可信的。但如果同一个云函数同时被小程序、Web、HTTP 等多种方式调用,就会出问题:

  • cloud.getWXContext() 是通过运行时环境变量来获取 openId 等信息的。
  • 非小程序端调用时,这些环境变量并不会被设置,但由于实例复用,进程中可能残留着上一次小程序调用时写入的环境变量。
  • 结果就是:非小程序调用有时会读到"别人的 openId",导致越权风险。

规避方式

  • 如果需要用 cloud.getWXContext() 获取用户信息,请保证该云函数的调用来源只有小程序端。
  • 混合调用场景下,改用请求参数显式传入用户身份,或在函数入口显式清理环境变量。