web3 WebSocket连接阻止节点进程退出

zi8p0yeb  于 2022-11-11  发布在  其他
关注(0)|答案(3)|浏览(175)

我有一个node js进程,它创建了一个web 3 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()
})

看起来web 3打开了一个套接字,这是有道理的,因为我从来没有告诉它关闭连接。通过查看文档和谷歌搜索,它看起来不像有一个关闭或结束方法的web 3对象。
手动关闭上述stop中的套接字,可以成功退出进程:

web3.currentProvider.connection.close()

有人有更优雅或官方认可的解决方案吗?我觉得很有趣的是,你必须手动完成这项工作,而不是让对象在进程结束时自行销毁。其他客户端似乎会自动完成这项工作,而不会明确告诉他们关闭连接。也许告诉所有由节点进程创建的客户端在关闭时关闭句柄/连接会更干净,但对我来说,这是意料之外。

aiazj4mn

aiazj4mn1#

我觉得很有趣的是,你必须手动完成这一点,而不是让对象在进程结束时销毁自己
这感觉很有趣,因为与异步编程相比,您可能接触过更多的同步编程。

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/
How to exit in Node.js
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()

csbfibhn

csbfibhn2#

由于EIP-1193impending release of Web3 1.0.0的实现,JavaScript web 3模块的提供者API最近经历了一些实质性的变化。
根据代码,web3.currentProvider.disconnect()看起来应该可以工作。该方法还接受可选的codereason参数,如the MDN reference docs for WebSocket.close(...)中所述。

**重要提示:你会注意到我引用了上面的源代码而不是文档。这是因为目前disconnect方法还不被认为是公共API的一部分。如果你在代码中使用它,你应该确保为它添加一个测试用例,因为它随时都可能崩溃!**从我所看到的来看,WebSocketProvider.disconnect是在web3@1.0.0-beta.38中引入的,并且在今天的最新发行版web3@1.0.0-beta.55中仍然存在。我不认为从现在到web3@1.0.0之间会有太大的变化,但是当涉及到内部API的结构时,没有什么限制。

我已经和目前的维护者Samuel Furter,也就是GitHub上的nividia详细讨论过将内部提供者公开的问题。我不完全同意他将其保留在内部的决定,但他是目前唯一的维护者,他的手 * 非常 * 忙着稳定1.0分支上的长期工作。
作为这些讨论的结果,我现在的意见是,那些需要一个稳定的API为他们的WebSocket提供者的人应该写一个他们自己的EIP-1193兼容的提供者,并将其发布在NPM上以供其他人使用。请永远遵循这一点,并在您自己的公共API中包含一个类似的disconnect方法。如果您用TypeScript编写它,将获得加分。因为这使您能够显式地将类成员声明为publicprotectedprivate
如果您这样做,请注意EIP-1193仍处于草案状态,因此您需要密切关注EthereumMagiciansProvider Ring Discord上的EIP-1193讨论,以了解可能发生的任何更改。

2lpgd968

2lpgd9683#

在node js进程结束时,只需调用:

web3.currentProvider.connection.close()

相关问题