2017-10-19 55 views
0

我有一个存储与一些其它的信息,我需要与他们相关联的阵列中的每个插座连接的NodeJS TCP服务器:存储插槽与多进程

var clients = []; 

net.createServer(sslOptions, function (_socket) { 
    _socket.name = 'someName'; 
    _sockect.foo = 'bar'; 
    clients.push(_socket); 
} 

服务器遍历数组客户对于一些商业逻辑的东西来说很常见。有时一个条件触发,我需要断开这些插槽中的一个:

if(condition) { 
    for(let x = 0; x < clients.length; x++) { 
    if(clients[x].name == 'someName') { 
     clients[x].disconnect(); 
     clients.splice(x, 1); 
     return; 
    } 
    } 
} 

有2个东西,我担心这个做法。如果我决定使用PM2 clusters或任何其他Nodejs进程管理器来复制我的应用以实现负载均衡,那么不同进程如何共享同一个阵列客户端?而如果一个进程正在迭代数组而另一个进程正在删除一个索引,会发生什么情况呢?可能会有竞争条件最终导致错误的断开和删除。

当然,解决方案是使用类似于原子DB的东西,但我不知道如何在Mongo或Redis中存储套接字。

回答

1

不同的进程如何共享相同的阵列客户端?

他们没有。

两个node.js进程无法访问相同的数组(它们处于单独的进程和单独的Javascript解释器中),因此在这方面没有直接数据共享或相应争用条件的机会。

如果一个进程正在迭代数组而另一个进程正在删除一个索引,那么会发生什么情况,最终可能会导致错误的断开和删除。

因为Javascript只在一个线程中运行所有Javascript,所以不能同时有多个Javascript块试图修改相同的数组。如果您要迭代数组并进行异步调用,则需要执行一些安全措施,因为阵列可能会在等待异步响应时发生更改,但在您的同步for循环运行时不会更改。

要了解如何让您像使用多个node.js进程一样工作,您可以从socket.io服务器如何支持群集中学到很多东西,因为它有相同的基本问题需要解决。他们希望能够发送到所有连接的套接字(跨所有群集服务器)或任何特定连接(不管该消息来自哪个服务器以及连接的客户端实际连接到哪个服务器)。

对于socket.io,它们基本上使用所有集群进程都可以访问的公共中介存储(在他们的情况下是redis数据存储)。由于redis是为多用户访问而设计的,因此客户可以谨慎使用其API并避免竞争状况。然后,redis存储将用于存储有关每个连接用户的元数据以及服务器进程包含其当前连接的指示符。

要从任何群集服务器进程向特定用户发送消息,请从redis存储中为该用户提取数据。如果用户连接到本地服务器进程(您正在执行查找的进程),那么您可以直接从元数据中获取其ID并在自己的本地已连接客户端列表中查找它们并发送给他们的插座。

如果它们连接到不同的服务器,则向该服务器发送消息,要求它们将消息中继到特定的套接字标识值。当服务器收到消息时,它会在它自己的连接到其进程的客户端列表中查找该ID,获取它们的套接字并向它们发送消息。

当客户端与任何集群进程连接或断开连接时,都会从redis存储库中添加或删除连接。请记住,您不会(也不能)将实际套接字对象存储在redis存储中,因为套接字对象对于特定进程是本地的。您仅将元数据存储在redis存储区和服务器ID中,以便查询Redis存储区的任何人都可以确定哪些用户已连接,以及他们当前连接了哪个服务器。您通常会使用唯一的用户名或其他唯一ID来表示每个用户,并且类似的服务器将由某种可以连接到它们的ID(可能是主机/端口号)表示。

为避免redis商店中的竞争条件,您只需在修改数据时使用良好的多用户数据管理实践和正确的redis API,并避免大多数问题。有关竞争条件的进一步建议将需要更多关于您要修改的内容的具体信息。但是,大多数情况下,您只需在连接时添加一个连接的用户,在断开连接时删除连接的用户,并且在使用正确的API完成后,这些都是原子操作。如果您正在提取用户列表并对其进行操作,那么如果他们在查询他们的时间与尝试实际发送给他们之间断开连接,您将会遇到潜在的竞争条件。为此,如果在您尝试发送消息时恰好消失,您只需准备好处理错误。

每个单独的node.js进程都会维护自己的连接到自己进程的套接字数组。由于node.js将JS作为单线程运行,并且该数组不与任何其他进程或线程共享,因此如果您的代码编写正确,那么在访问或维护该数组时不存在竞争条件的机会。

一个解决方案当然是使用类似于原子数据库的东西,但我不知道如何在Mongo或Redis中存储套接字。

您不在这些数据库中存储套接字。您存储套接字标识值和服务器标识值。服务器ID值(可以是主机/端口字符串)可用于连接到具有该套接字ID的连接的正确服务器。接收方服务器可以使用套接字标识在自己的数组中查找实际套接字对象以获取自己的连接。

0

我想你必须考虑用Socket.io Redis来代替。 它们将客户端存储在RedisAdapter中。您可以通过以下方式获取客户ID列表:

io.of('/').adapter.clients((err, clients) => { 
    console.log(clients); // an array containing all connected socket ids 
}); 

io.of('/').adapter.clients(['room1', 'room2'], (err, clients) => { 
    console.log(clients); 
}); 

// you can also use 

io.in('room3').clients((err, clients) => { 
    console.log(clients); // an array containing socket ids in 'room3' 
});