在实际开发中,我们经常会遇到需要重试的场景,比如网络请求、数据库操作等。本文将介绍一个简洁而强大的 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-3秒)
- 对于资源竞争,可能需要更长间隔
- 日志记录
- 建议在生产环境启用日志记录
- 记录重试过程有助于问题排查
- 错误处理
- 根据业务需求定制错误处理逻辑
- 考虑是否需要告警或监控
⚡️ 最佳实践
在 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
|
通过这个重试组件,我们可以更优雅地处理各种需要重试的场景,提高代码的健壮性和可维护性。它特别适合处理网络请求、数据库操作等不稳定的操作,让我们的应用更加可靠。