NodeJS + SocketIO 大套接字事件管理
Posted
技术标签:
【中文标题】NodeJS + SocketIO 大套接字事件管理【英文标题】:NodeJS + SocketIO large socket event management 【发布时间】:2015-02-06 21:48:53 【问题描述】:我有一百万个恐龙用户都登录了。
恐龙希望看到其他恐龙何时实时更新他们的个人资料,因此它们被连接到 NodeJS/Mongoose 模型中:
dinosaur.schema.post('save', function (doc)
socket.emit('dinosaur:save', doc);
);
其中socket
是连接恐龙的socket。
恐龙还将看到其他几件事情的实时更新。也许是新闻、cmets 等。
那么,我的问题是,在某些情况下,这种事件的发射会变大并影响性能吗?
在客户端,我会有类似socket.on('dinosaur:save', function())
的东西...我会在不需要的时候销毁监听器。但是,如果我正在收听每一个dinosaur:save
,理论上我可以在一秒钟内节省一百万的时间(比如说,如果每只恐龙都在同一秒内更新了他们的个人资料)。似乎有更好的方法来处理大型数据集。
我想我可能还想观看其他几个事件,我只是想知道是否有一些推荐的方法用于这种套接字管理。
编辑:需要明确的是,我知道房间,但如果我有一个我所在地区附近所有恐龙的滚动列表,我可能只是想接收所有 dinosaur:save
事件.所以我还是不确定。
【问题讨论】:
【参考方案1】:通知一百万件事情是很多数据包,如果您要通知的事情发生很多,那就是很多,而且尝试在屏幕上显示的内容甚至更多。
通常首先要考虑的是:
这些通知的实时性到底有多高?您能否在每个通知周期内将 60 秒或更长时间的通知批处理到每个用户的一个数据包中?
真的每个用户都必须看到其他用户的每一个变化吗?您知道任何用户界面都绝对不可能呈现一百万其他用户的状态。所以,我认为每个用户都不必知道每个其他用户的状态。如果有 1 到 50 个其他用户,则可能,但如果有一百万,则不是。
您能否通过算法确定给定用户可能对哪些用户状态感兴趣并且只向他们广播。例如,您能否仅在地理上靠近他们的其他用户上让他们保持最新状态?
您能否有一个用户界面,让用户告诉您他们想要跟踪哪些其他用户,以便您只更新这些用户?或者他们告诉你的用户和他们在地理上感兴趣的用户的某种组合。关键是你无论如何也看不到一百万用户,所以你将不得不发明一个显示比这少得多的用户界面。
如果客户端还没有其他用户的状态,您始终可以拥有一个 UI,该 UI 将根据需要获取其他用户的状态,因此您不必为每个客户端中的所有百万用户保留该状态(因为它不可能一次全部显示出来)。如果用户浏览查看一些他们还没有的东西,您只需通过套接字或 ajax 调用从服务器获取它。
哦,在您所说的规模上,您可能需要将用户的连接分布在多台服务器之间,因此您也必须处理这种复杂性。
【讨论】:
也许一个开始问题的好地方是:如果我在每个 'dinosaur:save' 上发出一个事件,这甚至是一个问题吗?让我们再次假设有一百万用户。如果我想更新我的恐龙个人资料页面并将该更新实时传播给用户,如何最好地实现?我可以从这个答案中想象,简单地使用我的新恐龙信息发出“恐龙:保存”事件并不是最好的实现。有什么想法吗? @AugieGardner - 我的回答不是已经包含了很多想法吗?你批量更改,你只通知相关用户,你按需获取一些用户的信息而不是实时广播,等等......我觉得你只是掩盖了我已经提出的选项,好像它们不相关。您是否知道在浏览器中保留 1,000,000 条任何内容的实时信息可能只是浪费,并且使您的问题变得比需要的更难,因为没有查看器可以处理那么多信息,如果他们想要搜索或浏览,您可以随时获取相关信息根据服务器的要求。 我想我的问题不在于理论,而在于应用程序,我不确定这是否很清楚。比方说,例如,我有一个恐龙有兴趣在他附近看到恐龙,所以主页上有一个可滚动的他附近的恐龙列表。假设如果此列表中的恐龙改变了它们的位置,我们想要同步,它们可能会。如果不更新每个dinosaur:save
,您能否给出一个具体的示例方法来解决从客户端到服务器的问题?为这个滚动列表创建一个房间?要么...?随意用一个例子修改答案,我可以接受
@AugieGardner - 对于大规模(数以百万计的项目移动)来说,这是一个需要高效完成的复杂问题。假设您想要在您的位置 X 距离内的所有恐龙的客户端中滚动列表。为了让每个客户端的列表保持最新,那么每次恐龙移动时,您都必须弄清楚它的邻居是谁,并通知他们新的位置以及曾经是邻居但不再是邻居的任何人需要被告知他们不再在附近(例如从列表中删除他们)。对数百万用户进行这些距离计算很复杂。
@AugieGardner - 我没有亲自完成过这种类型的距离计算,但我在文章中看到了关于如何比计算与移动的距离更有效地解决这类问题的算法对每一个其他恐龙。如果这就是您需要帮助的具体问题,那么我建议您在一个新问题中描述该特定问题,看看您是否可以吸引一些以前做过类似事情的人。现在你的问题看起来更像是一个关于管理套接字流量的套接字。【参考方案2】:
如果将来有人遇到这种情况,这就是问题和我当前的解决方案。
我们需要实时更新。如果您在某人的个人资料页面上,并且他们对其进行了更新,请显示。如果您正在查看某些满足要求的结果集,例如用户个人资料,并且其中任何一个用户更新了他们的个人资料,请显示出来。如果您在另一个页面上并且某些计数器发生了变化,例如您附近的用户,请显示出来。但是,我们不会同时出现在所有这些页面上,所以在客户端 如果我不在其他页面上,我什至不想知道其他更改。这个问题可能会导致我收到一切的通知,这可能会导致带宽问题,以及一大堆不必要的套接字使用。
所以,我解决问题的方法是使用rooms。我在命名空间上使用房间,因为命名空间通常用于访问同一套接字资源的两个互不相交的应用程序。房间也更适合这个应用程序。
我为每个用户个人资料页面创建了一个动态的动态房间。当访问者打开个人资料页面时,客户端调用socket.emit("joinRoom", modelName + ":" + modelObj._id);
并在服务器上处理socket.on('joinRoom', function(room)
socket.join(room);
);
。如果还没有房间,这会自动创建房间。并将用户添加到其中。 modelName 可以是我们想要的任何东西。这只是我如何划分房间的命名约定。你可以给房间打电话。但重要的部分是最后的._id
。使用 Mongoose,没有两个 DB 对象可以拥有相同的._id
,因此这保证了唯一的房间。
当此个人资料页面的所有者更新他们的信息时,我们会在服务器上调用:
io.sockets.in('Dinosaur:' + doc._id).emit("Dinosaur:" + doc._id + ":updated", doc);
并使用
socket.on(modelName + ":" + modelObj._id + ":updated" , function(msg)
// do something
)
在客户端接收该信息
Viola,我们仅将这些必要信息发送给感兴趣的客户。
--
(一个单独的问题)--
使用这种方法,我们还可以提供与多个用户有关的数据。如果我们有一个用户配置文件的服务结果列表,对于每个配置文件,我们可以将当前用户添加到所有这些服务结果配置文件的房间中。 (所以他们在属于_id
X、_id
Y、_id
Z 等的房间里。
当前用户将在多个房间中,所有房间都反映了这些用户的即时更新,因此也反映了整个餐饮结果列表,无论它可能是什么列表(可能是“附近的恐龙”)。
另一种方法,特别是如果列表更静态,是让套接字每隔 X 秒重新传递结果集,使用相同的套接字,并且只是相同的初始房间。
【讨论】:
以上是关于NodeJS + SocketIO 大套接字事件管理的主要内容,如果未能解决你的问题,请参考以下文章