如何设置超时函数防止Heroku返回H13错误代码
问题根因
你之前判断的「请求超时导致H13」是完全错误的,直接看崩溃日志的核心报错即可定位根因:
Error: Failed to launch the browser process!
chrome: error while loading shared libraries: libnss3.so: cannot open shared object file: No such file or directory
opensea-scraper 底层依赖Puppeteer(无头浏览器)实现爬取逻辑,Puppeteer自带的Chromium运行需要多个系统底层动态链接库,Heroku默认的Node.js运行环境是裁剪过的轻量镜像,没有预装这些依赖,所以接口调用触发Puppeteer启动浏览器时,进程直接因缺库崩溃,没有返回任何响应才触发H13错误。整个崩溃过程实际只用了2.5秒(日志里标记service=2510ms),和30秒超时规则没有任何关系,你看到的10秒左右崩溃是本地计时误差,你提到的「每3秒轮询保活」的方案是解决真实请求超时的,对当前缺库崩溃的问题完全无效。
另外你现有代码还存在异步逻辑bug:forEach 不会等待内部async函数执行,会直接提前返回响应,导致返回的数据不完整,这个bug不会触发崩溃,但会导致接口返回结果异常。
修复方案
按以下步骤操作即可解决问题:
- 安装Puppeteer运行所需系统依赖
进入Heroku应用后台的构建包(Buildpacks)配置页,添加Puppeteer官方适配的Heroku构建包,将其排序在Node.js默认构建包之前,重新部署应用时构建包会自动安装所有Chromium运行需要的系统库,无需手动逐个安装依赖。 - 配置Puppeteer启动参数适配Heroku环境
Heroku环境的权限限制不允许Chromium默认的沙箱启动模式,调用OpenseaScraper.rankings()时需要传入适配的启动参数,否则即使装完依赖也会启动失败:const ranking = await OpenseaScraper.rankings({ puppeteer: { args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage' ] } }) - 修复接口异步逻辑bug
把原来的forEach遍历改成for...of循环,避免异步逻辑未执行完就提前返回响应。数据库存库逻辑不需要阻塞接口返回,可以放在后台异步执行,接口拿到爬取结果后直接返回即可,修复后的接口代码参考:router.get('/', async (req, res) => { try { const ranking = await OpenseaScraper.rankings({ puppeteer: { args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'] } }) const NFTs = [] for (const item of ranking) { const floorPrice = item.floorPrice?.amount || 0 const nftItem = { slug: item.slug, name: item.name, logoUrl: item.logo, floorPrice } NFTs.push(nftItem) // 查库存库逻辑异步执行,不阻塞接口返回 NFT.findOne({ slug: item.slug }, (err, existRecord) => { if (err) return console.error('查库失败:', err) if (!existRecord) new NFT(nftItem).save().catch(e => console.error('存库失败:', e)) }) } res.json(NFTs) } catch(err) { console.error('接口运行报错:', err) res.status(500).json({ msg: '服务内部错误' }) } }) - 可选优化
OpenSea排名数据更新频率很低,不需要每次接口请求都实时启动浏览器爬取,可以加个定时任务,每10~15分钟爬一次数据写入MongoDB,接口请求直接读库返回,响应速度可以降到几十毫秒,也能避免Puppeteer偶发启动失败导致接口不可用。
内容的提问来源于stack exchange,提问作者Daniel Brannon
相关产品推荐
相关产品推荐

