【问题标题】:Timeout in Firebase FunctionsFirebase 函数超时
【发布时间】:2020-06-09 04:44:27
【问题描述】:

我正在本地测试一组 Cloud Functions,并根据需要为数据库、Firestore、Hosting、PubSub 和 Functions 设置模拟器。我正在尝试执行以下操作:

  1. functions/index.js 文件中,我正在导入用作不同端点的 HTTP 触发器的模块。
  2. 我正在使用依赖注入 (DI) 来允许模块从 functions/index.js 文件访问所有常见的依赖项。

到目前为止我的代码方法:

函数/index.js

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require('express');
const cors = require('cors');
const session = require('express-session');
const firestoreSession = require('connect-session-firebase')(session);
const cookieParser = require('cookie-parser');
const { validationResult } = require('express-validator');

const ref = admin.initializeApp({
    databaseURL: 'http://localhost:8080',
    credential: admin.credential.cert('path/to/credential.js')
});

// services
const UserService = require('./user/index')({
    express, cookieParser, cors, ref,
    session, firestoreSession, validationResult
});

exports.user = functions.https.onRequest(UserService);

HTTP 请求触发器是 express 的实例。我正在注入依赖项(如在 UserService 中),然后在 URL BASE_URL/user 下公开触发器。很少有其他类似的服务。在 UserService 中,我有以下内容:

module.exports = function({
    express, cors,
    session, firestoreSession,
    cookieParser, validationResult, ref
}) {
    const Validators = require('./validators');

    const userserv = express();
    userserv.use(cors({ origin: true }));
    userserv.use(session({
        name: 'user_session',
        resave: true,
        saveUninitialized: true,
        secret: 'TOKEN_SECRET_HERE',
        store: new firestoreSession({
            database: ref.database()
        })
    }));

    userserv.post('/login', (req, res, nxt) => {
        res.json({
            status: 200,
            message: 'Logged In'
        });
    });
    // ... other route handlers
    return userserv;
};

functions/user/index.js

现在,当我启动模拟器时,一切正常。当我使用 curl 发送 POST 请求时,该函数会冻结,直到发生超时。这是 curl 请求的示例:

curl -X POST -H 'Content-Type:application/json' -d '{}' http://localhost:5001/<App>/<region>/user/login

当发生超时或手动停止模拟器时,我会看到预期的 JSON 响应。不确定是什么导致功能冻结。我的猜测可能与会话存储的数据库设置有关,这会导致函数进入无限循环的请求。一些建议将不胜感激。

注意:我使用 DI 是因为我想在一个文件中管理导入并在服务之间共享会话存储。我所看到的设置 firebase 会话存储的方式使用来自 admin.initializeApp 的应用程序引用,因此,很容易将其作为参数传递给所有导入的服务模块。

更新 1

问题肯定出在会话中间件上。注释掉functions/user/index.js 中的会话设置代码不会导致服务器在发出请求并按预期及时发送响应时冻结。

更新 2 查看调试日志后,database-debug.log 似乎“有问题”:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by io.netty.util.internal.ReflectionUtil (file:/home/zerocool/.cache/firebase/emulators/firebase-database-emulator-v4.3.1.jar) to field sun.nio.ch.SelectorImpl.selectedKeys
WARNING: Please consider reporting this to the maintainers of io.netty.util.internal.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

【问题讨论】:

  • 添加一些调试日志,以准确了解您的函数能走多远。现在,你没有给自己任何暗示。
  • @DougStevenson 使用调试日志的输出更新了问题。此外,问题在于会话设置,因为将其注释掉可以解决问题。
  • 如果这在部署时有效但在模拟器上无效,请在 Firebase CLI GitHub 上发布一个问题,其中包含有关如何构建一个无法按预期方式工作的完整、最小示例的说明。 github.com/firebase/firebase-tools

标签: node.js firebase express google-cloud-functions firebase-tools


【解决方案1】:

我没有足够的声誉来发表评论。

您设置了密钥吗?

const ref = firebase.initializeApp({
  credential: firebase.credential.cert('path/to/serviceAccountCredentials.json'),
  databaseURL: 'https://databaseName.firebaseio.com'
});

另外,尝试传递标志:resave 和 saveUninitialized

express()
  .use(session({
    store: new FirebaseStore({
      database: ref.database()
    }),
    secret: 'keyboard cat'
    resave: true,
    saveUninitialized: true,
    cookie: {maxAge : 60000,
        secure: false,
        httpOnly: false }
  }));

检查安全性:

{
  "rules": {
    ".read": "false",
    ".write": "false",
    "sessions": {
      ".read": "false",
      ".write": "false"
    },
    "some_public_data": {
      ".read": "true",
      ".write": "auth !== null"
    }
  }
}

使用这些标志启动 CORS:

app.use(function(req, res, next) {
    res.header('Access-Control-Allow-Credentials', true);
    res.header('Access-Control-Allow-Origin', req.headers.origin);
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
    if(req.session){
      res.header("mytoken",req.token);
    }
    next();
});

编辑 1:在 cors 内发送帖子

const cors = require('cors')({origin: true});

userserv.post('/login', (req, res) => {    
     cors(req, res, () => {

      console.log('Sending POST inside CORS')

      res.status(200).send({message: 'Logged In'})

    });            
 });

【讨论】:

  • 您能解释一下为什么需要在 cors 中间件的 res 标头上添加“token”吗?如您所见,我还没有设置任何会话。基本代码应该只返回 JSON 响应。虽然我添加了凭据,但它并没有改变任何东西。至于 cookie,我需要它是安全的并且也只需要 http。对于本地环境,我可以将安全设置为 false,但不能在生产环境中设置。在我的情况下,只有 http 是必需的。如您所述,数据库规则也在那里。
  • 您不需要“令牌”,它只是 http auth 的安全内容。我猜这是 cors 阻止了您的发布请求。浏览器控制台上有消息吗?
  • 感谢您的帮助,但问题与 CORS 无关。从我帖子的更新中可以看出,这是实时数据库设置的问题。我实际上部署了该功能并使用生产数据库 URL 进行了测试,并且它有效;会话也按预期创建。这显然意味着我在本地环境中错误地设置了数据库。
【解决方案2】:

好的,我终于能够运行这些函数并获得预期的结果。正如在原始帖子的更新中所确定的,问题出在connect-session-firebase 商店设置上。为了在本地设置,我尝试了以下方法:

// ... other code unchanged
const ref = admin.initializeApp({
    // remove the following line
    // databaseURL: 'http://localhost:8080',
    credential: admin.credential.cert('path/to/credential.js')
});

另外,根据connect-session-firebase 的文档,我需要使用以下内容设置.env 文件:

FIREBASE_DATBASE_URL=http://localhost:9090
FIREBASE_SERVICE_ACCOUNT=./serviceAccount.json

数据库运行正常,对sessions.json 文件的查询按预期生成了一个条目。部署后也对此进行了测试(为此,我不需要 .env 文件,必须将 databaseURL 更改为项目的实际数据库 URL)。感谢大家的时间和帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-07
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-30
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多