可以在Thread :: handle_interrupt块之外异步处理ruby异常吗?

Kev*_*hey 10 ruby multithreading ruby-on-rails rabbitmq

乍一看,我认为新的ruby 2.0 Thread.handle_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
Run Code Online (Sandbox Code Playgroud)

当使用参数运行时,2它会输出如下内容:

--sending-
--received Ka-boom!
Run Code Online (Sandbox Code Playgroud)

也就是说,主线程RuntimeError在两秒后发送给另一个线程,但该线程在进入内部Thread.handle_interrupt块之前不会处理它.

不幸的是,如果我不知道我的线程在哪里被创建,我看不出这对我有什么帮助,因为我无法将它所做的一切都包装在一个块中.例如,在Rails中,我将包围Thread.handle_interruptbegin...rescue...end阻塞什么?根据Web服务器的运行情况,这不会有所不同吗?

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

register_handler_for(SomeExceptionClass) do 
 ... # handle the exception
end
Run Code Online (Sandbox Code Playgroud)

促成这个问题的是RabbitMQ gem如何bunny将连接级错误发送给打开Bunny::Session使用的线程Thread#raise.这些异常可以在任何地方结束,我想要做的就是记录它们,标记连接不可用,并继续我的路.

想法?

Tom*_*Tom 1

Ruby 通过 ruby​​ 对象提供此功能Queue(不要与 AMQP 队列混淆)。Queue如果 Bunny 要求您在打开 之前创建一个 ruby Bunny::Session​​,并且您将 Queue 对象传递给它,那么它将向该对象发送连接级错误,而不是使用Thread#raise将其发送回任何地方,那就太好了。然后,您可以简单地提供自己的Thread消息来通过队列消费消息。

可能值得查看 RabbitMQ gem 代码,看看是否可以做到这一点,或者询问该 gem 的维护者。

在 Rails 中,这不太可能起作用,除非您可以建立一个服务器范围的线程来从 ruby​​ 队列中消费,这当然是特定于 Web 服务器的。我不明白如何在短期对象中执行此操作,例如 Rails 视图的代码,其中线程被重用,但 Bunny 不知道(或关心)。