如何加速Firebase Functions+Express应用的慢请求(耗时超8秒)
Firebase Cloud Functions 请求性能优化方案
问题背景
我们的后端基于Node.js、Express和Firebase构建,目前所有请求(包括简单查询)耗时普遍超过8秒,已排除冷启动问题(后续请求同样缓慢)。尝试过拆分接口文件、移除第三方库,性能无改善。
环境配置信息
package.json 配置
{ "name": "functions", "description": "Cloud Functions for Firebase", "scripts": { "serve": "firebase serve --only functions", "shell": "firebase functions:shell", "start": "npm run shell", "deploy": "firebase deploy --only functions", "logs": "firebase functions:log" }, "engines": { "node": "14" }, "dependencies": { "@google-cloud/storage": "^5.8.2", "@sendgrid/mail": "^7.2.1", "algoliasearch": "^4.3.0", "bcrypt": "^5.1.0", "busboy": "^0.3.1", "cookie-parser": "^1.4.5", "cors": "^2.8.5", "dayjs": "^1.10.4", "dotenv": "^8.2.0", "easy-soap-request": "^4.1.3", "express": "^4.17.1", "firebase": "^7.15.5", "firebase-admin": "^8.6.0", "firebase-functions": "^3.23.0", "fs-extra": "^9.0.1", "jwt-decode": "^2.2.0", "moment": "^2.29.1", "request": "^2.88.2", "sharp": "^0.25.4", "sib-api-v3-sdk": "^8.4.2", "uuid": "^8.2.0", "xml-js": "^1.6.11" }, "devDependencies": { "firebase-functions-test": "^0.1.6" }, "private": true }
index.js 核心代码
require("dotenv").config(); const functions = require("firebase-functions"); const express = require("express"); const app = express(); const cookieParser = require("cookie-parser"); const cors = require("cors"); app.use(cors()); app.use(cookieParser()); app.use(express.json()); const dashboardRoutes = require("./routes/dashboardRoutes"); const userRoutes = require("./routes/userRoutes"); const pagesRoutes = require("./routes/pagesRoutes"); const orderRoutes = require("./routes/orderRoutes"); const cartRoutes = require("./routes/cartRoutes"); const wishlistRoutes = require("./routes/wishlistRoutes"); const authRoutes = require("./routes/authRoutes"); const storeRoutes = require("./routes/storeRoutes"); const createSellerRoutes = require("./routes/createSellerRoutes"); app.use("/", pagesRoutes); app.use("/user", userRoutes); app.use("/dash", dashboardRoutes); app.use("/order", orderRoutes); app.use("/cart", cartRoutes); app.use("/wishlist", wishlistRoutes); app.use("/auth", authRoutes); app.use("/s", storeRoutes); app.use("/cr", createSellerRoutes); const { cron_job1, cron_job2, cron_job3, cron_job4, } = require("./triggers/search_triggers_and_cron_jobs"); // 非实际文件名 const { /* other cron jobs */ } = require("./cron-jobs"); const { update_exchange_currency_rates, } = require("./cron-jobs/currency_exchange_rates"); const { reset_product_visits } = require("./triggers/products"); const { Home } = require("./handlers/pages"); const { db } = require("./util/admin"); const { product_basic_obj } = require("./util/product_basic_obj"); exports.apis = functions.https.onRequest(app); // 示例慢请求接口逻辑 app.get("/test-home", (req, res) => { let content = {}; db.collection("products") .where("status", "==", "active") .orderBy("visited", "desc") .limit(20) .get() .then((data) => { content.popular_today = []; data.forEach((x) => { content.popular_today.push(product_basic_obj(x.data())); }); return db .collection("products") .where("status", "==", "active") .orderBy("todaysSales", "desc") .limit(20) .get(); }) .then((data) => { content.hot_today = []; data.forEach((x) => { content.hot_today.push(product_basic_obj(x.data())); }); return db.collection("departments").get(); }) .then((data) => { content.departments = []; data.forEach((x) => { content.departments.push(x.data()); }); return db .collection("departments") .orderBy("products_sold_today", "desc") .limit(6) .get(); }) .then((data) => { content.top_departments = []; data.forEach((x) => { content.top_departments.push(x.data()); }); return res.status(200).json(content); }); }); // cron jobs 导出 exports.cron_job1 = cron_job1; exports.cron_job2 = cron_job2; exports.cron_job3 = cron_job3; exports.cron_job4 = cron_job4;
执行日志
i functions: Beginning execution of "us-central1-apis" ⚠ Google API requested! - URL: "https://www.googleapis.com/oauth2/v4/token" - Be careful, this may be a production service. i functions: Finished "us-central1-apis" in ~9s i functions: Finished "us-central1-apis" in ~8s i functions: Beginning execution of "us-central1-apis" i functions: Finished "us-central1-apis" in ~7s i functions: Beginning execution of "us-central1-apis" i functions: Finished "us-central1-apis" in ~7s i functions: Beginning execution of "us-central1-apis" i functions: Finished "us-central1-apis" in ~7s i functions: Beginning execution of "us-central1-apis" i functions: Finished "us-central1-apis" in ~6s i functions: Beginning execution of "us-central1-apis" i functions: Finished "us-central1-apis" in ~7s i functions: Beginning execution of "us-central1-apis" i functions: Finished "us-central1-apis" in ~7s
优化方案
1. 并行化Firestore查询
当前示例接口中4个Firestore查询是串行执行的,总耗时为各查询耗时之和。改成并行执行可大幅减少总耗时:
app.get("/test-home", async (req, res) => { try { // 并行发起所有查询 const [popularSnapshot, hotSnapshot, deptsSnapshot, topDeptsSnapshot] = await Promise.all([ db.collection("products") .where("status", "==", "active") .orderBy("visited", "desc") .limit(20) .get(), db.collection("products") .where("status", "==", "active") .orderBy("todaysSales", "desc") .limit(20) .get(), db.collection("departments").get(), db.collection("departments") .orderBy("products_sold_today", "desc") .limit(6) .get() ]); // 处理查询结果 const content = { popular_today: popularSnapshot.docs.map(x => product_basic_obj(x.data())), hot_today: hotSnapshot.docs.map(x => product_basic_obj(x.data())), departments: deptsSnapshot.docs.map(x => x.data()), top_departments: topDeptsSnapshot.docs.map(x => x.data()) }; return res.status(200).json(content); } catch (err) { return res.status(500).json({ error: err.message }); } });
2. 检查Firestore索引与查询效率
- 确保所有带
where+orderBy的查询都有复合索引,Firebase控制台会在查询失败时提示创建索引,未创建索引会导致全表扫描,大幅增加耗时。 - 避免
db.collection("departments").get()这类全集合查询,如果部门数据量大,考虑分页或只查询必要字段(用select()方法)。
3. 升级依赖版本
当前使用的firebase-admin(8.6.0)、firebase-functions(3.23.0)版本较旧,新版本通常包含性能优化和bug修复:
- 升级
firebase-admin到最新稳定版 - 升级
firebase-functions到最新稳定版 - 同步升级其他相关依赖(如
firebase)
4. 启用Cloud Functions的CPU/内存升级
默认的Cloud Functions资源配置(256MB内存)可能不足以应对高负载,可在函数配置中提升资源:
exports.apis = functions.runWith({ memory: "1GB", // 可选 512MB/1GB/2GB等 timeoutSeconds: 30 }).https.onRequest(app);
5. 缓存高频查询结果
对于首页这类高频访问的接口,可将查询结果缓存到内存缓存或Firebase Cache,缓存有效期设为5-15分钟,避免重复查询Firestore:
const cache = require('memory-cache'); app.get("/test-home", async (req, res) => { const cacheKey = "home_page_content"; const cachedContent = cache.get(cacheKey); if (cachedContent) { return res.status(200).json(cachedContent); } // 执行并行查询(同方案1) const [popularSnapshot, hotSnapshot, deptsSnapshot, topDeptsSnapshot] = await Promise.all([...]); const content = { ... }; cache.put(cacheKey, content, 10 * 60 * 1000); // 缓存10分钟 return res.status(200).json(content); });
6. 移除不必要的中间件与依赖
检查package.json中的依赖,移除未使用的库(如easy-soap-request、xml-js如果当前接口未用到),减少函数启动时的加载时间。同时,确保Express中间件只在需要的路由上使用,而非全局挂载。
7. 使用Firestore的select()减少数据传输
查询时只获取需要的字段,避免传输冗余数据:
db.collection("products") .where("status", "==", "active") .orderBy("visited", "desc") .limit(20) .select("id", "name", "price", "image") // 只获取必要字段 .get()
内容的提问来源于stack exchange,提问作者Harmony Chikari
相关产品推荐
相关产品推荐

