前言
在日常的开发过程中,由于接口的不稳定性,又或者是网络抖动导致的单次接口请求失败,需要前端通过重试策略,来解决系统不可用的问题。
接下来我们一起探索前端最佳的重试策略。
基础实现
对于基础扎实的前端同学来说,可以很容易地想到如下实现:
/**
* 重试方法
* @param {Function} fn 异步函数
* @param {number} times 重试次数
* @param {number} timeout 重试等待时间
* @returns
*/
function retry (fn, times, timeout) {
return new Promise((resolve, reject) => {
const attemp = () => {
fn().then(resolve).catch(err => {
if (times === 0) {
return reject(err)
}
times--;
setTimeout(attemp, timeout);
});
}
attemp();
});
}
retry(fetchData, 3, 1000);
上述代码基于 Promise 机制,通过拦截异步请求的 reject 状态,完成异步请求失败的重试操作。
整体的实现逻辑是没啥问题的,但是一个优秀的重试机制需要考虑如何设置一个适当的重试等待时间。
指数退避算法
指数退避算法正是解决如何设置适当的重试等待时间的算法,它的处理流程如下:
- 客户端发起网络请求。
- 如果请求失败,等待 1 + random_number_milliseconds 秒之后再重试请求。
- 如果请求失败,等待 2 + random_number_milliseconds 秒之后再重试请求。
- 如果请求失败,等待 4 + random_number_milliseconds 秒之后再重试请求。
- 依此类推,等待时间的上限为 maximum_backoff。
在指数退避算法中重试等待时间为:
Math.min((2 ** n + random_number_milliseconds), maximum_backoff)
这里的 random_number_milliseconds 是小于或等于 1000 的毫秒数(随机值)。这有助于避免出现以下情况:许多客户端同步进行处理并同时执行重试操作,导致同步发送每一波请求。每次重试请求后,系统都会重新计算 random_number_milliseconds 值。
/**
* 生成重试等待时间
* @param {number} times 重试次数
* @param {number} maximum_backoff 最大等待秒数
* @returns
*/
function createTimeout(times, maximum_backoff) {
const random_number_milliseconds = Math.floor(Math.random() * 1000);
return Math.min(Math.pow(2, times) * 1000 + random_number_milliseconds, maximum_backoff);
}
根据算法描述,可以实现上述函数来生成重试等待时间。
接下来,就可以在进行网络请求之前生成重试等待时间列表,以便后续重试使用。
const maximum_backoff = 64 * 1000;
function retry (fn, times) {
const operationTimeout = [];
for (let i = 0; i < times; i++) {
operationTimeout.push(createTimeout(i, maximum_backoff));
}
return new Promise((resolve, reject) => {
const attemp = () => {
fn().then(resolve).catch(err => {
if (times === 0) {
return reject(err)
}
times--;
setTimeout(attemp, operationTimeout.shift());
});
}
attemp();
});
}
达到 maximum_backoff 时间后可以选择继续重试,但是如果请求系统处于长时间不可用的状态,客户端重试再多次都是没有意义的,所以可以设置一个 deadline 时间上限,达到上限之后不再重试。
const maximum_backoff = 64 * 1000;
function retry (fn, times, deadline) {
const operationTimeout = [];
for (let i = 0; i < times; i++) {
if (deadline < 0) {
break;
}
const timeout = createTimeout(i, maximum_backoff);
deadline -= timeout;
operationTimeout.push(timeout);
}
times = operationTimeout.length;
return new Promise((resolve, reject) => {
const attemp = () => {
fn().then(resolve).catch(err => {
if (times === 0) {
return reject(err)
}
times--;
setTimeout(attemp, operationTimeout.shift());
});
}
attemp();
});
}
总结
以上就是本文的全部内容,希望能够给你带来帮助,欢迎「关注」、「点赞」、「转发」。
参考文档:https://cloud.google.com/storage/docs/retry-strategy?hl=zh-cn
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!