node.js之Firestore 实时更新 NodeJS 中的连接
我正在开发一个 NodeJS Web 应用程序,以通过 Admin SDK 从 Firestore DB 接收实时更新。
这是 Firestore 对象的初始化代码。在部署应用程序时(在 AWS Elastic Beanstalk 上),它只执行一次:
const admin = require('firebase-admin');
var serviceAccount = require('./../key.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
var db = admin.firestore();
FUNC.firestore = db;
然后我在 websocket 通信中使用这个 firestore 对象向浏览器发送实时更新。这个想法是使用服务器作为浏览器和 Firestore 之间的代理。
socket.on('open', function (client) {
var query = FUNC.firestore.collection("notifications").doc(client.user.id.toString()).collection("global");
query.onSnapshot(querySnapshot => {
querySnapshot.docChanges().forEach(change => {
client.send({ id: change.doc.id, body: change.doc.data(), type: change.type });
});
}, err => {
console.log(`Encountered error: ${err}`);
});
});
socket.on('close', function (client) {
var unsub = FUNC.firestore.collection("notifications").doc(client.user.id.toString()).collection("global").onSnapshot(() => {
});
unsub();
});
它运行了一段时间,但几个小时后客户端停止接收
onSnapshot()更新,一段时间后服务器记录错误:
Encountered error: Error: 10 ABORTED: The operation was aborted.
怎么了?我应该在每个连接上初始化 firestore 吗?是否存在生命周期错误?
谢谢
编辑(一个非常糟糕的解决方案)
我尝试为每个登录的用户创建一个 firebase-admin 应用程序实例,并以这种方式更改了我的代码
const admin = require('firebase-admin');
var serviceAccount = require('./../key.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
FUNC.getFirestore = function (user) {
try {
user.firebase = admin.app(user.id.toString());
return user.firebase.firestore();
} catch(e) {
//ignore
}
var app = admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
}, user.id.toString());
user.firebase = app;
return user.firebase.firestore();
}
FUNC.removeFirebase = function (user) {
if (user.firebase) {
user.firebase.delete();
}
}
然后是套接字监听器:
self.on('open', function (client) {
var query = FUNC.getFirestore(client.user).collection("notifications").doc(client.user.id.toString()).collection("global");
query.onSnapshot(querySnapshot => {
querySnapshot.docChanges().reverse();
querySnapshot.docChanges().forEach(change => {
client.send({ id: change.doc.id, body: change.doc.data(), type: change.type });
});
}, err => {
console.log(`Encountered error: ${err}`);
});
});
self.on('close', function (client) {
var unsub = FUNC.getFirestore(client.user).collection("notifications").doc(client.user.id.toString()).collection("global").onSnapshot(() => {
});
unsub();
FUNC.removeFirebase(client.user);
});
因此,当客户端因某种原因断开连接时,服务器会删除其 Firebase 应用程序,它可以工作,但我注意到 巨大的服务器内存泄漏,我需要一些帮助
请您参考如下方法:
更新的答案
经过多次研究,我明白这种方法是错 .当然,旧的答案可能是一种解决方法,但不是问题的真正解决方案,因为 Firestore 并非旨在执行以下操作:Firestore <--(Admin SDK)--> Server <--(WebSocket)--> Client .
为了创建最佳通信,我了解并应用了 Firestore 安全规则 ( https://firebase.google.com/docs/firestore/security/get-started ) 和自定义 token 生成 ( https://firebase.google.com/docs/auth/admin/create-custom-tokens )。所以正确的流程是:
Client login request --> Server + Admin SDK generate custom auth token and return to client
然后,实时通信将仅在 Client 和 Firestore 本身之间进行,因此:
Client + Custom Auth Token <--(Firebase JS SDK)--> Firestore DB
如您所见,服务器不再参与实时通信,但客户端直接从 Firestore 接收更新。
旧答案
终于可以自己回答了。首先,我尝试过的第二种解决方案非常糟糕,因为通过 Admin SDK 创建的每个新应用程序都存储在 RAM 中,有 20/30 的用户,该应用程序的 RAM 超过 1GB,绝对不能接受。
所以第一个实现是更好的解决方案,无论如何我错误地注册/取消注册 onSnapshot 监听器生命周期。每个
onSnapshot() call 返回一个不同的函数,即使在同一个引用上调用。因此,我没有在套接字关闭时关闭监听器,而是打开了另一个监听器。应该是这样:
socket.on('open', function (client) {
var query = FUNC.firestore.collection("notifications").doc(client.user.id.toString()).collection("global");
client.user.firestoreUnsub = query.onSnapshot(querySnapshot => {
querySnapshot.docChanges().forEach(change => {
client.send({ id: change.doc.id, body: change.doc.data(), type: change.type });
});
}, err => {
console.log(`Encountered error: ${err}`);
});
});
socket.on('close', function (client) {
client.user.firestoreUnsub();
});
将近 48 小时后,监听器仍然可以正常工作并且没有发生内存泄漏。
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。



