VSCode Less语言服务扩展生产环境文件扫描卡顿求助
VSCode Less语言服务扩展生产环境扫描卡顿排查分析
问题背景
生产环境下,vscode-less扩展完成文件扫描并输出日志「scaning file done」耗时近10分钟,但调试环境运行正常。控制台日志显示扫描过程仅打印了少量文件路径,与实际项目文件数量不符。
核心代码片段
stream.on('file', (stat) => { const entry = makeEntryFile(stat.path, stat.ctime); // Return Cache if it exists and not outdated const cached = cache.get(entry.filepath); if (cached && cached.ctime.getTime() >= entry.ctime.getTime()) { listOfPromises.push(cached); return; } listOfPromises.push(makeSymbolsForDocument(cache, entry, settings)); }); stream.on('error', (err) => { if (settings.showErrors) { reject(err); } }); stream.on('end', () => __awaiter(this, void 0, void 0, function* () { let projectSymbols = []; let importedSymbols = []; try { projectSymbols = yield Promise.all(listOfPromises); // on production, it takes almost ten minute to get the log done. (0, server_1.getConnection)().console.log('scaning file done') if (settings.scanImportedFiles) { importedSymbols = yield scannerImportedFiles(cache, projectSymbols, settings); } } catch (err) { if (settings.showErrors) { reject(err); } } resolve(projectSymbols.concat(importedSymbols)); })); function makeSymbolsForDocument(cache, entry, settings) { return (0, fs_1.readFile)(entry.filepath).then((data) => { if (cache.keys().length % 50 == 0) { (0, server_1.getConnection)().console.log('scaning file ' + entry.filepath) } const doc = vscode_languageserver_1.TextDocument.create(entry.filepath, 'less', 1, data); const { symbols } = (0, parser_1.parseDocument)(doc, null, settings); symbols.ctime = entry.ctime; cache.set(entry.filepath, symbols); return symbols; }); }
控制台日志
scaning file businessapplication/invoice/splitInvoiceModal.less scaning file platform/flow/workflowdrawing/ruletabs/designatedPosts.less scaning file platform/expenseclaim/editor/component/expenseRecord/compositeAmount.less scaning file done
可能的卡顿原因及排查方案
1. 无限制并发导致资源耗尽
代码中通过Promise.all一次性处理所有文件的读取和解析任务,生产环境文件量极大时,会瞬间占用大量CPU、IO资源,导致任务排队阻塞。调试环境因文件量小或调试器隐性限制并发,所以无此问题。
- 排查:添加并发控制,使用类似
p-limit的库限制同时执行的任务数(比如每次处理20个文件),避免一次性启动过多异步操作。 - 验证:修改代码加入并发限制后,重新打包生产环境扩展,测试扫描耗时是否下降。
2. 缓存校验逻辑失效
缓存校验的时间比较cached.ctime.getTime() >= entry.ctime.getTime()可能存在精度问题:生产环境文件系统的时间戳精度(如毫秒级)与调试环境不同,导致大量缓存被误判为过期,所有文件被迫重新解析,大幅增加耗时。
- 排查:在代码中添加缓存命中/未命中的统计日志,对比生产与调试环境的命中率;调整时间比较逻辑,允许1秒以内的时间差,避免精度问题导致的缓存失效。
3. 文件读取IO性能瓶颈
生产环境若使用网络存储(如NAS)或低性能磁盘,fs.readFile的异步读取延迟会被放大,大量文件的IO延迟叠加导致总耗时剧增。调试环境通常使用本地高速磁盘,所以表现正常。
- 排查:在
makeSymbolsForDocument中添加每个文件读取+解析的耗时统计,定位是否是IO环节拖慢速度;优化错误处理,补充readFile的错误捕获,避免因个别文件读取失败导致整体任务阻塞。
4. 同步日志输出阻塞任务
生产环境中,server_1.getConnection().console.log是同步IO操作,当文件数量极大时,累计的日志输出会阻塞异步任务执行。当前每处理50个文件输出一次日志,若文件总量上万,日志IO的开销不可忽视。
- 排查:临时注释所有日志输出代码,重新打包测试扫描耗时;若耗时明显下降,将日志输出改为异步操作,或进一步降低输出频率(如每200个文件输出一次)。
5. 生产环境编译优化异常
调试环境使用未压缩代码,而生产环境代码经过webpack打包压缩,可能存在编译优化导致的逻辑异常:比如__awaiter编译错误、parseDocument函数被错误优化导致性能下降。
- 排查:将生产环境的编译产物拿到本地调试,复现卡顿问题;对比调试与生产代码的编译差异,检查是否有函数逻辑被篡改或性能关键代码被压缩降级。
内容的提问来源于stack exchange,提问作者Jack Hu
相关产品推荐
相关产品推荐

