node.js - NodeJS和Mongo线谁上线

标签 node.js mongodb

TL; 博士
记录在线用户并报告计数(基于 mongo find)

我们为学校和学生提供了一个 saas 应用程序,作为其中的一部分,我一直想要一个在线自动收报机的“直播”。
学校的老师会看到计数器,学生和家长会触发它。
我有一个 socket.io 从 web 应用程序连接到 NodeJS 应用程序。
在有大量流量的地方,Node/Mongo 服务器无法处理它,与其投入更多资源,我认为最好优化代码 - 因为我不知道我在做什么:D
每个学生页面加载:
使用以下对象创建 socket.io 连接:

{
'name': 'student or caregiver name',
'studentID': 123456,
'schoolID': 123,
'role': 'student', // ( or 'mother' or 'father' )
'page': window.location
}
在我的 Node 脚本中:
io.on('connection', function(client) {
    // if it's a student connection.. 
    if(client.handshake.query.studentID) {
        let student = client.handshake.query; // that student object
            student.online = new Date();
            student.offline = null;
        db.collection('students').updateOne({ 
           "reference": student.schoolID + student.studentID + student.role }, { $set: student 
        }, { upsert: true });


    }

    // IF STAFF::: just show count!
    if(client.handshake.query.staffID) {
      db.collection('students').find({ 'offline': null, 'schoolID':client.handshake.query.schoolID }).count(function(err, students_connected) {
          
          emit('online_users' students_connected);
       });
    }



    client.on('disconnect', function() {
        // then if the students leaves the page..
        if(client.handshake.query.studentID) {
            db.collection('students').updateMany({ "reference": student.reference }, { $set: { "offline": new Date().getTime() } })
            .catch(function(er) {});
         }

         // IF STAFF::: just show updated count!
         if(client.handshake.query.staffID) {
           db.collection('students').find({ 'offline': null, 'schoolID':client.handshake.query.schoolID }).count(function(err, students_connected) {
          
                emit('online_users' students_connected);
            });
         }
     });
});


您会添加哪些 Mongo 索引,您是否会以不同的方式(并在不同的集合中)存储在线学生到像这样的“页面跟踪”类型的交易?
(这会记录页面和持续时间,所以我稍后有另一个调用来提取它 - 但这并没有被大量使用或导致问题。
如果分开,然后插入,然后删除?
EMIT() 给员工用户,我如何只向与学生具有相同学校 ID 的员工发送?
谢谢!

最佳答案

您已简要介绍了该问题,但没有对问题发生的原因进行诊断。基于一些假设,我将尝试回答您的问题。
首先,您已经提到您希望就什么索引可以帮助您的事业提出建议,根据您所提到的,这是一个写入繁重的系统,原则上索引只会减慢写入速度,因为在每次写入时,处理索引的 Btree也将不得不更新。尽管读取变得更好,特别是在包含大量数据的庞大集合的情况下。
因此,如果您的集合有 100 万个文档,则索引可以为您提供很大帮助。由于 Btree,它可以帮助您仅浏览所需的数据,甚至无需扫描所有数据。
并且应该根据您所做的读取调用专门创建索引。
例如

{"student_id" : "studentID", "student_fname" : "Fname"}
如果这里的 read 调用是基于 student_id然后对其进行创建和索引,如果涉及多个值(相等 - 排序或任何内容),则在这些字段上创建复合索引,首先优先考虑相等字段,然后优先处理范围和排序字段。
现在是问题的第二部分,在这种情况下什么会更好。
这是一个主观的事情,我相信每个人都会有不同的方法。我的解决方案基于一些假设。
假设
该系统需要满足特定功能,即学生的在线状态在某个时间间隔内更新,并且该数据可供家长、教师等阅读。
您正在使用的套接字,如果它们始终保持连续连接,那么与服务器的并发连接就是那么多,如果需要与否,我不知道。但是正如您已经知道的那样,并发连接对于服务器来说很重,除非需要 100% 尝试混合方法。
如果您可以暂时断开连接或仅在很短的时间内保持与服务器的连接,那么请考虑这一点。这基本上意味着,您优雅地断开与服务器的连接,连接发送数据并重复。
或者,只是采用心跳系统,您的前端应用程序将在设置的时间间隔后调用 API 并 ping 服务器,基于此您可以处理学生是否在线,有点延迟,是的,但易于扩展。
请使用 redis 或任何其他内存数据存储进行如此频繁的写入,特别是当您不需要长时间保存数据时。
例如,假设我们为用户的每个类/部分使用 redis 列表,并且仅在从前端收到他们的最后一次心跳时更新时间戳(纪元)。
在一个有 60 名学生的类(class)中,根据 student_id 或类似的东西对学生进行排序。
为该类创建一个列表
对于升序学生列表中的第一个 student_id,像这样更新纪元
LSET mylist 0 "1266126162661" //Epoch Time Stamp 
0 是您的第一个学生,59 是我们的第 60 个学生,请在每次心跳时更新。通过 API 或您拥有的相同套接字系统。取决于您的用例。
当需要读取调用时
LRANGE classname/listname 0 59
现在您拥有所有用户的时代,通过数据库或另一个列表维护学生列表,您可以简单地将索引与特定学生匹配。
LSET studentList 0 "student_id" //Student id of the student or any other data, I am trying to explain the logic
在前端,当您拥有 epoch 时,会根据您的用例考虑最新的 epoch,例如假设我希望学生在 5 分钟前收到心跳时在线。
当前时间戳 - 时间戳(如果少于 5 分钟(以秒为单位))然后在线或离线。

关于node.js - NodeJS和Mongo线谁上线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63590857/

相关文章:

node.js - 为什么在 Express 路线运行后我无法调用 `next`?

javascript - NodeJS如何循环查询

mongodb - 删除 MongoDB 集合中的数据但保留索引的更好方法

node.js - 在 Mongoose 中添加多个验证

node.js - Mongoose:不将 _id 放入嵌入式文档

java - MongoDB 3.3.0以上版本中QueryBuilder和BasicDBObjectBuilder的使用

node.js - 在 mongoose 预查找中间件上添加条件

javascript - 无法读取未定义的属性 'undefined',但我确定 var 是有效的

node.js - browserify 从命令行不执行任何操作

node.js - 为什么我应该创建一个 json 来保存来自 Mongoose + MongoDB + Node.JS 的文档?