【发布时间】:2013-10-29 18:40:05
【问题描述】:
据我所知,由于 Node.js 中事件循环的性质,在请求已被移交给回调函数后,无法轻松阻止请求的处理。
由于每个事件回调不绑定到任何其他特定的流程或事件,一旦回调被添加到事件循环的队列或当前正在处理,即使 request.on('timeout ') 处理程序被触发。
如果请求超时,有什么方法可以停止处理请求?我能想到的部分解决这个问题的唯一方法是在请求期间执行的每个异步回调的顶部添加一个检查,以查看请求是否超时,如果超时,则立即返回并停止连续的回调。
但是,如果已经有一个从超时请求发出的未完成的异步调用正在处理中,这并不能解决问题。在这种情况下,除非您破坏与正在执行异步处理的任何操作(例如执行 MySQL 查询)的连接,否则它将继续并最终调用与其关联的异步回调。
这在 PHP + Apache 等其他编程范例中不是问题,因为 apache 会为每个传入的请求创建一个新的 PHP 进程,因此如果请求超时,该进程最终会被终止并停止整个执行流程。
有没有人遇到过这个问题并找到了现有的库或自定义方法来轻松解决这个问题?
编辑:添加特定示例:
现在我们有一个内置的 API 节点处理传入请求以跟踪分析,将请求数据发布到兔子队列,然后返回响应。如果请求进来并且由于某种原因请求需要超过 30 秒的时间来处理,它将超时并且将向客户端发送错误响应。此时,客户端将尝试再次拨打相同的电话(这样我们就不会丢失分析数据)。
但是,由于请求本身与发布到 rabbit 的调用之间没有任何联系(除了启动发布的请求是该请求),因此无法停止发布该数据。因此,如果 rabbit 服务器最终完成了对发布请求的处理,现在将会出现重复数据,因为客户端现在已经发出了 2 个(或更多)请求。
阻止初始请求完成发布的唯一方法是在请求超时时销毁rabbit连接。这样,初始发布就会停止,并且来自客户端的重试请求不会重复它。但是,对于在整个请求中进行的任何异步调用,都需要对连接进行相同类型的手动销毁。如果在超时发生时我有 5 个未完成的异步调用,我将需要销毁所有 5 个异步连接(无论它们是与 rabbit、mysql、mongo 等的连接)。
但是,问题不仅仅在于请求超时时已经建立的连接。即使请求在发布到rabbit之前超时,被调用来处理该请求的函数已经执行并且不会停止,除非在各个位置进行一些检查以检查请求是否超时并返回马上。
【问题讨论】:
-
您可以将rabbit连接存储在一个带有id(可以是客户端ip或cookie值或其他)的全局对象上,您可以在下一个请求时检索此连接。您还可以将兔子连接存储在套接字本身并监听“关闭”,并在发生这种情况时终止兔子连接。杀死意味着调用“关闭”或设置一个标志,以便在您自己的代码中保释。选择第一个选项,您可以通过某个 id 将多个 socket/http 连接汇聚到单个服务器“连接”。
-
@DDS 现在我将兔子连接附加到请求中,然后在超时时关闭连接。但是,我希望有一个更通用的解决方案,包括停止处理整个请求流的能力。现在我想不出一个好方法,除非我将完全与节点相反的东西引入节点(为每个传入请求创建一个新进程)或冗余/冗长(在每个异步调用之前添加一个检查以查看是否请求已超时)。这两种方法似乎都不是很直观。
-
如果你不使用node 0.11.x,你仍然可以使用生成器(ES6),但你必须将它编译成vanilla JS(ES5),你可以使用traceur。