重试
借助 Effect 的重试策略增强韧性,用可自定义的重试策略与回退机制稳健地处理临时性失败。
在软件开发中,经常会遇到某次操作因网络问题、资源不可用或外部依赖等各种因素而临时失败的情况。此时,通常希望自动重试该操作,让它最终能够成功。
重试是处理临时性失败、确保关键操作成功执行的强大机制。在 Effect 中,借助内置函数与调度策略,重试变得简单而灵活。
在本指南中,我们将探讨 Effect 中重试的概念,并学习如何使用 retry 和 retryOrElse 函数来处理失败场景。我们还会看到如何使用 Schedule 定义重试策略,由它决定何时重试以及重试多少次。
无论你处理的是网络请求、数据库交互,还是其他任何容易出错的操作,掌握 Effect 的重试能力都能显著提升应用程序的韧性与可靠性。
retry
Effect.retry 函数接收一个 effect 和一个 Schedule 策略,并在该 effect 失败时按照策略的规则自动重试。
如果该 effect 最终成功,就会返回其结果。
如果重试次数用尽而该 effect 仍然失败,则会把该失败传播出去。
这在处理间歇性失败时很有用,例如网络问题或临时的资源不可用。通过定义重试策略,你可以控制重试次数、重试之间的延迟,以及何时停止重试。
示例(以固定延迟重试)
import { Effect, Schedule } from "effect"
let count = 0
// Simulates an effect with possible failures
const task = Effect.async<string, Error>((resume) => {
if (count <= 2) {
count++
console.log("failure")
resume(Effect.fail(new Error()))
} else {
console.log("success")
resume(Effect.succeed("yay!"))
}
})
// Define a repetition policy using a fixed delay between retries
const policy = Schedule.fixed("100 millis")
const repeated = Effect.retry(task, policy)
Effect.runPromise(repeated).then(console.log)
/*
Output:
failure
failure
failure
success
yay!
*/
立即重试 n 次
你也可以用一个更简单的、立即重试的策略,让失败的 effect 重试固定次数:
示例(最多重试任务 5 次)
import { Effect } from "effect"
let count = 0
// Simulates an effect with possible failures
const task = Effect.async<string, Error>((resume) => {
if (count <= 2) {
count++
console.log("failure")
resume(Effect.fail(new Error()))
} else {
console.log("success")
resume(Effect.succeed("yay!"))
}
})
// Retry the task up to 5 times
Effect.runPromise(Effect.retry(task, { times: 5 }))
/*
Output:
failure
failure
failure
success
*/
基于条件重试
你可以通过指定条件来自定义重试的管理方式。使用 until 或 while 选项来控制何时停止重试。
示例(重试直到满足特定条件)
import { Effect } from "effect"
let count = 0
// Define an effect that simulates varying error on each invocation
const action = Effect.failSync(() => {
console.log(`Action called ${++count} time(s)`)
return `Error ${count}`
})
// Retry the action until a specific condition is met
const program = Effect.retry(action, {
until: (err) => err === "Error 3",
})
Effect.runPromiseExit(program).then(console.log)
/*
Output:
Action called 1 time(s)
Action called 2 time(s)
Action called 3 time(s)
{
_id: 'Exit',
_tag: 'Failure',
cause: { _id: 'Cause', _tag: 'Fail', failure: 'Error 3' }
}
*/
如果你的重试条件基于成功的结果而不是错误,也可以使用 Effect.repeat。
retryOrElse
Effect.retryOrElse 函数会按照一个确定的 Schedule 策略,尝试对失败的 effect 重试多次。
如果重试次数用尽而该 effect 仍然失败,它就会改为运行一个回退 effect。
当你想在反复失败之后指定一个替代操作,从而优雅地处理失败时,这个函数很有用。
示例(带回退的重试)
import { Effect, Schedule, Console } from "effect"
let count = 0
// Simulates an effect with possible failures
const task = Effect.async<string, Error>((resume) => {
if (count <= 2) {
count++
console.log("failure")
resume(Effect.fail(new Error()))
} else {
console.log("success")
resume(Effect.succeed("yay!"))
}
})
// Retry the task with a delay between retries and a maximum of 2 retries
const policy = Schedule.addDelay(Schedule.recurs(2), () => "100 millis")
// If all retries fail, run the fallback effect
const repeated = Effect.retryOrElse(
task,
policy,
// fallback
() => Console.log("orElse").pipe(Effect.as("default value")),
)
Effect.runPromise(repeated).then(console.log)
/*
Output:
failure
failure
failure
orElse
default value
*/