You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测axios请求超时?IPFS最优网关选择方案求助

问题解决与优化方案

一、检测axios请求状态(超时/成功)

首先明确axios的请求逻辑:

  • 成功响应:请求在timeout时限内正常完成,会进入try块的后续代码,此时直接返回该网关URL即可。
  • 超时失败:请求超过设定的timeout,axios会抛出错误,且错误的code属性为ECONNABORTED,可在catch块中识别该类型。
  • 其他错误:网关返回4xx/5xx状态码、网络连接失败等情况,同样会进入catch块,需统一处理重试逻辑。

二、修正你的递归实现代码

原代码存在几个问题:没有返回Promise、可能重复尝试同一网关、无终止条件(极端情况会无限递归)。以下是修正后的版本:

const publicGateway = [
    "https://ipfs.eth.aragon.network/ipfs/",
    "https://ipfs.io/ipfs/",
    "https://cloudflare-ipfs.com/ipfs/",
    "https://ipfs.fleek.co/ipfs/",
    "https://ipfs-infura.io/ipfs/",
    // ... more
]

export const findPublicGateway = async (ipfs) => {
    // 复制网关列表,避免修改原数组
    const availableGateways = [...publicGateway];
    
    const getGateway = async () => {
        // 终止条件:无可用网关时抛出错误
        if (availableGateways.length === 0) {
            throw new Error("所有IPFS网关均无法访问");
        }
        
        // 随机选取并移除网关,避免重复尝试
        const randomIndex = Math.floor(Math.random() * availableGateways.length);
        const gateway = availableGateways.splice(randomIndex, 1)[0];
        const url = gateway + ipfs;

        try {
            // 仅请求文件头部字节,减少数据传输,加速判断
            await axios({
                method: 'get',
                url: url,
                timeout: 2000,
                headers: { 'Range': 'bytes=0-0' }
            });
            // 请求成功,返回有效URL
            return url;
        } catch (error) {
            if (error.code === 'ECONNABORTED') {
                console.log(`网关 ${gateway} 超时,尝试下一个`);
            } else {
                console.log(`网关 ${gateway} 请求失败: ${error.message}`);
            }
            // 递归尝试下一个网关
            return getGateway();
        }
    }

    return getGateway();
}

改进点说明:

  • 改为异步函数,返回Promise,调用方可通过await获取结果。
  • 每次尝试后移除已试过的网关,避免重复无效请求。
  • 添加终止条件,防止无限递归。
  • 使用Range请求头仅获取文件第一个字节,大幅缩短响应判断时间。

三、更优实现方案

针对批量IPFS哈希渲染的场景,推荐以下几种更高效的方案:

1. 并行测试网关(最快响应优先)

逐个尝试网关效率低,可同时向多个网关发起请求,优先使用第一个成功响应的网关:

export const findPublicGatewayParallel = async (ipfs, maxParallel = 3) => {
    const availableGateways = [...publicGateway];
    const promises = [];

    // 同时发起指定数量的并行请求
    for (let i = 0; i < Math.min(maxParallel, availableGateways.length); i++) {
        const randomIndex = Math.floor(Math.random() * availableGateways.length);
        const gateway = availableGateways.splice(randomIndex, 1)[0];
        const url = gateway + ipfs;
        
        const promise = axios({
            method: 'get',
            url: url,
            timeout: 2000,
            headers: { 'Range': 'bytes=0-0' }
        }).then(() => url)
          .catch(() => null); // 失败返回null,不中断Promise.any
        
        promises.push(promise);
    }

    // 等待第一个成功的响应
    try {
        const result = await Promise.any(promises);
        return result;
    } catch {
        // 第一批请求全失败,递归尝试剩余网关
        if (availableGateways.length > 0) {
            return findPublicGatewayParallel(ipfs, maxParallel);
        }
        throw new Error("所有IPFS网关均无法访问");
    }
}

优势:利用并行请求,最快可在2秒内找到可用网关,比逐个尝试效率提升数倍。

2. 网关健康预检测+缓存

定期检测网关可用性,维护健康网关列表,避免每次都尝试失效网关:

// 缓存健康网关列表,定期更新
let healthyGateways = [...publicGateway];

// 每5分钟执行一次健康检测
setInterval(async () => {
    const healthChecks = publicGateway.map(gateway => 
        axios({
            method: 'head',
            url: gateway,
            timeout: 1000
        }).then(() => gateway)
          .catch(() => null)
    );
    const results = await Promise.all(healthChecks);
    healthyGateways = results.filter(g => g !== null);
}, 5 * 60 * 1000);

export const findPublicGatewayCached = async (ipfs) => {
    if (healthyGateways.length === 0) {
        throw new Error("无可用健康IPFS网关");
    }

    const triedGateways = new Set();
    while (triedGateways.size < healthyGateways.length) {
        const randomIndex = Math.floor(Math.random() * healthyGateways.length);
        const gateway = healthyGateways[randomIndex];
        
        if (triedGateways.has(gateway)) continue;
        triedGateways.add(gateway);
        
        const url = gateway + ipfs;
        try {
            await axios({
                method: 'get',
                url: url,
                timeout: 2000,
                headers: { 'Range': 'bytes=0-0' }
            });
            return url;
        } catch (error) {
            console.log(`健康网关 ${gateway} 失效,尝试下一个`);
        }
    }

    throw new Error("所有健康网关均无法访问该IPFS哈希");
}

优势:提前过滤失效网关,减少无效尝试;缓存健康状态,降低重复检测开销。

3. 批量哈希复用网关

如果需要渲染一批IPFS哈希,可复用同一个可用网关,无需每个哈希重新查找:

export const renderBatchHashes = async (ipfsHashes) => {
    // 先找到一个可用网关
    const gateway = await findPublicGatewayParallel(ipfsHashes[0]);
    
    // 用同一网关批量请求哈希
    return Promise.all(ipfsHashes.map(hash => 
        axios.get(gateway + hash)
            .then(res => ({ hash, data: res.data }))
            .catch(async () => {
                // 若该网关对当前哈希失效,单独为其查找新网关
                const newGateway = await findPublicGatewayParallel(hash);
                return axios.get(newGateway + hash).then(res => ({ hash, data: res.data }));
            })
    ));
}

优势:减少网关查找次数,大幅提升批量处理效率。

内容的提问来源于stack exchange,提问作者SnowyField

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:20:27