Node.js fetch循环处理26个URL过慢 Vercel部署触发504超时如何解决
问题根因分析
- 初始代码性能问题:你在循环中使用
await逐个发起请求,所有请求为串行执行,26个请求的总耗时等于所有单个请求耗时的叠加,只要单个请求平均耗时超过200ms,总耗时就会超过5秒,属于逻辑实现问题,不是Node.js fetch的固有特性。 - 并发后仍超时的原因:Vercel免费版Serverless函数冷启动本身有数百ms延迟,加上公网请求第三方站点的网络波动,只要有2-3个站点的响应超过2秒,整体并发耗时就会触达5秒的免费版上限。
可行解决方案
- 方案1:并发请求+单请求超时控制+失败降级
使用Promise.allSettled批量处理并发请求,给每个fetch设置最大超时时间(如800ms),超时的站点直接跳过,不阻塞整体返回。 - 方案2:调整Vercel函数超时限制
若你是Vercel付费用户,可在项目根目录的vercel.json中配置函数最大执行时长,最高可设为30秒,配置示例:{ "functions": { "api/*.js": { "maxDuration": 10 } } } - 方案3:前端拆分批量请求
免费版用户可让前端将26个站点拆分为3-4组,分组调用接口,每次接口仅处理一组站点的请求,前端收到所有分组结果后自行合并,单次接口耗时可稳定控制在2秒以内。 - 方案4:增加缓存策略
如果目标站点的/items.json数据更新频率较低,可以用Vercel KV或者Edge Cache缓存已经拉取过的站点数据,缓存有效期内的请求直接返回缓存结果,耗时可降低到100ms以内。
优化后代码示例(带超时的并发实现)
// 封装带超时的fetch const fetchWithTimeout = (url, timeout = 800) => { return Promise.race([ fetch(url), new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), timeout) ) ]) } export default async (req, res) => { if (req.method !== 'POST') { return res.status(405).end() } try { const body = JSON.parse(req.body) const sites = body.list || [] const siteLists = [] // 生成所有请求的Promise数组 const requestPromises = sites.map(async (site, index) => { if (!site?.url) return null try { const jurl = `${site.url}/items.json` const fUrl = await fetchWithTimeout(jurl) const siteData = await fUrl.json() if (!siteData?.items?.length) return null const items = siteData.items.map(item => ({ url: site.url, img: item.images[0].url, title: item.title })) return { id: index, name: site.name || "", label: site.label || "", items: items } } catch (err) { return null } }) // 批量处理所有请求结果 const results = await Promise.allSettled(requestPromises) results.forEach(result => { if (result.status === 'fulfilled' && result.value) { siteLists.push(result.value) } }) return res.send({ sites: siteLists }) } catch (err) { return res.status(500).send({ error: 'Internal error' }) } }
内容的提问来源于stack exchange,提问作者Nathan Bernard
相关产品推荐
相关产品推荐

