如何处理JavaScript中Selenium崩溃?捕获异常后程序仍终止
Selenium长时间运行后崩溃问题处理方案
问题现象
Selenium服务长时间运行正常后突然崩溃,报错Process unexpectedly closed with status 0。已用catch块捕获Promise异常并打印错误,但程序仍会终止。此外还出现过不可复现的MaxListenersExceededWarning内存泄漏警告,重新运行相同输入时不会触发。
崩溃报错信息
A critical error was thrown while processing https://www.testtesttest.com/norwegian-english/snakk. Error was WebDriverError: Process unexpectedly closed with status 0 /opt/narjetas/node_modules/selenium-webdriver/lib/error.js:524 let err = new ctor(data.message) ^ WebDriverError: Process unexpectedly closed with status 0 at Object.throwDecodedError (/opt/narjetas/node_modules/selenium-webdriver/lib/error.js:524:15) at parseHttpResponse (/opt/narjetas/node_modules/selenium-webdriver/lib/http.js:587:13) at Executor.execute (/opt/narjetas/node_modules/selenium-webdriver/lib/http.js:515:28) at runMicrotasks (<anonymous>) at processTicksAndRejections (node:internal/process/task_queues:96:5) { remoteStacktrace: '' }
当前错误捕获代码
getUrl(req.params.url, driver).then(html => { res.send(`${html}`); setTimeout(() => { console.info("INFO: Exiting driver."); driver.quit().catch(() => { console.error("ERROR: Failed to quit driver."); }) }, 1000); }).catch((error) => { console.error(`A critical error was thrown while processing ${req.params.url}. Error was ${error}`); res.send(''); });
核心疑问
- 如何处理该Selenium错误,避免程序终止?
- 无固定触发场景的崩溃该如何排查?
- 如何优化Selenium使用方式,提升稳定性?
其他异常记录
(node:1156709) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added to [process]. Use emitter.setMaxListeners() to increase limit (Use `node --trace-warnings ...` to show where the warning was created)
平台环境
[root@lab ~]# cat /etc/*-release NAME="Red Hat Enterprise Linux" VERSION="8.6 (Ootpa)" ID="rhel" ID_LIKE="fedora" VERSION_ID="8.6" PLATFORM_ID="platform:el8" PRETTY_NAME="Red Hat Enterprise Linux 8.6 (Ootpa)" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:redhat:enterprise_linux:8::baseos" HOME_URL="https://www.redhat.com/" DOCUMENTATION_URL="https://access.redhat.com/documentation/red_hat_enterprise_linux/8/" BUG_REPORT_URL="https://bugzilla.redhat.com/" REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8" REDHAT_BUGZILLA_PRODUCT_VERSION=8.6 REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" REDHAT_SUPPORT_PRODUCT_VERSION="8.6" Red Hat Enterprise Linux release 8.6 (Ootpa) Red Hat Enterprise Linux release 8.6 (Ootpa) [root@lab ~]# firefox --version Mozilla Firefox 91.13.0esr
解决方案与优化建议
1. 阻止程序终止的错误处理
全局捕获未处理异常
添加全局异常捕获,避免未处理的Promise拒绝或uncaughtException直接终止程序,同时强制清理残留进程:
// 捕获未处理的Promise拒绝 process.on('unhandledRejection', (reason) => { console.error('未处理Promise拒绝:', reason); if (driver) { driver.quit().catch(() => { // 强制杀掉浏览器和驱动进程 require('child_process').execSync('pkill -f firefox && pkill -f geckodriver'); }); } }); // 捕获未捕获的同步异常 process.on('uncaughtException', (error) => { console.error('未捕获异常:', error); process.exitCode = 1; if (driver) { driver.quit().catch(() => { require('child_process').execSync('pkill -f firefox && pkill -f geckodriver'); }); } });
完善Promise链捕获
确保所有异步操作都被捕获,包括getUrl内部的潜在异常,避免遗漏未处理的Promise。
2. 优化Selenium使用方式
避免复用driver实例
长时间复用driver易导致内存泄漏、进程异常,建议每次请求创建新driver实例,用完立即销毁:
async function handleRequest(req, res) { let driver; try { // 每次请求创建独立driver const options = new firefox.Options() .headless() .addArguments('--no-sandbox') .addArguments('--disable-dev-shm-usage'); driver = await new Builder().forBrowser('firefox').setFirefoxOptions(options).build(); const html = await getUrl(req.params.url, driver); res.send(html); } catch (error) { console.error(`处理${req.params.url}出错:`, error); res.send(''); } finally { // 无论成功失败都销毁driver if (driver) { try { await driver.quit(); } catch (e) { console.error('销毁driver失败,强制清理进程'); require('child_process').execSync('pkill -f firefox && pkill -f geckodriver'); } } } }
解决EventEmitter内存泄漏
手动调整process的最大监听器数量,或排查代码中重复添加监听器的逻辑:
// 取消监听器数量限制(或设置为更大数值如20) process.setMaxListeners(0);
优化浏览器启动参数
添加稳定性相关参数,减少资源占用:
const options = new firefox.Options() .headless() // 无头模式运行,节省资源 .addArguments('--disable-gpu') .addArguments('--no-sandbox') .addArguments('--disable-dev-shm-usage') // 解决/dev/shm空间不足问题 .addArguments('--memory-high=512') // 限制内存使用(单位MB) .addArguments('--disk-cache-size=102400'); // 限制缓存大小(单位KB)
3. 排查无固定触发场景的错误
增加日志粒度
在getUrl函数内部添加详细日志,记录每个操作阶段(导航、元素等待、源码获取等)的状态,定位崩溃节点:
async function getUrl(url, driver) { console.log(`开始导航至: ${url}`); await driver.get(url); console.log(`导航完成,当前URL: ${await driver.getCurrentUrl()}`); // 等待页面加载完成 await driver.wait(until.elementLocated(By.tagName('body')), 10000); console.log(`页面元素加载完成`); const html = await driver.getPageSource(); console.log(`获取页面源码完成`); return html; }
捕获浏览器日志
获取浏览器控制台日志,排查是否由页面级错误导致崩溃:
// 在driver创建后启用日志捕获 driver.manage().logs().get('browser').then(logs => { logs.forEach(log => console.log(`浏览器日志[${log.level.name}]:`, log.message)); });
监控系统资源
定时记录系统和进程资源使用情况,排查是否因资源耗尽导致崩溃:
const os = require('os'); setInterval(() => { const processMem = process.memoryUsage(); const systemMem = { total: Math.round(os.totalmem() / 1024 / 1024), free: Math.round(os.freemem() / 1024 / 1024) }; console.log(`进程内存: ${Math.round(processMem.heapUsed / 1024 / 1024)}MB | 系统剩余内存: ${systemMem.free}/${systemMem.total}MB`); }, 60000); // 每分钟记录一次
内容的提问来源于stack exchange,提问作者Grant Curell
相关产品推荐
相关产品推荐

