2017-10-19 57 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' 
});