node.js之Firestore 实时更新 NodeJS 中的连接

davidwang456 阅读:43 2025-05-04 20:05:19 评论:0

我正在开发一个 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 小时后,监听器仍然可以正常工作并且没有发生内存泄漏。


标签:NodeJs
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号