如果发生超时错误,我可以自动重新运行方法吗?

时间:2013-10-14 15:59:45

标签: ruby-on-rails ruby

我们有一个应用程序可以对外部服务进行数百次API调用。有时一些电话需要花费太多时间来回应。

我使用rake_timeout gem来查找耗时的过程,因此,只要某个请求花费太长时间来响应,就会抛出Timeout::Error。我正在挽救这个错误并对该方法进行重试:

def new
 @make_one_external_service_call = exteral_api_fetch1(params[:id])
 @make_second_external_call = exteral_api_fetch1(@make_one_external_service_call)

  #Below code will be repeated in every method

 tries = 0
rescue Timeout::Error => e
  tries += 1
  retry if tries <= 3
  logger.error e.message 
end

这使该方法可以完全重新运行它。这非常冗长,我每次都在重复。

有没有办法做到这一点,如果Timeout:Error发生,它会自动重试该方法三次?

4 个答案:

答案 0 :(得分:3)

我有一个小模块:

# in lib/retryable.rb
module Retryable

  # Options:
  # * :tries - Number of tries to perform. Defaults to 1. If you want to retry once you must set tries to 2.
  # * :on - The Exception on which a retry will be performed. Defaults to Exception, which retries on any Exception.
  # * :log - The log level to log the exception. Defaults to nil.
  #
  # If you work with something like ActiveRecord#find_or_create_by_foo, remember to put that call in a uncached { } block. That
  # forces subsequent finds to hit the database again.
  #
  # Example
  # =======
  #   retryable(:tries => 2, :on => OpenURI::HTTPError) do
  #     # your code here
  #   end
  #
  def retryable(options = {}, &block)
    opts = { :tries => 1, :on => Exception }.merge(options)

    retry_exception, retries = opts[:on], opts[:tries]

    begin
      return yield
    rescue retry_exception => e
      logger.send(opts[:log], e.message) if opts[:log]
      retry if (retries -= 1) > 0
    end

    yield
  end

end

而不是你的模特:

extend Retryable

def new
  retryable(:tries => 3, :on => Timeout::Error, :log =>:error) do
    @make_one_external_service_call = exteral_api_fetch1(params[:id])
    @make_second_external_call = exteral_api_fetch1(@make_one_external_service_call)
  end
  ...
end

答案 1 :(得分:2)

你可以这样做:

module Foo
  def self.retryable(options = {})
    retry_times   = options[:times] || 10
    try_exception = options[:on]    || Exception

    yield if block_given?
  rescue *try_exception => e
    retry if (retry_times -= 1) > 0
    raise e
  end
end

Foo.retryable(on: Timeout::Error, times: 5) do
  # your code here
end

您甚至可以将多个例外传递给“catch”:

Foo.retryable(on: [Timeout::Error, StandardError]) do
  # your code here
end

答案 2 :(得分:1)

我认为你需要的是retryable宝石。

使用gem,您可以编写如下方法

def new
  retryable :on => Timeout::Error, :times => 3 do
   @make_one_external_service_call = exteral_api_fetch1(params[:id])
   @make_second_external_call = exteral_api_fetch1(@make_one_external_service_call)
  end
end

请阅读文档以获取有关如何使用gem及其提供的其他选项的更多信息

答案 3 :(得分:0)

你可以为此写一个帮助方法:

class TimeoutHelper
  def call_and_retry(tries=3)
    yield
  rescue Timeout::Error => e
    tries -= 1
    retry if tries > 0
    Rails.logger.error e.message
  end
end

(完全未经测试)并通过

进行调用
TimeoutHelper.call_and_retry { [your code] }