我有一个节点js进程,它创建了一个web3 websocket连接,如下所示:
web3 = new Web3('ws://localhost:7545')
当进程完成时(我发送一个SIGTERM),它不会退出,而是永远挂起而没有控制台输出。
我在SIGINT和SIGTERM上注册了一个监听器,通过process._getActiveRequests()
和process._getActiveHandles()
来观察流程处理的处理方式,我看到了:
Socket {
connecting: false,
_hadError: false,
_handle:
TCP {
reading: true,
owner: [Circular],
onread: [Function: onread],
onconnection: null,
writeQueueSize: 0 },
<snip>
_peername: { address: '127.0.0.1', family: 'IPv4', port: 7545 },
<snip>
}
为了完整性,以下是监听信号的代码:
async function stop() {
console.log('Shutting down...')
if (process.env.DEBUG) console.log(process._getActiveHandles())
process.exit(0)
}
process.on('SIGTERM', async () => {
console.log('Received SIGTERM')
await stop()
})
process.on('SIGINT', async () => {
console.log('Received SIGINT')
await stop()
})
看起来web3正在打开一个套接字,这是有道理的,因为我从未告诉它关闭连接。浏览文档和谷歌搜索,它看起来不像是web3对象的close或end方法。
手动关闭上面stop
的套接字可以让进程成功退出:
web3.currentProvider.connection.close()
任何人都有更优雅或官方认可的解决方案?我觉得有必要手动执行此操作,而不是让对象在进程结束时自行销毁。其他客户端似乎自动执行此操作而未明确告知他们关闭其连接。也许告诉你的节点进程创建的所有客户端在关机时关闭他们的句柄/连接是否更清楚,但对我来说,这是出乎意料的。
答案 0 :(得分:1)
在节点js流程结束时,只需调用:
web3.currentProvider.connection.close()
答案 1 :(得分:1)
让我感到很有趣的是,您必须手动执行此操作,而不是让对象在进程结束时自行销毁
这很有趣,因为与异步相比,您可能已经接触了更多的同步编程。考虑下面的代码
fs = require('fs')
data = fs.readFileSync('file.txt', 'utf-8');
console.log("Read data", data)
在上方奔跑时,您会得到输出
$ node sync.js
Read data Hello World
这是一个同步代码。现在考虑相同版本的异步版本
fs = require('fs')
data = fs.readFile('file.txt', 'utf-8', function(err, data) {
console.log("Got data back from file", data)
});
console.log("Read data", data);
运行时,您将获得以下输出
$ node async.js
Read data undefined
Got data back from file Hello World
现在,如果您认为自己是同步程序员,则该程序应该在最后一个console.log("Read data", data);
处结束,但是您得到的是随后打印的另一条语句。现在感觉好笑吗?让我们在流程中添加退出语句
fs = require('fs')
data = fs.readFile('file.txt', 'utf-8', function(err, data) {
console.log("Got data back from file", data)
});
console.log("Read data", data);
process.exit(0)
现在,当您运行该程序时,它将在最后一条语句处结束。
$ node async.js
Read data undefined
但是实际上没有读取该文件。为什么?因为您从未为JavaScript引擎花时间执行挂起的回调。理想情况下,当没有工作要做时(没有挂起的回调,函数调用等),该过程会自动完成。这就是异步世界的工作方式。有一些不错的SO线程和文章值得您关注
https://medium.freecodecamp.org/walking-inside-nodejs-event-loop-85caeca391a9
https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
Why doesn't my Node.js process terminate once all listeners have been removed?
How does a node.js process know when to stop?
因此在异步世界中,您需要告诉进程退出,或者在没有待处理任务(您知道如何检查-process._getActiveRequests()
和process._getActiveHandles()
时,它将自动退出)
答案 2 :(得分:0)
由于EIP-1193和impending release of Web3 1.0.0的实现,JavaScript web3模块的提供者API最近进行了一些重大更改。
每the code,看来web3.currentProvider.disconnect()
应该可以工作。如the MDN reference docs for WebSocket.close(...)
中所述,此方法还接受可选的code
和reason
参数。
重要提示:您会注意到我引用了上面的源代码,而不是文档。这是因为目前disconnect
方法不被认为是公共API的一部分。如果在代码中使用它,则应确保为其添加一个测试用例,因为它可能随时中断!据我所知,WebSocketProvider.disconnect
是在{{1}中引入的},并在今天的最新版本web3@1.0.0-beta.38
中仍然存在。鉴于稳定的1.0.0版本即将发布,我认为从现在到web3@1.0.0-beta.55
之间这种变化不太可能发生,但是内部API的结构没有任何限制
我已经与GitHub的当前维护者Samuel Furter(又名nividia)公开讨论了内部提供商的详细信息。我不同意他决定在内部保留它的决定,但是在他的辩护中,他是目前唯一的维护者,他非常全力以赴地稳定正在进行中的长期工作。 web3@1.0.0
分支。
作为这些讨论的结果,我目前的观点是,那些需要为WebSocket提供程序提供稳定API的人应该编写自己的EIP-1193兼容提供程序,并将其发布在NPM上以供其他人使用。为此,请遵循semver,并在您自己的公共API中包含类似的1.0
方法。如果您使用disconnect
编写奖励积分,则可以将类成员显式声明为TypeScript
,public
或protected
。
如果执行此操作,请注意EIP-1193仍处于草稿状态,因此您需要密切注意EthereumMagicians和Provider Ring Discord上的EIP-1193讨论,随时应对可能发生的任何变化。