nodejs-tw / ama

Ask me anything!
MIT License
31 stars 1 forks source link

使用nodejs net模組產生的 socket 記憶體不斷上升 #33

Closed iamso1 closed 5 years ago

iamso1 commented 5 years ago

目的

我希望可做一個簡單的socket 應用

使用的工具

node.js 8.9.3 pm2

操作流程

使用pm2執行 socket server端服務 (pm2 start app.js) 並測試三種case:

  1. 只開啟server端
  2. 開啟server端後 開啟client端連接 並送資料
  3. 不斷的開關client端連接

遇到的問題

透過pm2 ls 觀察目前執行緒所佔的資源 發現socket server端的記憶體不斷上升 最終crash重啟

測試case 1 發現在沒有client連接的情況下 記憶體就會一直上升(每分鐘0.5M左右的速度) 測試case 2 發現會加速server端記憶體暴增的速度 測試case 3 發現client連接上時 server記憶體會上升 關掉後server記憶體偶爾會釋放 大多都是繼續維持剛剛佔用的記憶體

嘗試過的解法

看error log 曾發現過maxlistener 提示會造成memory leak的問題 將socket server端監聽事件的程式碼調動後即沒有看到此情況發生 先前是將close之類的listener寫在connect的block裡面 移出來後已解決 雖然已經沒有看到警告訊息 但是記憶體仍會上升

參考了一篇不錯的文章 https://cnodejs.org/topic/58eb5d378cda07442731569f 檢查了其中提到的case 自己應該是沒有發生 且使用文章中的工具查測也沒有看到memory leak的情況

因此想請問一下各位大大是否有什麼方向可建議 謝謝

目前正在嘗試的做法

嘗試重新寫一個最精簡的socket server端程式測試是否會有同樣情況發生

2/28更新

  1. 新增測試專案方便測試討論 測試專案
iamso1 commented 5 years ago

更新: 我嘗試了將所有程式碼都砍掉 只剩下socket端 在收到data時 馬上將data回傳 client送資料的頻率是10秒一次 發現儘管是這樣做記憶體還是會不斷上升(client 1秒一次的效果更明顯)

目前情境client連接到socket後並不會中斷 會一直連著 發現網路上也有人是同樣問題 請問這會是nodejs本身的bug嗎? 是否有人也有遇到同樣情況有解掉的呢 謝謝

const socketServer = net.createServer(function(connection) {
  const connectSrcIp = connection.remoteAddress;
  connection.on('data', function(data) {
    console.log(`[hearbeat] mac: ${Date.now()}`);
    connection.write(data);
  });
  connection.on('close', function() {
    connection.removeAllListeners();
  });
  connection.on('error', function() {
    console.log('socket error, lose connection');
  });

  connection.setTimeout(30000, () => {
    console.log('close redundant  socket');
    connection.end();
  });
});
jimmyolo commented 5 years ago

node version ? 如果想關閉不活躍的連結可用socket.setTimeout 但要記得.end() & destroy()

conn
  .on('timeout', () => {
    disconn(conn);
  })
  .on('error', (err) => {
    disconn(conn, err);
  })

function disconn(socket, err) {
  if(conn.writable) {
    conn.end();
    return;
  }
  conn.destroy(err);
}
iamso1 commented 5 years ago

node version ? 如果想關閉不活躍的連結可用socket.setTimeout 但要記得.end() & destroy()

conn
  .on('timeout', () => {
    disconn(conn);
  })
  .on('error', (err) => {
    disconn(conn, err);
  })

function disconn(socket, err) {
  if(conn.writable) {
    conn.end();
    return;
  }
  conn.destroy(err);
}

victor0801x 您好 我使用的是8.9.3唷 我有使用settimeout去關閉不活躍的socket 不過只有end 沒有destroy 如果沒有destroy 這socket還會存在嗎?

另外 在這次測試的情境中 因為我不會中斷它 會一直連接著 且送資料 所以我在範例就沒有寫我settimeout這段

jimmyolo commented 5 years ago

https://nodejs.org/dist/latest-v11.x/docs/api/net.html#net_socket_end_data_encoding_callback Half-closes the socket. i.e., it sends a FIN packet. It is possible the server will still send some data.

我想這篇的解釋對你應該有幫助 https://stackoverflow.com/a/52927597/3195525 .end()能正常關閉socket的前提是雙方都還正常,適用於graceful terminate .destroy()則強制關閉

function close_socket(socket) {
  if(conn.writable) {
    conn.end();
  }
  conn.destroy();
}
iamso1 commented 5 years ago

@victor0801x 了解 感謝您的回覆

回到這個例子 我確定連線沒有中斷 但是還是會繼續長...

iamso1 commented 5 years ago

寫了一個測試專案方便測試

初步觀察情況 記憶體仍會上升 其中rss會一直上升 但heaptotal 跟 heapused卻保持穩定

jimmyolo commented 5 years ago

我從下午1點跑到11點一直維持在23~25mb 不過我沒用node-schedule跑改用setInterval

"use strict";
const is_gc_enable = process.env.GC ? true : false;

var net = require('net');
const schedule = require('node-schedule');
const moment = require('moment');

var server = net.createServer(function (socket) {
  socket
    .pipe(socket)
    .on('error', function () {
      console.log('socket error, lose connection');
    });
});

server.listen(3001, 'localhost');

print_mem_info();
setInterval(() => {
  if (is_gc_enable) {
    global.gc();
  }
  print_mem_info();
}, 60 * 1000);

function print_mem_info() {
  const memory = process.memoryUsage();
  console.log(
    moment().format('YYYY/MM/DD HH:mm:ss'),
    'rss',
    memory.rss / (1024 * 1024),
    'heapTotal',
    memory.heapTotal / (1024 * 1024),
    'heapUsed',
    memory.heapUsed / (1024 * 1024),
    'external',
    memory.external / (1024 * 1024),
  );
}

手動gc也維持在20~24左右 應該不是node的問題

clonn commented 5 years ago

不確定程式碼是如何運做, 通常 memory leak 發生點如果在自己變數 scope 的問題為最大宗,建議可以重新掃一下 variable 是不是有某個變數一直在增大當中。

另外,如果是 memory leak 的問題,其實開了 gc 也沒有用,gc 的速度會跟不上 memory 使用成長

iamso1 commented 5 years ago

@victor0801x 不好意思, 請問方便詢問您的使用環境嗎? 使用的linux版本 以及 node版本

我後來放了一天多 其中也是會有上升下降的情況 但是最後memory總共上升了8mb

iamso1 commented 5 years ago

不確定程式碼是如何運做, 通常 memory leak 發生點如果在自己變數 scope 的問題為最大宗,建議可以重新掃一下 variable 是不是有某個變數一直在增大當中。

另外,如果是 memory leak 的問題,其實開了 gc 也沒有用,gc 的速度會跟不上 memory 使用成長

@clonn 請問~在自己變數scope的意思是什麼呢? 是否有可以檢查的方式? 我跑了 測試專案 仍會有記憶體上升的問題

jimmyolo commented 5 years ago

另外,如果是 memory leak 的問題,其實開了 gc 也沒有用,gc 的速度會跟不上 memory

是的,而且leak是無法回收的

我只是想說應該不是delay gc造成短期間memory上升

@iamso1 我是用node 10.15.1 on centos 7.6

iamso1 commented 5 years ago

@victor0801x 感謝您的回覆 我後來觀察比較長的時間後(兩天左右) 發現前面雖然會一直上升 但是上升到一定的記憶體後就會趨於穩定 (會上升 但是會自動再回收下降)

iamso1 commented 5 years ago

自己回覆一下發生memory leak的問題: 是因為使用了 node-mariasql 套件 該套件本身存在memory leak問題 因此不是 net模組造成的問題

jimmyolo commented 5 years ago

@iamso1 歡迎改用 MariaDB Node.js connector