【问题标题】:Angular Universal + Firebase Cloud Function causing memory limit exceededAngular Universal + Firebase Cloud Function 导致超出内存限制
【发布时间】:2020-05-23 20:49:43
【问题描述】:

目前我正在开发一个使用 Firebase 作为后端的 Angular 8 应用程序。

我关注Jeff Delaney's Tutorial 并成功部署在名为 ssr my express server 的云功能上。

一切正常...几乎!部署 ssr 功能后,我可以看到所有云功能的内存使用量增加,即使是最小的(1 个事务具有 get 和 1 个事务中的更新):

functions/src/index.ts

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
import * as functions from 'firebase-functions';
// The Firebase Admin SDK to access Firestore.
import * as admin from 'firebase-admin';

admin.initializeApp();

export const ssr = functions.https.onRequest(require(`${process.cwd()}/dist/server`).app);

export const unsubscribeToMeal = functions.region('europe-west1').https.onCall((data) => {
  const mealDoc = admin.firestore().doc('meals/' + data.meal.uid);
  const adminDoc = admin.firestore().doc('users/' + data.meal.adminId);
  return admin.firestore().runTransaction(t => {
    return t.get(mealDoc)
      .then(doc => {
        const meal = doc.data();
        if (doc.exists && meal) {
          const promises = [];
          const participantIndex = meal.users.findIndex((element: any) => {
            return element.detail.uid === data.userToUnsubscribe.uid;
          });
          meal.users.splice(participantIndex, 1);
          const pendingRequest = meal.users.filter((user: any) => user.status === 'pending').length > 0;
          const p4 = t.update(mealDoc, { 'users': meal.users, 'participantId': admin.firestore.FieldValue.arrayRemove(data.userToUnsubscribe.uid), 'nbRemainingPlaces': meal['nbRemainingPlaces'] + 1, 'pendingRequest': pendingRequest })
          promises.push(p4);
          if (promises.length > 0) {
            return Promise.all(promises)
              .then(() => console.log('user unsubscribed'))
              .catch((err: any) => console.log('error unsubscribing user : ' + data.userToUnsubscribe.first_name + ' ' + data.userToUnsubscribe.last_name + ' of the meal ' + meal.uid + '. Err : ' + err))
          }
          else {
            // doc.data() will be undefined in this case
            throw new functions.https.HttpsError('not-found', 'no such document!');
          }
        } else {
          // doc.data() will be undefined in this case
          console.log('doc does not exist');
          throw new functions.https.HttpsError('not-found', 'no such document!');
        }
      })
  })
});

触发 unsubscribeToMeal 函数从没有部署 ssr 函数的 60MB 内存使用量到部署 ssr 函数的 240MB 内存使用量。

所以我想知道发生了什么?看起来 express 服务器应用程序是每个云功能实例上的引导程序,这会导致内存使用量增加并因此产生更多费用。

正如 Doug Stevenson 解释的 here,我将全局变量限制为最小化冷启动,所以不应该这样。

server.ts

(global as any).WebSocket = require('ws');
(global as any).XMLHttpRequest = require('xhr2');

import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { enableProdMode } from '@angular/core';
import * as express from 'express';
import { join } from 'path';

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
export const app = express();
import * as cookieParser from 'cookie-parser';
import { from } from 'rxjs';
app.use(cookieParser());

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist/browser');

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap } = require('./dist/server/main');


// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

app.set('view engine', 'html');
app.set('views', DIST_FOLDER);

// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express.static(DIST_FOLDER, {
  maxAge: '1y'
}));

// All regular routes use the Universal engine
app.get('*', (req: express.Request, res: express.Response) => {
  res.render('index2', {
    req
  });
});

// Start up the Node server
app.listen(PORT, () => {
  console.log(`Node Express server listening on http://localhost:${PORT}`);
});

任何解决方案仍然具有低内存使用率(不增加函数的内存限制)并且同时具有快速服务器的 ssr 函数用于角度通用?

【问题讨论】:

    标签: angular firebase google-cloud-functions out-of-memory angular-universal


    【解决方案1】:

    我认为内存的增加并不意外。您的教程中有描述:

    Angular 是一个客户端框架,旨在在浏览器中运行应用程序。 Universal 是一个可以在服务器上运行你的 Angular 应用的工具,...

    因此,使用 SSR 解决方案,您将运行 Angular 应用程序表单客户端移动到服务器,因此所有代码在客户端正常运行,现在在服务器上运行。我不认为在不保留所需资源的情况下运行某些东西是可能的......

    也许在这种特殊情况下,AppEngine 会更好地部署它......我希望它能以某种方式帮助你:)

    【讨论】:

    • 在服务器上使用的内存增加当然是正常的。但是,我知道每个函数在被调用时都会生成一个新的服务器实例,这就是为什么我不明白为什么我的 ssr 函数会影响我的 unsubscribeToMeal 函数的内存。
    【解决方案2】:

    在与 firebase 社区取得联系后:“使用 firebase deploy --only 函数将为您的所有函数重新部署代码,并且使用的内存将是所有这些函数一起加载的累积总和”。 因此解决方案是将函数拆分为不同的文件,并使用 process.env.[functionName] 和 if 语句为 explained here 以避免这种 expressFunction 内存的增加。

    【讨论】:

    • 我在想这个,但是我没有找到任何教程......你能成功使用它吗?
    • 我的回答是长期只是为了评论
    【解决方案3】:

    是的 vitooh !您需要将每个函数拆分为 ts 或 js 文件。这是我的新结构:

    • /函数
      • /src
        • index.ts
        • /my_functs
          • ssr.ts
          • unsubscribeToMeal.ts
          • ...其他功能

    每个函数文件都需要以exports = module.exports = functions开头... 例如,这是我的新 express 函数的代码(你可以与我在第一篇文章中的代码进行比较)

    functions/src/my_functs/ssr.ts file
    // The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
    import * as functions from 'firebase-functions';
    
    exports = module.exports = functions.https.onRequest(require(`${process.cwd()}/dist/server`).app);
    

    不要忘记在每个文件中导入所需的内容,例如“import * as functions from 'firebase-functions';”。我还更改了我在部署到 firebase 时使用的 index.ts 文件:firebase deploy --only 函数并从函数文件夹安装了 glob (npm i glob)。

    functions/src/index.ts file
    
    const glob = require("glob");
    if (process.env.FUNCTION_NAME) {
      // Specific function requested, find and export only that function
      const functionName = process.env.FUNCTION_NAME;
      const files = glob.sync(`./my_functs/${functionName}.@(js|ts)`, { cwd: __dirname, ignore: './node_modules/**' });
      const file = files[0];
      exports[functionName] = require(file);
    } else {
      // Name not specified, export all functions
      const files = glob.sync('./my_functs/*.@(js|ts)', { cwd: __dirname, ignore: './node_modules/**' });
      for (let f = 0, fl = files.length; f < fl; f++) {
        const file = files[f];
        const functionName = file.split('/').pop().slice(0, -3); // Strip off '.(js|ts)'
        exports[functionName] = require(file);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-12-26
      • 1970-01-01
      • 2019-07-07
      • 2017-09-04
      • 1970-01-01
      • 2021-12-07
      • 2017-03-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多