Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

Nodeで外部のwebサーバーを建ててkillするまで

Goで書いたwebサーバーを、Nodeでテストをしています(地獄)。テストする上で、Nodeのchild_processモジュールが孫プロセスをうまくkillできずに躓きました。そこで、孫プロセスをゴリ押しでkillする方法を調べて実装してみました。

孫プロセスのPIDを取得する

孫プロセスのPIDさえ取得できれば、killすることはとてもカンタンにできます。イメージはこんな感じです。

process.kill(mago_pid); // PIDがmago_pidのプロセスをkill

孫プロセスのPIDの取得は、親子共々殺したい - Qiitaを参考に書いてみます。

const child_process = require('child_process');
const server = child_process.spawn('go', ['run', `server.go`]);
// サーバーのPIDはserver.pidで取得できる
child_process.exec(`ps --ppid=${server.pid} | cut -d' ' -f1`, (error, stdout, stderr) => {
    if (error)
        throw new Error(error);
    // 孫プロセスのPID値を取得できた!
    console.log(stdout, stderr);
});

孫プロセスをkillする

あとは取得したPIDを使ってkillすれば完成です。

const child_process = require('child_process');
const server = child_process.spawn('go', ['run', `server.go`]);

(async () => {
    // サーバーが立ち上がるまで待つ
    await new Promise(resolve => {
        server.stdout.on('data', data => {
            if (data.toString() === 'Start server\nListening port 12345...\n')
                resolve();
        });
    });
    console.log('server started');

    // サーバーのPIDはserver.pidで取得できる
    const {stdout, stderr} = await new Promise((resolve, reject) => {
        child_process.exec(`ps --ppid=${server.pid}`, (error, stdout, stderr) => {
            if (error)
                reject(error);
            resolve({stdout, stderr});
        })
    });
    for (const line of stdout.split('\n').slice(1)) {
        const pid = line.trim().split(' ')[0];
        if (pid === '')
            continue;
        process.kill(pid, 'SIGINT');
    }
    console.log('finish server');
})();

番外編: util.promisifyを使ってもっと見やすく書く

util.promisifyはべんりなのでドンドン使いましょう

const util = require('util');
const child_process = require('child_process'), exec = util.promisify(child_process.exec).bind(util);
const server = child_process.spawn('go', ['run', `server.go`]);

(async () => {
    // サーバーが立ち上がるまで待つ
    await new Promise(resolve => {
        server.stdout.on('data', data => {
            if (data.toString() === 'Start server\nListening port 12345...\n')
                resolve();
        });
    });
    console.log('server started');

    // サーバーのPIDはserver.pidで取得できる
    const {stdout, stderr} = await exec(`ps --ppid=${server.pid}`);

    for (const line of stdout.split('\n').slice(1)) {
        const pid = line.trim().split(' ')[0];
        if (pid === '')
            continue;
        process.kill(pid, 'SIGINT');
    }
    console.log('finish server');
})();