0

据我所知,由于 Node.js 中事件循环的性质,在请求已经移交给回调函数后,无法轻松地阻止它被处理。

由于每个事件回调与任何其他特定流程或事件无关,因此一旦将回调添加到事件循环的队列或当前正在处理,即使 request.on('timeout') 处理程序也会继续处理它被触发。

如果超时,有什么方法可以停止处理请求?我能想到的部分解决这个问题的唯一方法是在请求期间执行的每个异步回调的顶部添加一个检查,以查看请求是否超时,如果超时,则立即返回并停止连续的回调。

但是,如果已经有一个从超时请求发出的未完成的异步调用正在处理中,这并不能解决问题。在这种情况下,除非您破坏与正在执行异步处理的任何操作(例如执行 MySQL 查询)的连接,否则它将继续并最终调用与其关联的异步回调。

这在 PHP + Apache 等其他编程范例中不是问题,因为 apache 会为每个传入请求创建一个新的 PHP 进程,因此如果请求超时,该进程最终会被终止并停止整个执行流程。

有没有人遇到这个问题并找到一个现有的库或自定义方法来轻松解决这个问题?

编辑:添加一个具体的例子:

现在我们有一个内置的 API 节点处理传入的请求以跟踪分析,将请求数据发布到兔子队列,然后返回响应。如果请求进来并且由于某种原因请求需要超过 30 秒的时间来处理,它将超时并且将向客户端发送错误响应。此时,客户端将尝试再次拨打相同的电话(因此我们不会丢失分析)。

但是,由于请求本身与发布到 rabbit 的调用之间没有任何联系(除非是该请求启动了发布),因此无法停止发布该数据。因此,如果 rabbit 服务器最终完成了对发布请求的处理,现在将会有重复的数据,因为客户端现在已经发出了 2 个(或更多)请求。

阻止初始请求完成发布的唯一方法是在请求超时时破坏兔子连接。这样,初始发布就会停止,并且来自客户端的重试请求不会重复它。但是,对于在整个请求中进行的任何异步调用,都需要执行这种相同类型的手动销毁连接。如果在超时发生时我有 5 个未完成的异步调用,我将需要销毁所有 5 个异步连接(无论它们是与 rabbit、mysql、mongo 等的连接)。

但是,问题超出了请求超时时已经建立的连接。即使请求在发布到rabbit之前超时,被调用来处理该请求的函数已经执行并且不会停止,除非在各个位置进行一些检查以检查请求是否超时并返回立即地。

4

1 回答 1

1

对于更具体的问题,您可能会得到更好的答案。像“一个 mysql 查询”这样的技术细节比“一个请求”这样的模糊的非技术术语更容易讨论。

我能想到的部分解决这个问题的唯一方法是在请求期间执行的每个异步回调的顶部添加一个检查,以查看请求是否超时,如果超时,则立即返回并停止连续的回调

你现在不需要,也永远不需要 node.js 中的代码。没有人会这样做,因为这不是问题。即使是这样,在堆栈中解决比在应用程序中更低的问题也是有意义的。

这在 PHP + Apache 等其他编程范例中不是问题,因为 apache 会为每个传入的请求创建一个新的 PHP 进程,因此如果请求超时,该进程最终会被终止并停止整个执行流程。

这在 node.js 中甚至不是问题,因为对于传入的 HTTP 请求,甚至没有新进程甚至新线程。发生的结果本质上是:

  • 客户端连接并发送 GET 请求
  • 操作系统接收连接
  • 节点收到 GET 请求
  • 假设此时客户端进程(浏览器)在客户端机器上突然被杀死
  • 确切的结果有几种情况。如果在浏览器所在的房子里断电,则没有任何东西通过电线,在其他情况下,客户端我会发送数据包以提前关闭 TCP 连接。
  • 但这并不重要在节点侧。你处理请求,进行数据库查询,渲染 HTML,最坏的情况是一旦工作完成,没人关心,但这只是一个 GET 请求。您的应用程序应该每秒执行许多此类操作。将完成该请求的处理视为“问题”是错误的。这不是问题,也没有任何我听说过的可以用任何编程语言实际完成的事情。服务器实际上无法区分一个正在正确等待响应的客户端与 200 毫秒前断电的客户端。HTTP 没有“取消”方法。事实上,完成整个 HTTP 请求可能在语义上更正确。例如,如果 Web 浏览器向您发送信用卡购买的完整 HTTP POST,但在您响应之前断开连接,该事务应该完成。在启动后中止它是不正确的。
于 2013-10-29T18:56:35.217 回答