【问题标题】:web3 websocket connection prevents node process from exitingweb3 websocket连接阻止节点进程退出
【发布时间】:2018-11-10 22:16:29
【问题描述】:

我有一个创建 web3 websocket 连接的节点 js 进程,如下所示:

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中手动关闭socket可以让进程成功退出:

web3.currentProvider.connection.close()

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

【问题讨论】:

  • When the process completes 是什么意思?您的进程是一个监听端口的 HTTP 服务器。它怎么知道它应该停止倾听?
  • 抱歉,我不清楚这个过程是如何完成的。我正在向节点进程发送终止信号。 express 和 mongo 似乎会在进程退出时自动关闭句柄,但 web3 不会。
  • 它在什么系统上运行?您的进程是否有子进程?
  • debian 杰西。没有孩子
  • Debian 在 Docker 容器中运行,但我认为这无关紧要。

标签: javascript websocket ethereum solidity web3


【解决方案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/

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()

【讨论】:

  • 我想我不清楚自己的期望。我更新了这个问题,希望它更容易理解。让我觉得有趣的不是事件循环是如何工作的,而是 web3 客户端没有监听进程退出和清理的事实。
  • @AndyPang,那件事,你的期望是 22 号陷阱。只有在没有任何待处理的情况下,你的进程才会退出。 websocket 是一个连续开放的渠道,应该保持开放。在 NodeJS 中,事情不会自行关闭,如果您需要进程退出,那么您需要使用process.exit(0)。如果它是一个 http 请求,那么它会自行完成,但它是一个套接字,它应该保持打开状态,直到被告知关闭
  • 我不认为你理解这个问题。 web3 是一个客户端库,它打开一个 websocket 连接来完成它的工作。我是那个图书馆的消费者。我的期望不是让 websocket 自行关闭。它用于创建和管理 websocket 连接的 web3 库自行清理。
  • @AndyPang,可以显示你用过的web3代码的其余部分,我想更好地理解
  • 当然,你可以在这里找到它:github.com/ethereum/web3.js。我在里面找不到任何连接清理代码,但我没有花太多时间去挖掘。
【解决方案2】:

由于EIP-1193impending release of Web3 1.0.0 的实现,JavaScript web3 模块的提供程序API 最近发生了一些重大变化。

根据the code,看起来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。鉴于稳定的 1.0.0 版本很快就会发布,我认为从现在到web3@1.0.0 之间这可能不会有太大变化,但在内部 API 的结构方面没有任何限制。

我已经与当前的维护者 Samuel Furter(又名 GitHub 上的 nividia)详细讨论了将内部提供程序公开。我不完全同意他将其保留在内部的决定,但在他的辩护中,他是目前唯一的维护者,他非常忙于稳定长期正在进行的工作1.0 分支。

作为这些讨论的结果,我目前的观点是,那些需要为其 WebSocket 提供者提供稳定 API 的人应该编写自己的 EIP-1193 兼容提供者,并将其发布在 NPM 上供其他人使用。请遵循 semver,并在您自己的公共 API 中包含类似的 disconnect 方法。如果您将其写入 TypeScript,则可以加分,因为这使您能够将类成员显式声明为 publicprotectedprivate

如果您这样做,请注意 EIP-1193 仍处于草案状态,因此您需要密切关注 EthereumMagiciansProvider Ring Discord 上的 EIP-1193 讨论以保持领先地位可能发生的任何变化。

【讨论】:

  • 感谢有关此未记录的说明。另请注意,如果您使用 IpcProvider web3@^1.2.2 则没有 disconnect() 方法
【解决方案3】:

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

web3.currentProvider.connection.close()

【讨论】:

    猜你喜欢
    • 2015-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-04
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多