ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Hyperf Retry 组件实战:注解式重试、策略组合与熔断防雪崩指南

Hyperf Retry 组件实战:注解式重试、策略组合与熔断防雪崩指南 后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载导读Hyperf 是高性能的协程框架而网络通信天然不稳定微服务场景下调用失败在所难免。hyperf/retry 组件为 Hyperf 提供了从“注解式重试”到“多策略可插拔组合”的一整套容错方案既能用#[Retry]一行注解完成重试也能通过自定义注解、组合策略次数限制、异常分类、退避间隔、预算限流、超时、熔断、降级适配任意业务场景还能用 Fluent 链式调用编写过程式重试逻辑。读完本文你将掌握 retry 组件的全部配置参数、策略语义以及其底层 AOP 切面与令牌桶预算的实现原理能够在自己的 Hyperf 服务中安全、可控地引入重试机制。为什么重试必须“受控”分布式系统依赖网络通信而网络本质上不稳定因此良好的容错设计必不可少。但无差别重试非常危险当通信出现问题时如果每个请求都被重试一次相当于系统 IO 负载瞬间提升 100%极易触发雪崩Avalanche。同时重试还要考虑错误原因——如果是重试无法解决的问题重试只会浪费资源如果被重试的接口不幂等还可能造成数据不一致等更严重的后果。hyperf/retry 组件正是为解决这些问题而设计它提供一整套丰富的重试机制用策略Policy组合的方式覆盖不同场景的差异化需求并内置预算Budget机制从全局层面限制重试对系统造成的额外负载。安装composer require hyperf/retry安装完成后组件会通过 ConfigProvider 自动注册注解收集器与 AOP 切面无需额外配置即可使用#[Retry]注解。Hello World一行注解开启重试在需要重试的方法上添加#[Retry]注解即可/** * Retry method ketika terjadi exception */ #[Retry] public function foo() { // 发起远程调用 }默认的重试策略足以覆盖日常大多数重试需求并且不会因为过度重试而引发雪崩——这正是默认策略中内置了BudgetRetryPolicy预算策略的原因。从源码实现看#[Retry]的能力由 RetryAnnotationAspect 这个 AOP 切面提供该切面监听AbstractRetry注解见 AbstractRetry.php在切面process()中先把注解上的所有策略实例化并组合成一个HybridRetryPolicy随后进入canRetry → process → beforeRetry → 循环的重试循环源码中以attempt:/end:标签实现 goto 循环直到策略判定不再重试为止。也就是说注解只是声明真正的重试逻辑全部由策略驱动。深入自定义从 0 构建专属重试注解组件通过组合多个重试策略实现可插拔pluggability每个策略只关注重试流程中的一个侧面——是否重试的判断、重试间隔、结果处理等。通过调整注解中使用的策略你可以配置出适配任意场景的重试行为。官方强烈建议按具体业务需求构建自己的“别名注解”。下面演示如何创建一个最大尝试次数为 3的新注解。说明默认的#[Retry]注解本身就可以通过#[Retry(maxAttempts3)]控制最大重试次数这里仅为演示目的假定该参数不存在。第一步继承 AbstractRetry首先创建新的注解类继承\Hyperf\Retry\Annotation\AbstractRetry?php declare(strict_types1); namespace App\Annotation; use Attribute; #[Attribute(Attribute::TARGET_METHOD)] class MyRetry extends \Hyperf\Retry\Annotation\AbstractRetry { }注意源码中注解基类的实际命名空间是Hyperf\Retry\Annotation\AbstractRetry见 AbstractRetry.php。AbstractRetry本身继承自Hyperf\Di\Annotation\AbstractAnnotation其collectMethod()会把注解收集到 DI 的AnnotationCollector中供 AOP 切面在运行时读取。第二步限制重试次数按需覆写$policies属性。要限制重试次数需要用到MaxAttemptsRetryPolicy它有一个参数$maxAttempts最大尝试次数上限?php declare(strict_types1); namespace App\Annotation; use Attribute; use Hyperf\Retry\Policy\MaxAttemptsRetryPolicy; #[Attribute(Attribute::TARGET_METHOD)] class MyRetry extends \Hyperf\Retry\Annotation\AbstractRetry { public $policies [ MaxAttemptsRetryPolicy::class, ]; public $maxAttempts 3; }现在#[MyRetry]会让任何方法最多循环执行 3 次。从 MaxAttemptsRetryPolicy 的实现看start()时把上下文attempt置为 1beforeRetry()每次自增attemptcanRetry()仅在attempt maxAttempts时返回 true否则置retryExhausted并终止重试。第三步加入异常分类策略我们还需要ClassifierRetryPolicy来控制哪类错误才值得重试。加入后默认它只会对抛出的Throwable进行重试?php declare(strict_types1); namespace App\Annotation; use Attribute; use Hyperf\Retry\Policy\ClassifierRetryPolicy; use Hyperf\Retry\Policy\MaxAttemptsRetryPolicy; #[Attribute(Attribute::TARGET_METHOD)] class MyRetry extends \Hyperf\Retry\Annotation\AbstractRetry { public $policies [ MaxAttemptsRetryPolicy::class, ClassifierRetryPolicy::class, ]; public $maxAttempts 3; }继续打磨限定超时异常 可变间隔退避你可以持续细化这个注解直到满足自定义需求。例如只重试用户自定义的TimeoutException并使用可变间隔backoff策略重试前至少睡眠 100 毫秒?php declare(strict_types1); namespace App\Annotation; use Attribute; use Hyperf\Retry\Policy\ClassifierRetryPolicy; use Hyperf\Retry\Policy\MaxAttemptsRetryPolicy; use Hyperf\Retry\Policy\SleepRetryPolicy; #[Attribute(Attribute::TARGET_METHOD)] class MyRetry extends \Hyperf\Retry\Annotation\AbstractRetry { public $policies [ MaxAttemptsRetryPolicy::class, ClassifierRetryPolicy::class, SleepRetryPolicy::class, ]; public $maxAttempts 3; public $base 100; public $strategy \Hyperf\Retry\BackoffStrategy::class; public $retryThrowables [\App\Exception\TimeoutException::class]; }只要该文件能被 Hyperf 扫描到位于app/目录、由注解扫描器加载就可以在方法上使用#[MyRetry]来重试超时错误。关于策略顺序RetryAnnotationAspect::makePolicy()会按$policies数组的顺序依次实例化策略并传入注解的完整参数make($policy, $annotation-toArray())最终组合成HybridRetryPolicy。策略被当作“堆叠的中间件”顺序不同行为可能不同——例如预算策略需要放在次数策略之前才能先做全局限流再判断次数。默认配置详解#[Retry]注解的完整默认属性如下与 Annotation/Retry.php 源码中的构造函数默认值一一对应/** * 重试策略数组可视为堆叠的中间件 * var string[] */ public $policies [ FallbackRetryPolicy::class, ClassifierRetryPolicy::class, BudgetRetryPolicy::class, MaxAttemptsRetryPolicy::class, SleepRetryPolicy::class, ]; /** * 重试间隔算法 */ public string $sleepStrategyClass SleepStrategyInterface::class; /** * 最大尝试次数 */ public int $maxAttempts 10; /** * Retry Budget 重试预算 * ttl: 令牌有效时长秒 * minRetriesPerSec: 基础令牌生成速率每秒最少可重试次数 * percentCanRetry: 按请求量的该比例生成新令牌 * * var array|RetryBudgetInterface */ public $retryBudget [ ttl 10, minRetriesPerSec 1, percentCanRetry 0.2, ]; /** * 每次尝试的基础时间间隔毫秒 * 对 backoff 策略而言是第一次尝试的间隔对 flat 策略而言是每次尝试的间隔 */ public int $base 0; /** * 配置 Predicate谓词判断某个异常是否应被重试 * Predicate 返回 true 表示应重试返回 false 表示不重试 * * var callable|string */ public $retryOnThrowablePredicate ; /** * 配置 Predicate判断某个返回值是否应被重试 * Predicate 返回 true 表示应重试返回 false 表示不重试 * * var callable|string */ public $retryOnResultPredicate ; /** * 配置被记为失败因而需要重试的 Throwable 类列表 * 任何匹配或继承自列表中任一类的 Throwable 都会被重试除非被 ignoreThrowables 忽略。 * 忽略ignore的优先级高于重试retry。 * * var arraystring|\Throwable */ public $retryThrowables [\Throwable::class]; /** * 配置被忽略因而不会重试的错误类列表 * 任何匹配或继承自列表中任一类的异常都不会被重试即使它被 retryThrowables 标记。 * * var arraystring|\Throwable */ public $ignoreThrowables []; /** * 所有尝试耗尽后的 fallback 回调 * * var callable|string */ public $fallback ;几个默认值的要点maxAttempts 10默认最多尝试 10 次首次调用 9 次重试。base 0默认不等待立即重试若需要退避必须显式配置base与strategy。retryBudget三个参数共同决定令牌桶规模在 RetryBudget 中最大令牌数为maxToken (minRetriesPerSec / percentCanRetry) * ttl。按默认值计算即(1 / 0.2) * 10 50个令牌对应每秒约 5 次可重试的额度上限。retryThrowables [\Throwable::class]默认对所有异常一律重试若只想重试特定异常如TimeoutException务必覆写该属性。可选策略详解以下策略均可独立或组合使用是自定义注解时的“积木”。Max Attempts PolicyMaxAttemptsRetryPolicy限制最大尝试次数。参数类型描述maxAttemptsint最大尝试次数实现上首次尝试时attempt记为 1每重试一次自增 1直到attempt maxAttempts时置retryExhausted true并终止MaxAttemptsRetryPolicy.php。Error Classifier PolicyClassifierRetryPolicy通过分类器classifier判断某个错误是否值得重试。参数类型描述ignoreThrowablesarray被忽略的Throwable类名列表优先级高于retryThrowablesretryThrowablesarray需要重试的Throwable类名列表优先级高于retryOnThrowablePredicateretryOnThrowablePredicatecallable通过函数判断某个Throwable是否可重试可重试返回 true否则 falseretryOnResultPredicatecallable通过函数判断某个返回值是否可重试可重试返回 true否则 false从 ClassifierRetryPolicy 的源码看其判定顺序是严格的先检查ignoreThrowables命中即不重试→ 再检查retryThrowables命中即重试→ 最后才调用retryOnThrowablePredicateretryOnResultPredicate仅当未抛出异常lastThrowable为 null且返回值非空时才被调用。这意味着你既可以用类名列表精确圈定异常也可以用闭包编写任意复杂的判定逻辑。Fallback PolicyFallbackRetryPolicy当重试资源耗尽后执行一个替代方法降级兜底。参数类型描述fallbackcallable兜底方法除了is_callable能识别的普通闭包/函数外fallback还支持classmethod格式的字符串框架会从Container容器中取出对应的class再执行其method方法。这种写法在需要注入依赖的降级逻辑中非常实用。Sleep PolicySleepRetryPolicy提供两种重试间隔策略固定间隔FlatStrategy与可变间隔BackoffStrategy。参数类型描述baseint基础睡眠时间毫秒strategystring任意实现Hyperf\Retry\SleepStrategyInterface的类名如Hyperf\Retry\BackoffStrategy从 SleepRetryPolicy 的实现看start()时通过容器按sleepStrategyClass实例化睡眠策略传入basebeforeRetry()在每次重试前调用strategy-sleep()完成等待。FlatStrategy每次固定睡base毫秒BackoffStrategy则以base为第一次间隔、随后递增如base, base*2, base*4...从而在连续失败时自动拉开重试节奏降低对下游的瞬时压力。Timeout PolicyTimeoutRetryPolicy当总执行时间超过指定时长后退出重试会话超时熔断。参数类型描述timeoutfloat超时时间秒注意这是针对整个重试会话多次尝试累计的时间预算而非单次调用的超时。Circuit Breaker PolicyCircuitBreakerRetryPolicy当重试失败并退出重试会话后直接进入“熔断fused”状态一段时间期间不再做任何尝试。参数类型描述circuitBreakerState.resetTimeoutfloat恢复所需时间秒熔断状态由 CircuitBreakerState 维护组件还提供了配套的#[CircuitBreaker]注解见 CircuitBreaker.php及对应测试 CircuitBreakerAnotationAspectTest.php 验证其行为。Budget PolicyBudgetRetryPolicy每个#[Retry]注解会对应生成一个令牌桶token bucket每次被注解的方法被调用就往桶里放入一个带有过期时间ttl的令牌当发生可重试错误时必须先消耗相应数量percentCanRetry的令牌才能执行重试否则不再重试错误继续向下抛出。举个例子当percentCanRetry 0.2时每次重试要消耗 5 个令牌1 / 0.2 5。这样当对端peer崩溃时最多只会带来20%的额外重试消耗对大多数系统而言是可接受的安全水位。为了照顾低频方法调用量少导致令牌不足令牌桶还会每秒生成一批“最低额度”令牌minRetriesPerSec保证系统重试能力的下限稳定。参数类型描述retryBudget.ttlint令牌有效期秒retryBudget.minRetriesPerSecint每秒保证的最低重试次数retryBudget.percentCanRetryfloat重试次数不超过总请求量的百分比其实现细节在 RetryBudget.php 中构造时算出maxToken令牌桶上限init()首次会预生成minRetriesPerSec / percentCanRetry个令牌之后每秒通过协程定时器Timer-tick(1, ...)再补充同数量令牌同时剔除过期令牌与溢出令牌consume()检查桶内令牌数是否大于等于1 / percentCanRetry满足则一次性出队相应数量。对应测试见 RetryBudgetTest.php。重要提醒重试组件的令牌桶不跨 worker 共享每个 worker 进程各自持有一个桶因此实际可发生的总重试次数约为“单桶上限 × worker 数量”在规划容量时需要把 worker 数乘进去。别名注解由于重试注解的配置项相对复杂组件预置了以下别名注解方便日常快速书写#[RetryThrowable]仅重试Throwable等同于默认的#[Retry]。#[RetryFalsy]仅当返回值与 false 宽松相等$result false时重试不重试异常。#[BackoffRetryThrowable]#[RetryThrowable]的可变间隔版本重试间隔至少 100 毫秒。#[BackoffRetryFalsy]#[RetryFalsy]的可变间隔版本重试间隔至少 100 毫秒。这些别名注解的源码分别位于 RetryThrowable.php、RetryFalsy.php、BackoffRetryThrowable.php 与 BackoffRetryFalsy.php并有 RetryFalsyTest.php 等测试用例覆盖其行为。Fluent 链式调用除了注解方式组件还支持用普通 PHP 函数以链式调用的方式使用重试。Retry类通过__callStatic静态代理到 FluentRetry因此以下写法都合法。方式一Retry::with()手动组装策略?php $result \Hyperf\Retry\Retry::with( new \Hyperf\Retry\Policy\ClassifierRetryPolicy(), // 默认重试所有 Throwable new \Hyperf\Retry\Policy\MaxAttemptsRetryPolicy(5) // 最多重试 5 次 )-call(function () { if (rand(1, 100) 20) { return true; } throw new Exception; });方式二语义化 Fluent 链?php $result \Hyperf\Retry\Retry::whenReturns(false) // 当返回 false 时重试 -max(3) // 最多 3 次 -inSeconds(5) // 总耗时最多 5 秒 -sleep(1) // 每次间隔 1 毫秒 -fallback(function () { return true; }) // 兜底函数 -call(function () { if (rand(1, 100) 20) { return true; } return false; });FluentRetry 提供的完整链式方法包括with(...$policies)、when($callable)表达式策略、whenReturns($value)、whenThrows($throwable Throwable::class)、max($times)、inSeconds($seconds)对应TimeoutRetryPolicy、fallback($callable)、sleep($base)固定间隔、backoff($base)退避间隔以及call($callable)。call()内部与注解切面共用同一套HybridRetryPolicy循环逻辑它会构造一个兼容的ProceedingJoinPoint因此过程式调用与注解式调用的重试语义完全一致。若未指定任何策略就调用call()会抛出BadMethodCallException提醒你至少指定一个策略。对应测试见 RetryTest.php 与 RetryAnnotationAspectTest.php。总结如何为你的服务选择重试组合回顾组件提供的全部能力推荐按以下思路落地低频、幂等、可重试的异常直接使用默认#[Retry]内置预算策略已能防雪崩需要精确控制次数与间隔自定义注解组合MaxAttemptsRetryPolicyClassifierRetryPolicySleepRetryPolicy并用retryThrowables限定异常范围、strategy BackoffStrategy开启退避对总时长敏感加入TimeoutRetryPolicy给整个重试会话设置硬性时间上限对下游崩溃敏感加入CircuitBreakerRetryPolicy失败后进入熔断窗口不再空转必须兜底降级配置fallback闭包或classmethod重试耗尽后返回替代结果过程式场景非注解方法、回调内等使用Retry::whenReturns(...)-max(...)-call(...)链式调用语义清晰且与注解行为一致。始终记住重试是容错的工具而非万能药务必同时保证被调用接口的幂等性并结合预算与熔断机制控制对下游的冲击才能让分布式系统在故障面前既“扛得住”又“稳得住”。赞分享后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载相关推荐Hyperf 熔断器Circuit Breaker组件实战指南注解驱动、状态机与降级策略Hyperf 熔断器Circuit Breaker组件实战指南注解驱动、状态机与降级策略 导读 在微服务与分布式系统中一个基础服务不可用往往会导致调用链后端Web框架微服务RPC框架异步编程Hyperf 熔断器Circuit Breaker组件实战指南注解驱动、状态机与降级策略Hyperf 熔断器Circuit Breaker组件实战指南注解驱动、状态机与降级策略 导读 在微服务架构中单个下游服务的故障或超时往往会在高并发下迅后端Web框架微服务RPC框架异步编程Hyperf Retry 组件完全指南基于注解与可插拔策略的高可用重试机制Hyperf Retry 组件完全指南基于注解与可插拔策略的高可用重试机制 重试是分布式系统中抵御网络抖动与瞬时故障的第一道防线但盲目重试反而会放大系统负载后端Web框架微服务RPC框架异步编程上一篇网盘直链下载解决方案技术原理与实践指南下一篇警惕你的数字记忆正在消失构建个人记忆安全防线创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表