Node.js中的process.exit()不会在使用createReadStream时退出。

8
我有一个通过EAGI与Asterisk通信的程序。 Asterisk打开我的Node.js应用程序,并通过STDIN向其发送数据,程序通过STDOUT向Asterisk发送命令。当用户挂断电话时,Node.js进程会收到SIGHUP命令。为了更好地退出,这个功能已经可以运作。
Asterisk还在fd 3(STDERR+1)上发送原始音频数据。Node.js进程正确拦截数据,并能够读取音频、转换音频或执行其他操作。然而,当在fd 3上创建createReadStream时,Node.js进程不会退出,并迅速变成僵尸进程。如果我注释掉createReadStream代码,Node.js将按预期退出。
如何让Node.js使用process.exit()函数正常退出?我正在使用Node.js版本v0.10.30。
Node.js createReadStream代码:
// It was success
this.audioInStream = fs.createReadStream( null, { 'fd' : 3 } );

// Pipe the audio stream to a blackhole for now so it doesn't get queued up
this.audioInStream.pipe( blackhole() );

SIGHUP代码:

process
.on( 'SIGHUP', function() {
    log.message.info( "[%s] Asterisk process hung up.", that.callerid );
    that.exitWhenReady();
} );

退出WhenReady函数
Index.prototype.exitWhenReady = function() {
    if( !this.canExit )
        return;

    log.message.info( "[%s] Exiting program successfully.", this.callerid );

    // Get rid of our streams
    this.audioInStream.unpipe();
    this.audioInStream.close();
    this.audioInStream.destroy();

    process.exit( 0 );
};

黑洞模块:
var inherits = require( 'util' ).inherits;
var Writable = require( 'stream' ).Writable;
var process = require( 'process' );

function Blackhole( opts ) {
    if( !(this instanceof Blackhole) )
        return( new Blackhole( opts ) );

    if( !opts )
        opts = {};

    Writable.call( this, opts );
}

inherits( Blackhole, Writable );

Blackhole.prototype._write = function( chunk, encoding, done ) {
    process.nextTick( done );
};

module.exports = Blackhole;

值得注意的是,当使用createReadStream读取fd 3时,Asterisk进程挂断和成功退出程序的日志不会显示在日志文件中,但是在其它情况下,这些日志会被记录。

1
我认为这是根本问题:https://github.com/nodejs/node-v0.x-archive/issues/7101 尽管已经关闭,但问题并没有真正解决。在node中,从tty读取的整个过程都很混乱,读/写会阻塞,而它们应该是异步的,在简单的fd上有不同的流/套接字类.... - Johannes Rudolph
2个回答

7
我发现挂钩SIGHUP并打开fd 3会导致程序即使调用process.exit()也无法关闭。这真的很奇怪。
我解决这个问题的方法是监听进程的“exit”事件。在“退出”事件中,我使用SIGTERM手动杀死了自己的进程。这足以停止整个程序。我发现这实际上与Winston记录器异常记录器非常配合。Winston能够将异常写入日志文件,然后成功退出。
结果代码:
process
.on( 'SIGHUP', function() {
    log.message.info( "[%s] Asterisk process hung up.", that.callerid );
    that.exitWhenReady( true );
} )
.on( 'exit', function() {
    process.kill( process.pid, 'SIGTERM' );
} );

上述函数基本上在收到SIGHUP信号时调用exitWhenReady()。它检查所有任务是否完成,一旦所有任务完成,它就会调用"process.exit()",该函数调用上面事件的函数。

希望这能帮助某些人。


1
谢谢!在处理 SIGINT 时,我遇到了类似的情况,process.exit 没有起作用。使用 SIGTERM 就可以解决问题了。 - Amiram Korach
1
我正在捕获SIGTERM信号,而process.exit()无法工作,因为我有一个输入流从/dev/input/event3读取。发送另一个SIGTERM没有帮助,但在on-exit块中使用SIGKILL解决了问题。(尽管最后,我决定在处理完捕获SIGTERM的清理工作后,在on-SIGTERM块中直接执行SIGKILL更加干净。)真是疯狂,居然需要这样做。 - njlarsson

0
在寻找答案时遇到了这个问题。对于“强制退出”,我并不是很满意,因为它会停止其他进程完成他们的工作。
经过一些广泛的试验和错误,我发现了以下方法。不要使用createReadStream(),而是创建一个套接字:
const audio = new net.Socket({ fd: 3, readable: true });

其次,在SIGHUP上创建一个事件监听器,以销毁您的stdin和音频流:

process.addListener("SIGHUP", () => {
  process.stdin.destroy();
  audio.destroy();
});

这样做应该可以让 Node.js 干净地退出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接