LoginSignup
1
4

More than 3 years have passed since last update.

(小ネタ)Node.jsのWebアプリでclusterを使いながら定期的に子プロセスを再起動させる

Last updated at Posted at 2020-04-05

Node.jsでサーバサイドWebアプリを開発中、なぜかメモリリークがあるライブラリに遭遇してしまったので、ワークアラウンドとして、定期的にプロセスを再起動させて、メモリリークの問題を緩和したいと思いました。

サーバサイド

http://0.0.0.0:10080 をリスンするプロセスが4つ立ち上がり、5秒未満で子プロセスを停止させ、その後に再起動します。(実用上は、もっと長い時間でプロセスを殺すべきです。)

src/index.js
const cluster = require("cluster");
const http = require("http");

const sleep = time => new Promise(done => setTimeout(done, time));

const clusterCount = 4;
const portNumber = 10080;

if (cluster.isMaster) {
  const spawnProcess = () => {
    // プロセスを終了させるまでの時間: 0 〜 5000 msec
    const ttl = ~~(5000 * Math.random());
    const child = cluster.fork();
    let timeout;

    child.on("listening", () => {
      // 指定時間で終了(Graceful kill)させる
      console.log(`誕生! 死まで ${ttl} msec.`);
      timeout = setTimeout(() => {
        console.log(`死: ${child.id}`);
        child.kill();
      }, ttl);
    });
    child.on("disconnect", () => {
      // 別の理由で死んだ場合はkillをキャンセル
      if (timeout) {
        clearTimeout(timeout);
      }
    });
    child.on("exit", () => {
      // 子プロセスが終了したら代わりのものを1つ起動する
      spawnProcess();
    });
  };

  // 子プロセスを複数起動する
  for (let i = 0; i < clusterCount; i++) {
    spawnProcess();
  }
}
if (cluster.isWorker) {
  // Express や Koa など好きに使いましょう
  http
    .createServer(async (req, res) => {
      // リクエスト終了までやや時間がかかる設定
      await sleep(1000);
      res.writeHead(200);
      res.end("Request done\n");
    })
    .listen(portNumber);
}

起動すると、下記のようなログを吐きながら、プロセスの終了と生成を延々と繰り返します。

誕生! 死まで 2712 msec.
誕生! 死まで 3984 msec.
誕生! 死まで 4297 msec.
誕生! 死まで 1547 msec.
死: 4
誕生! 死まで 4276 msec.
死: 2
:
:

テスト

ab コマンドできちんとリクエストが中断されずにいるか、テストしてみます、

$ ab -c 20 -n 500 http://localhost:10080/
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Finished 500 requests


Server Software:        
Server Hostname:        localhost
Server Port:            10080

Document Path:          /
Document Length:        13 bytes

Concurrency Level:      20
Time taken for tests:   26.226 seconds
Complete requests:      500
Failed requests:        0
Total transferred:      44000 bytes
HTML transferred:       6500 bytes
Requests per second:    19.07 [#/sec] (mean)
Time per request:       1049.029 [ms] (mean)
Time per request:       52.451 [ms] (mean, across all concurrent requests)
Transfer rate:          1.64 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   1.0      0       7
Processing:  1000 1008   5.4   1007    1025
Waiting:     1000 1007   4.7   1006    1023
Total:       1000 1008   5.5   1007    1025

Percentage of the requests served within a certain time (ms)
  50%   1007
  66%   1010
  75%   1012
  80%   1013
  90%   1016
  95%   1019
  98%   1020
  99%   1022
 100%   1025 (longest request)

特に何も問題なくリクエスト処理は完了しているようです。

Complete requests:      500
Failed requests:        0

まとめ

  • cluster でマルチプロセス化できるし、定期的にプロセスを再起動して、健全性を保つことができるはず
    • こういうゴミ掃除はマルチスレッドモデルだとできなさそう
1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4