我正在开发一个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应用的原因而断开连接时,它可以工作,但是我注意到服务器上的巨大内存泄漏,我需要一些帮助
答案 0 :(得分:0)
最后,我可以向自己反抗。首先,我尝试过的第二个解决方案是一个非常糟糕的解决方案,因为通过Admin SDK创建的每个新应用都存储在RAM中,拥有20/30用户的应用达到了超过1GB的RAM,这是绝对不能接受的。
因此第一个实现是更好的解决方案,无论如何,我错了在onSnapshot侦听器生命周期中注册/注销。每个onSnapshot()
调用都返回一个不同的函数,即使在同一引用上被调用也是如此。因此,我没有打开套接字,而是关闭了监听器,而是打开了另一个监听器。这应该是这样:
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小时后,侦听器仍然可以正常工作,并且不会发生内存泄漏。