2017-08-28 41 views
1

クライアントが生体電位情報を2秒ごとにサーバーに送信するIoTアプリケーションで作業しています。クライアントは、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です。これは、スケールに役立つ3つの主要な事柄を提供します。

  1. 以前に開いた接続をすばやく再利用できるため、1回限りの操作で独自の接続を作成して閉じる必要はありません。
  2. これは同時に(データベースで投げる最大負荷を制御し、おそらくそれが使用するメモリの最大量を制限する)プール内の同時データベース接続の最大数を指定することができます。その制限を超えた接続はキューに入れられます(通常は負荷の高い状況で必要なリソースなので、リソースを圧倒しません)。
  3. 一部のリソースがなくなるまで接続がリークするだけでなく、プールがテスト時にすぐに空になり、サーバーがそれ以上のトランザクションを処理できなくなるような接続リークの問題があるかどうかを確認するのが簡単になります(テストで問題が発生する可能性は非常に高いです)。

次に、データベース接続のトランザクション時間を調べて、特定のトランザクションを処理する速度を確認することもできます。あなたが処理しようとしているトランザクション数/秒を知っているので、あなたのデータベースとそれがどのように構成され、資源化されているか(メモリ、CPU、ディスクの速度など...)それを投げたい

関連する問題