2017-08-28 111 views
1

我正在處理物聯網應用程序,客戶端每2秒向服務器發送生物潛在信息。客戶端每2秒發送一個包含400行數據的CSV文件。我的服務器上運行着一個Socket.IO websocket服務器,它從每個客戶端捕獲這些信息。一旦捕獲到這些信息,服務器必須每2秒將這400條記錄推送到一個mysql數據庫中。儘管只要客戶端數量很小,這種方式就可以很好地工作,但隨着客戶端數量的增長,服務器開始拋出「進程內存異常」。NodeJS - 處理內存不足100個併發連接

以下是接收到異常:

<--- Last few GCs ---> 
    98522 ms: Mark-sweep 1397.1 (1457.9) -> 1397.1 (1457.9) MB, 1522.7/0 ms [allocation failure] [GC in old space requested]. 
    100059 ms: Mark-sweep 1397.1 (1457.9) -> 1397.0 (1457.9) MB, 1536.9/0 ms [allocation failure] [GC in old space requested]. 
    101579 ms: Mark-sweep 1397.0 (1457.9) -> 1397.0 (1457.9) MB, 1519.9/0 ms [last resort gc]. 
    103097 ms: Mark-sweep 1397.0 (1457.9) -> 1397.0 (1457.9) MB, 1517.9/0 ms [last resort gc]. 


<--- JS stacktrace ---> 

==== JS stack trace ========================================= 

Security context: 0x35cc9bbb4629 <JS Object> 
    2: format [/xxxx/node_modules/mysql/node_modules/sqlstring/lib/SqlString.js:~73] [pc=0x6991adfdf6f] (this=0x349863632099 <an Object with map 0x209c9c99fbd1>,sql=0x2dca2e10a4c9 <String[84]: Insert into rent_66 (sample_id,sample_time, data_1,data_2,data_3) values ? >,values=0x356da3596b9 <JS Array[1]>,stringifyObjects=0x35cc9bb04251 <false>,timeZone=0x303eff... 

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory 
Aborted 

下面是我的服務器代碼:

var app = require('express')(); 
var http = require('http').Server(app); 
var io = require('socket.io')(http); 
var mysql = require('mysql'); 

var conn = mysql.createConnection({ 
    host: '<host>', 
    user: '<user>', 
    password: '<password>', 
    database: '<db>', 
    debug: false, 
}); 

conn.connect(); 

io.on('connection', function (socket){ 
    console.log('connection'); 
var finalArray = [] 
    socket.on('data_to_save', function (from, msg) { 
    var str_arr = msg.split("\n"); 
    var id = str_arr[1]; 
    var timestamp = str_arr[0]; 
    var data = str_arr.splice(2); 
    finalArray = []; 
    var dataPoint = []; 
    data.forEach(function(value){ 
     dataPoint = value.split(","); 
     if(dataPoint[0]!=''){ 
       finalArray.push([dataPoint[0],1,dataPoint[1],dataPoint[2],dataPoint[3]]); 
       finalArray.push([dataPoint[0],1,dataPoint[4],dataPoint[5],dataPoint[5]]); 
     } 
    }); 
    var sql = "Insert into rent_"+id+" (sample_id,sample_time, channel_1,channel_2,channel_3) values ? "; 
    var query = conn.query (sql, [finalArray],function(err,result){ 
     if(err) 
      console.log(err); 
     else 
     console.log(result); 
    }); 

    conn.commit(); 
    console.log('MSG from ' + str_arr[1] + ' ' + str_arr[0]); 

}); 

}); 
http.listen(9000, function() { 
    console.log('listening on *:9000'); 
}); 

我能得到服務器處理100個併發連接後,我開始接收過程內存不足例外。在引入數據庫插入之前,服務器將簡單地將csv作爲文件存儲在磁盤上。通過該設置,服務器能夠處理1200+個併發連接。

根據互聯網上可用的信息,看起來像數據庫插入查詢(這是異步的)在內存中保存400行數組,直到插入通過。因此,隨着客戶端數量的增長,服務器的內存足跡會增加,從而最終導致內存不足。

我確實經歷了許多有關--max_old_space_size在互聯網上提出的建議,我不確定這是一個長期的解決方案。另外,我不確定我應該在什麼基礎上決定應該在此提及的價值。

另外,我已經經歷了關於異步實用程序模塊的建議。但是,串行插入數據可能會在客戶端插入數據的時間與服務器將此數據保存到數據庫之間引入巨大的延遲。

我已經圍繞這個問題繞了很多次。有沒有辦法服務器可以處理來自1000多個併發客戶端的信息,並將這些數據以最小延遲保存到Mysql數據庫中。我在這裏遇到了一個路障,並且對這個方向的任何幫助都非常感激。

+0

我不知道這是如何進入你的代碼,但在這個引號是不合法的Javascript:'socket.on('data_to_save',...)'。 – jfriend00

+0

我不是MySQL的專家,要麼是一個好的解決方案可能來自更好的配置,或者我會建議去一個更輕量級的DB系統,例如MongoDB。 – Pac0

+0

我首先註釋掉你的數據庫代碼,看看你是否可以處理傳入的消息。如果你能處理得很好,那麼問題出在你的DB代碼上(可能是內存或資源泄漏)。 – jfriend00

回答

1

我會總結我的評論,因爲他們發送了您正確的路徑來解決您的問題。

首先,您必須確定問題是否由您的數據庫引起。最簡單的方法是註釋掉數據庫部分,看看你可以擴展多高。如果您在沒有內存或CPU問題的情況下進入數千臺計算機,那麼您的注意力可能轉移到了解爲什麼將數據庫代碼添加到混合中會導致問題。

假設問題是由數據庫引起的,那麼當需要處理大量活動數據庫請求時,您需要開始瞭解它是如何處理事物的。通常情況下,第一個用於繁忙數據庫的是connection pooling。這給你三個主要的事情,可以幫助規模。

  1. 它使您可以快速重複使用以前打開的連接,因此您不必每個操作都創建自己的連接,然後關閉它。
  2. 它允許您同時指定池中併發數據庫連接的最大數量(控制您在數據庫上的最大負載,也可能限制它將使用的最大內存量)。超出限制的連接將排隊(這通常是您在高負載情況下所需的,因此您不會壓倒所擁有的資源)。
  3. 它可以更容易地查看您是否有連接泄漏問題,而不僅僅是泄漏連接,直到您耗盡某些資源,池中的測試將很快變爲空,您的服務器將無法處理更多事務(所以你更有可能在測試中看到問題)。

然後,您可能還需要查看數據庫連接的事務時間,以瞭解它們可以處理任何給定事務的速度。您知道您嘗試處理的事務數/秒,因此您需要查看數據庫及其配置和資源分配方式(內存,CPU,磁盤速度等)是否能夠滿足您的負載想要拋棄它。