我们正在开发一个使用新的firebase云功能的应用程序.目前正在发生的事情是将事务放入队列节点.然后该函数删除该节点并将其放入正确的节点.由于能够脱机工作,因此已实施.
我们目前的问题是功能的速度.该功能本身需要大约400毫秒,所以没关系.但有时功能需要很长时间(大约8秒),而条目已经添加到队列中.
我们怀疑服务器需要时间来启动,因为当我们在第一次执行后再次执行操作时.它花费的时间更少.
有什么方法可以解决这个问题吗?在这里,我添加了我们的功能代码.我们怀疑它没有任何问题,但我们添加它以防万一.
const functiOns= require('firebase-functions'); const admin = require('firebase-admin'); const database = admin.database(); exports.insertTransaction = functions.database .ref('/userPlacePromotionTransactionsQueue/{userKey}/{placeKey}/{promotionKey}/{transactionKey}') .onWrite(event => { if (event.data.val() == null) return null; // get keys const userKey = event.params.userKey; const placeKey = event.params.placeKey; const promotiOnKey= event.params.promotionKey; const transactiOnKey= event.params.transactionKey; // init update object const data = {}; // get the transaction const transaction = event.data.val(); // transfer transaction saveTransaction(data, transaction, userKey, placeKey, promotionKey, transactionKey); // remove from queue data[`/userPlacePromotionTransactionsQueue/${userKey}/${placeKey}/${promotionKey}/${transactionKey}`] = null; // fetch promotion database.ref(`promotions/${promotionKey}`).once('value', (snapshot) => { // Check if the promotion exists. if (!snapshot.exists()) { return null; } const promotion = snapshot.val(); // fetch the current stamp count database.ref(`userPromotionStampCount/${userKey}/${promotionKey}`).once('value', (snapshot) => { let currentStampCount = 0; if (snapshot.exists()) currentStampCount = parseInt(snapshot.val()); data[`userPromotionStampCount/${userKey}/${promotionKey}`] = currentStampCount + transaction.amount; // determines if there are new full cards const currentFullcards = Math.floor(currentStampCount > 0 ? currentStampCount / promotion.stamps : 0); const newStamps = currentStampCount + transaction.amount; const newFullcards = Math.floor(newStamps / promotion.stamps); if (newFullcards > currentFullcards) { for (let i = 0; i <(newFullcards - currentFullcards); i++) { const cardTransaction = { action: "pending", promotion_id: promotionKey, user_id: userKey, amount: 0, type: "stamp", date: transaction.date, is_reversed: false }; saveTransaction(data, cardTransaction, userKey, placeKey, promotionKey); const completedPromotion = { promotion_id: promotionKey, user_id: userKey, has_used: false, date: admin.database.ServerValue.TIMESTAMP }; const promotiOnPushKey= database .ref() .child(`userPlaceCompletedPromotions/${userKey}/${placeKey}`) .push() .key; data[`userPlaceCompletedPromotions/${userKey}/${placeKey}/${promotionPushKey}`] = completedPromotion; data[`userCompletedPromotions/${userKey}/${promotionPushKey}`] = completedPromotion; } } return database.ref().update(data); }, (error) => { // Log to the console if an error happened. console.log('The read failed: ' + error.code); return null; }); }, (error) => { // Log to the console if an error happened. console.log('The read failed: ' + error.code); return null; }); }); function saveTransaction(data, transaction, userKey, placeKey, promotionKey, transactionKey) { if (!transactionKey) { transactiOnKey= database.ref('transactions').push().key; } data[`transactions/${transactionKey}`] = transaction; data[`placeTransactions/${placeKey}/${transactionKey}`] = transaction; data[`userPlacePromotionTransactions/${userKey}/${placeKey}/${promotionKey}/${transactionKey}`] = transaction; }
Frank van Pu.. 92
这里有一个firebaser
听起来你正在经历一个所谓的冷启动功能.
当您的功能在一段时间内未执行时,Cloud Functions会将其置于使用较少资源的模式中.然后,当您再次点击该功能时,它将从此模式恢复环境.恢复所需的时间包括固定成本(例如恢复容器)和部分可变成本(例如,如果使用大量节点模块,则可能需要更长时间).
我们不断监控这些操作的性能,以确保开发人员体验和资源使用之间的最佳组合.所以期望这些时间随着时间的推移而改善
好消息是你应该只在开发过程中体验这一点.一旦你的功能在生产中经常被触发,很可能它们再也不会出现冷启动.
这里有一个firebaser
听起来你正在经历一个所谓的冷启动功能.
当您的功能在一段时间内未执行时,Cloud Functions会将其置于使用较少资源的模式中.然后,当您再次点击该功能时,它将从此模式恢复环境.恢复所需的时间包括固定成本(例如恢复容器)和部分可变成本(例如,如果使用大量节点模块,则可能需要更长时间).
我们不断监控这些操作的性能,以确保开发人员体验和资源使用之间的最佳组合.所以期望这些时间随着时间的推移而改善
好消息是你应该只在开发过程中体验这一点.一旦你的功能在生产中经常被触发,很可能它们再也不会出现冷启动.
更新 - 看起来很多这些问题可以使用隐藏变量解决,process.env.FUNCTION_NAME
如下所示:https://github.com/firebase/functions-samples/issues/170#issuecomment-323375462
使用代码更新 - 例如,如果您有以下索引文件:
... exports.doSomeThing = require('./doSomeThing'); exports.doSomeThingElse = require('./doSomeThingElse'); exports.doOtherStuff = require('./doOtherStuff'); // and more.......
然后将加载所有文件,并且还将加载所有这些文件的要求,从而导致大量开销并污染所有功能的全局范围.
而是将您的包括分开:
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'doSomeThing') { exports.doSomeThing = require('./doSomeThing'); } if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'doSomeThingElse') { exports. doSomeThingElse = require('./doSomeThingElse'); } if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'doOtherStuff') { exports. doOtherStuff = require('./doOtherStuff'); }
这只会在专门调用该函数时加载所需的文件; 让您的全球范围更加清洁,从而加快冷靴的速度.
这应该允许比我在下面做的更简洁的解决方案(尽管下面的解释仍然有效).
原始答案
看起来需要文件和在全局范围内发生的一般初始化是冷启动期间减速的一个重要原因.
随着项目获得更多功能,全局范围受到越来越多的污染,使问题变得更糟 - 特别是如果您将功能范围限定在单独的文件中(例如Object.assign(exports, require('./more-functions.js'));
在您的文件中使用)index.js
.
通过将所有需求转移到如下所示的init方法,然后将其作为该文件的任何函数定义中的第一行,我已经设法看到冷启动性能的巨大收益.例如:
const functiOns= require('firebase-functions'); const admin = require('firebase-admin'); // Late initialisers for performance let initialised = false; let handlebars; let fs; let path; let encrypt; function init() { if (initialised) { return; } handlebars = require('handlebars'); fs = require('fs'); path = require('path'); ({ encrypt } = require('../common')); // Maybe do some handlebars compilation here too initialised = true; }
当我将这种技术应用到一个包含8个文件的~30个函数的项目时,我已经看到从大约7-8s到2-3s的改进.这似乎也导致函数需要不经常冷启动(可能是由于较低的内存使用量?)
不幸的是,这仍然使HTTP功能几乎不能用于面向用户的生产用途.
希望Firebase团队将来有一些计划允许适当的功能范围,以便只需要为每个功能加载相关模块.
我面临着与firestore云功能类似的问题.最大的是性能.特别是在早期创业公司的情况下,当你无法让早期客户看到"呆滞"的应用程序时.一个简单的文档生成功能,例如给出:
- 函数执行花了9522毫秒,完成状态代码:200
然后:我有一个直截了当的条款和条件页面.使用云功能,由于冷启动执行甚至有时需要10-15秒.然后我将它移动到一个node.js应用程序,托管在appengine容器上.时间已经缩短到2-3秒.
我一直在比较mongodb的许多功能和firestore,有时我也想知道在我的产品的早期阶段我是否也应该转移到另一个数据库.我在firestore中最大的优点是onCreate的触发器功能,onUpdate文件对象.
https://db-engines.com/en/system/Google+Cloud+Firestore%3BMongoDB
基本上,如果您的网站的静态部分可以卸载到appengine环境,也许不是一个坏主意.