Ruby on rails ruby异常可以在Thread::handle\u中断块之外异步处理吗?

Ruby on rails ruby异常可以在Thread::handle\u中断块之外异步处理吗?,ruby-on-rails,ruby,multithreading,rabbitmq,Ruby On Rails,Ruby,Multithreading,Rabbitmq,乍一看,我以为新的ruby 2.0Thread.handle\u interrupt会解决我所有的异步中断问题,但除非我弄错了,否则我无法让它做我想做的事情(我的问题在最后和标题中) 从文档中,我可以看到如何避免在某个块中接收中断,而将它们延迟到另一个块。下面是一个示例程序: duration = ARGV.shift.to_i t = Thread.new do Thread.handle_interrupt(RuntimeError => :never) do 5.tim

乍一看,我以为新的ruby 2.0
Thread.handle\u interrupt
会解决我所有的异步中断问题,但除非我弄错了,否则我无法让它做我想做的事情(我的问题在最后和标题中)

从文档中,我可以看到如何避免在某个块中接收中断,而将它们延迟到另一个块。下面是一个示例程序:

duration = ARGV.shift.to_i

t = Thread.new do
  Thread.handle_interrupt(RuntimeError => :never) do
    5.times { putc '-'; sleep 1 }
    Thread.handle_interrupt(RuntimeError => :immediate) do
      begin
        5.times { putc '+'; sleep 1}
      rescue
        puts "received #{$!}"
      end
    end
  end
end

sleep duration
puts "sending"
t.raise "Ka-boom!"

if t.join(20 + duration).nil?
  raise "thread failed to join"
end
当使用参数
2
运行时,它会输出如下内容:

--sending-
--received Ka-boom!
也就是说,主线程在两秒钟后向另一个线程发送
运行时错误
,但该线程在进入内部
线程之前不会处理它。handle\u interrupt

不幸的是,如果我不知道我的线程是在哪里创建的,我看不出这对我有什么帮助,因为我不能将它所做的一切都包装在一个块中。例如,在Rails中,我将如何包装
线程.handle\u interrupt
begin…rescue…end
块?这是否会因运行的Web服务器而异

我所希望的是一种注册处理程序的方法,就像
Kernel.trap
的工作方式一样。也就是说,我想指定与上下文无关的处理代码,它将处理特定类型的所有异常:

register_handler_for(SomeExceptionClass) do 
 ... # handle the exception
end
引发这个问题的是RabbitMQ gem,
bunny
如何使用
thread\raise
向打开
bunny::Session
的线程发送连接级别错误。这些异常可能会在任何地方结束,我只想记录它们,标记连接不可用,然后继续我的方式


想法?

Ruby通过Ruby对象(不要与AMQP队列混淆)实现了这一点。如果Bunny要求您在打开
Bunny::Session
之前创建一个ruby
队列,并将该队列对象传递给它,它将向该队列对象发送连接级别的错误,而不是使用
Thread#raise
将其发送回任何地方,那就太好了。然后,您只需提供自己的
线程
,即可通过队列使用消息

可能值得查看RabbitMQ gem代码内部,看看您是否可以这样做,或者询问该gem的维护人员

在Rails中,除非您可以建立一个服务器范围的线程来使用ruby队列,否则这不太可能起作用,这当然是web服务器特有的。我不知道如何在一个短暂的对象(例如Rails视图的代码)中做到这一点,在Rails视图中,线程被重用,但Bunny不知道(或不在乎)。

我想提出(哈哈!)一个实用的解决方法。这里有龙。我假设您正在构建一个应用程序,而不是一个要重新分发的库,如果不是,那么就不要使用它

您可以修补
Thread#raise
,特别是在会话线程实例上

模块异步异常
@异常队列=queue.new
类错误
错误
结束
AsynchronousExceptions.exception\u queue.push(错误)
结束
结束
session\u thread=thread.current
session_thread.singleton_class.prepend(AsynchronousExceptions)
请记住,异常队列本质上是一个全局队列。我们也为每个人打补丁,而不仅仅是阅读器循环。幸运的是,执行
Thread.raise
,没有什么正当的理由,所以您可以安全地逃脱