
后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载从 Midway v3.5.0 开始框架内置了方法级自定义重试能力通过retryWithAsync异步与retryWith同步两个工具函数把多次调用 try/catch的样板代码收敛为一行包裹逻辑。本文以官方文档《重试机制》为主线结合midwayjs/core包的真实源码与单元测试系统讲解重试的触发语义、this绑定、重试间隔、异常处理等完整用法读完即可在自己的服务类中落地一套健壮的重试方案。为什么需要重试机制在实际业务中远程调用、异步 IO、第三方接口这类容易失败的方法我们常常需要反复尝试多次并配合try/catch捕获每一次的错误。最常见的写法是这样的// 定义了一个异步函数 async function invoke(id) { // 一些远程调用逻辑 } async invokeNew() { let error; try { return await invoke(1); } catch(err) { error err; } try { return await invoke(2); } catch(err) { error err; } if (error) { // .... } }可以看到每增加一次尝试就要多写一层try/catch代码重复且冗长可读性也随重试次数的增加急剧下降。Midway 提供的重试工具正是为了解决这一痛点把执行 → 捕获错误 → 再次执行 → 直到成功或达到上限的循环逻辑封装在框架内部业务代码只保留一次包裹与一次异常兜底。定义重试函数retryWithAsync 基本用法使用retryWithAsync包裹一个异步函数即可为它加上重试能力。它从midwayjs/core统一导出import { retryWithAsync } from midwayjs/core; async function invoke(id) { // ... } async function someServiceMethod() { // 默认调用加上重试两次最多执行三次 const invokeNew retryWithAsync(invoke, 2); try { return await invokeNew(1); } catch(err) { // err } }包裹后的方法具备以下关键特性参数与返回值完全一致retryWithAsync返回的新函数签名与原函数保持相同源码中通过ParametersT与ReturnTypeT泛型约束保证类型完整调用方无需感知任何差异重试周期内成功即返回只要某一次调用成功、未抛出错误就会立刻把成功的返回值返回给调用方不再继续尝试全部失败则抛出异常当所有尝试都以失败告终时默认抛出框架内置的MidwayRetryExceededMaxTimesError异常。类场景下的 this 绑定receiver 参数在类如 Midway 的 Service中使用重试包裹时需要注意this的指向问题。因为传入的retryFn通常是类实例方法直接包裹会丢失正确的this上下文。从 Midway v3.5.1 起retryWithAsync增加了receiver参数专门用于解决两个问题方法调用时正确的this指向包裹后新方法定义的正确性保持与原方法一致的签名类型。示例import { retryWithAsync } from midwayjs/core; export class UserService { async getUserData(userId: string) { // wrap const getUserDataOrigin retryWithAsync( this.getUserDataFromRemote, 2, { receiver: this, // 此参数用于处理 this 指向 } ); // invoke return getUserDataOrigin(userId); } async getUserDataFromRemote(userId: string) { // get data from remote } }如果没有receiver参数则需要手工bind才能把this绑定到实例上同时还要对包裹结果做一次类型断言写法明显繁琐// wrap const getUserDataOrigin retryWithAsync( this.getUserDataFromRemote.bind(this) as typeof this.getUserDataFromRemote, 2, { receiver: this, } );从源码看receiver的绑定实现在 packages/core/src/util/retry.ts每次调用都会执行retryFn.bind(options.receiver || this)(...args)即优先绑定传入的receiver未传时回退到包裹函数定义时的this。重试次数的语义额外次数 默认调用retryWithAsync的第二个参数用于声明额外重试的次数默认值为 1即默认调用失败后再多试一次总计最多执行 2 次。注意这个值指代的是在默认调用之后额外重试的次数。例如传入2意味着默认调用 重试两次总计最多执行 3 次。这一点可以从 retry 源码 的do...while循环结构中精确印证循环先执行一次try调用再判断defaultRetry-- 0因此实际执行次数恒等于retryTimes 1。也就是说传入的 retryTimes实际最多执行次数语义01不重试仅默认调用1默认值2默认调用 额外重试 1 次23默认调用 额外重试 2 次nn 1默认调用 额外重试 n 次同步重试retryWith与retryWithAsync对应框架还提供了同步版本retryWith用于包裹普通非 async函数。它的参数结构与retryWithAsync几乎相同retryFn、retryTimes、options唯一区别是返回值是同步结果不需要awaitoptions不包含retryInterval同步调用无法等待间隔详见下文。import { retryWith } from midwayjs/core; function query(id: string) { // 同步逻辑可能抛出异常 } const queryWithRetry retryWith(query, 2, { receiver: this, }); try { const result queryWithRetry(1); } catch (err) { // 处理错误 }同步版本的内部实现同样位于 packages/core/src/util/retry.ts逻辑与异步版完全对称只是不包含sleep间隔等待。重试延迟retryInterval 避免高频重试频繁、无间隔的重试会给下游服务数据库、第三方 API 等造成不必要的压力。为此retryWithAsync提供了retryInterval选项用于指定每次失败后、下次重试前的等待时间毫秒const invokeNew retryWithAsync(invoke, 2, { retryInterval: 2000, // 执行失败后2s 后继续重试 });关于该参数有两个实现层面的细节值得注意见 retry 源码等待动作发生在本次调用抛出错误之后、进入下一次循环之前即每次失败都会完整等待一个retryInterval判断条件是options.retryInterval 0只有显式传入非负数值才会触发sleep默认undefined时该条件为false因此默认行为是无间隔连续重试。如果希望失败后立即重试但让出事件循环可以传入0。底层等待借助了框架的sleep工具函数实现packages/core/src/util/index.ts它基于setTimeout返回一个 Promise并支持通过AbortController提前终止。:::tip同步方法retryWith没有retryInterval属性因为同步调用无法异步等待请不要在同步场景下依赖间隔重试。:::抛出的错误MidwayRetryExceededMaxTimesError 与原始错误默认行为抛出框架异常默认情况下如果超过最大重试次数仍失败框架会抛出MidwayRetryExceededMaxTimesError。它是框架内置的标准错误定义于 packages/core/src/error/framework.ts继承自MidwayError基类具备以下属性可供捕获后处理name固定为MidwayRetryExceededMaxTimesErrorMidwayError基类会把name设置为构造函数名见 packages/core/src/error/base.tscode对应FrameworkErrorEnum.RETRY_OVER_MAX_TIME数值10017注册后为MIDWAY_10017见 framework.tscause携带最后一次失败的原始异常对象可以从中取出具体的错误详情message格式为Invoke 方法名 retries exceeded the maximum number of times(次数), error: 原始错误信息。import { retryWithAsync, MidwayRetryExceededMaxTimesError } from midwayjs/core; async function invoke(id) { // ... } async function someServiceMethod() { // 默认调用加上重试两次最多执行三次 const invokeNew retryWithAsync(invoke, 2); try { return await invokeNew(1); } catch(err) { // err.name MidwayRetryExceededMaxTimesError // err.cause instanceof CustomError true } } async invokeNew() { throw new CustomError(customError); }由于MidwayRetryExceededMaxTimesError是标准的MidwayError子类你可以在 Midway 的错误过滤器中统一捕获处理返回统一的错误响应在业务层直接读取err.cause拿到最后一次原始异常做精细化判断如区分网络超时与业务异常。抛出原始错误throwOriginError如果希望跳过框架异常的包装直接把最后一次失败的原始 error 对象抛给调用方可以通过配置参数开启const invokeNew retryWithAsync(invoke, 2, { throwOriginError: true, });开启后最终抛出的是原始异常本身MidwayRetryExceededMaxTimesError的包装逻辑将被跳过对应 retry 源码 中的分支判断。这在需要保持下游异常类型一致性例如让既有的异常拦截逻辑按原类型识别错误时非常有用。源码视角重试的完整执行链路综合 packages/core/src/util/retry.ts 的实现一次带重试的异步调用完整链路如下调用retryWithAsync(retryFn, retryTimes, options)返回一个新的包装函数类型签名与原函数一致包装函数被调用时进入do...while循环先执行一次retryFn.bind(options.receiver || this)(...args)若成功立即return结果循环终止若抛错暂存到error变量若设置了非负retryInterval则await sleep(retryInterval)等待判断defaultRetry-- 0决定是否进入下一轮尝试循环结束后仍未成功按throwOriginError决定抛出原始异常还是MidwayRetryExceededMaxTimesError后者会带上方法名、重试次数和cause原始错误。两个工具函数都通过packages/core/src/index.ts统一导出见 index.ts与MidwayRetryExceededMaxTimesError一起作为midwayjs/core的公开 API 使用。测试验证重试行为的可预期性仓库为重试能力提供了完整的单元测试 packages/core/test/util/retry.test.ts覆盖了异步与同步两个维度包括成功路径一个前两次抛错、第三次成功的方法用retryWithAsync(fn, 2, { receiver })包裹后能正常拿到返回值测试中返回hello harry失败路径超出重试次数后rejects.toThrowError(MidwayRetryExceededMaxTimesError)原始错误模式throwOriginError: true时直接抛出原始Error匿名函数兜底匿名函数全部失败时异常信息中方法名显示为anonymous对应源码retryFn.name || anonymous间隔重试retryInterval: 1000且重试两次时断言整个流程耗时不低于约 2 秒验证了等待逻辑真实生效this 绑定receiver传入实例对象后方法内部可正确访问实例属性如this.initData。这些用例直接印证了本文前述的所有语义总执行次数为retryTimes 1、间隔在每次失败后生效、异常类型与cause行为一致。小结Midway 的retryWithAsync/retryWith把易失败方法的重试逻辑收敛为一次包裹调用配合receiverthis 绑定、retryInterval重试间隔、throwOriginError异常透传三个选项足以覆盖服务类方法、匿名函数、同步/异步等绝大多数重试场景。无论是处理远程调用的瞬时故障还是为第三方接口调用增加韧性这套内置能力都能让业务代码保持简洁同时把重试失败的最终异常交给框架统一的错误处理链路去兜底。赞分享后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载相关推荐Midway 重试机制完全指南用 retryWithAsync / retryWith 告别手写 try-catch 重试代码Midway 重试机制完全指南用 retryWithAsync / retryWith 告别手写 try catch 重试代码 重试是处理远程调用、异步任务等后端微服务云原生Midway 重试机制retryWithAsync / retryWith完整指南方法级重试、this 绑定与异常处理Midway 重试机制retryWithAsync / retryWith完整指南方法级重试、this 绑定与异常处理 Midway 自 v3.5.0 起后端微服务云原生终极指南OpenAPI-Specification重试机制详解——API调用失败的完美解决方案终极指南OpenAPI Specification重试机制详解——API调用失败的完美解决方案 在当今的API开发中处理调用失败是确保系统稳定性的关键环节。API设计文档后端上一篇如何用PrivateGPT快速提升财务报表分析效率审计工作者的AI助手终极指南下一篇告别手动切换Dracula Theme自动日夜模式配置指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考