Node.jsでサーバサイドWebアプリを開発中、なぜかメモリリークがあるライブラリに遭遇してしまったので、ワークアラウンドとして、定期的にプロセスを再起動させて、メモリリークの問題を緩和したいと思いました。
- コード: https://github.com/knjname/2020-04-05_restartClusterChild
- 参考: https://nodejs.org/api/cluster.html
サーバサイド
http://0.0.0.0:10080をリスンするプロセスが4つ立ち上がり、5秒未満で子プロセスを停止させ、その後に再起動します。(実用上は、もっと長い時間でプロセスを殺すべきです。)
src/index.js
constcluster=require("cluster");consthttp=require("http");constsleep=time=>newPromise(done=>setTimeout(done,time));constclusterCount=4;constportNumber=10080;if(cluster.isMaster){constspawnProcess=()=>{// プロセスを終了させるまでの時間: 0 〜 5000 msecconstttl=~~(5000*Math.random());constchild=cluster.fork();lettimeout;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(leti=0;i<clusterCount;i++){spawnProcess();}}if(cluster.isWorker){// Express や Koa など好きに使いましょうhttp.createServer(async(req,res)=>{// リクエスト終了までやや時間がかかる設定awaitsleep(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 でマルチプロセス化できるし、定期的にプロセスを再起動して、健全性を保つことができるはず
- こういうゴミ掃除はマルチスレッドモデルだとできなさそう