文章

🔄 Ruby 异常重试组件:优雅处理失败重试

实现一个优雅的 Ruby 异常重试组件,轻松处理失败重试场景

🔄 Ruby 异常重试组件:优雅处理失败重试

在实际开发中,我们经常会遇到需要重试的场景,比如网络请求、数据库操作等。本文将介绍一个简洁而强大的 Ruby 异常重试组件,帮助你更优雅地处理这些场景。


🎯 功能特点

  • 可配置的重试次数
  • 可自定义重试间隔时间
  • 支持自定义日志记录
  • 灵活的错误处理回调
  • 支持重试前的自定义操作

💎 代码实现

核心方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def retry_times(opt = {})
  # 获取日志记录器(可选)
  logger ||= opt[:logger]

  # 设置重试次数,默认为 2 次
  retry_num = opt[:num] || 2

  # 设置重试间隔时间,默认为 3 秒
  sleep_time = opt[:sleep_time] || 3

  begin
    # 执行传入的代码块
    return yield(opt.merge({num: retry_num}))
  rescue Exception => e
    # 记录异常信息
    logger&.warn "\n#{e.message}\n" + e.backtrace.join("\n")

    if retry_num > 0
      # 还有重试次数,继续尝试
      retry_num -= 1

      # 执行重试前的回调(如果有)
      opt[:retry_block].call if opt[:retry_block]

      # 等待指定时间
      sleep(sleep_time)

      # 重试
      retry
    else
      # 重试次数用完,记录最后的错误
      logger&.warn "\n#{e.message}\n" + e.backtrace.join("\n")

      # 执行错误处理回调(如果有)
      opt[:error_block].call(e) if opt[:error_block]

      # 返回失败结果
      return false
    end
  end
end

🚀 使用示例

基本使用

1
2
3
4
5
# 最简单的使用方式
result = retry_times do
  # 你的业务代码
  api_call()
end

自定义配置

1
2
3
4
5
# 配置重试次数和间隔时间
result = retry_times(num: 3, sleep_time: 5) do
  # 你的业务代码
  database_operation()
end

使用错误回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 定义错误处理回调
error_block = proc { |e|
  warn_messages << "操作失败:#{e.message}"
}

# 使用错误回调
result = retry_times(
  error_block: error_block,
  num: 3,
  sleep_time: 2
) do
  # 你的业务代码
  external_service_call()
end

添加重试前操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 定义重试前的操作
retry_block = proc {
  # 重试前的清理或准备工作
  cleanup_connections()
}

# 完整配置示例
result = retry_times(
  num: 3,
  sleep_time: 5,
  logger: Rails.logger,
  retry_block: retry_block,
  error_block: error_block
) do |opt|
  # 可以在代码块中访问配置选项
  puts "当前剩余重试次数: #{opt[:num]}"
  risky_operation()
end

📝 使用建议

  1. 合理设置重试次数
    • 考虑业务场景的容错需求
    • 避免过多重试导致资源浪费
  2. 适当的重试间隔
    • 对于网络请求,建议较短间隔(1-3秒)
    • 对于资源竞争,可能需要更长间隔
  3. 日志记录
    • 建议在生产环境启用日志记录
    • 记录重试过程有助于问题排查
  4. 错误处理
    • 根据业务需求定制错误处理逻辑
    • 考虑是否需要告警或监控

⚡️ 最佳实践

在 Rails 项目中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# app/lib/retryable.rb
module Retryable
  extend self

  def retry_times(opt = {})
    # 组件代码...
  end
end

# 在业务代码中使用
class UserService
  include Retryable

  def process_data
    retry_times(
      logger: Rails.logger,
      num: 3
    ) do
      # 业务逻辑
    end
  end
end

处理特定异常

1
2
3
4
5
6
7
8
9
10
11
12
13
def retry_on_network_error
  retry_times(
    num: 3,
    error_block: ->(e) { Bugsnag.notify(e) }
  ) do
    # 只捕获网络相关异常
    begin
      api_call()
    rescue Net::HTTPError, Timeout::Error => e
      raise e
    end
  end
end

通过这个重试组件,我们可以更优雅地处理各种需要重试的场景,提高代码的健壮性和可维护性。它特别适合处理网络请求、数据库操作等不稳定的操作,让我们的应用更加可靠。


本文由作者按照 CC BY 4.0 进行授权