| HANDOFF.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-server/src/main/resources/application.yml.example | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-web/src/views/DataImport.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
HANDOFF.md
@@ -19,12 +19,15 @@ - 加固(已完成,编译/构建通过):后端 `listDirs` 子目录/文件列表各限 300(新增 `dirsTruncated`/`filesTruncated` 标记,`excelCount` 仍为真实总数);前端「从目录批量导入」每批限 60 个文件(超限提示分批);目录浏览弹窗显示截断提示。 - 待办:让崩溃机器用户在 Chrome 开 `chrome://crashes` 记下原因码(OOM / GPU / STATUS_ACCESS_VIOLATION 等),并确认崩溃时点的是侧边栏「数据导入」菜单还是页面上某个按钮;据此决定下一步。 - **重大进展(08-28 下午)**: - 用户确认:**崩溃时服务器后端日志无任何记录 → 请求根本没到后端** → 卡在**静态资源/网络层**(点菜单后浏览器现场下载数据导入页 chunk JS,还没到发 API 那一步)。接口/数据库彻底排除。 - ~~请求根本没到后端~~(旧结论,已被推翻):用户后来确认 **chunk JS 4ms/7ms 正常,pending 的是 `/api/import/batches` 接口**(页面已进入,loadBatches+loadOverview 两个 batches 请求全部 pending);后端无日志=请求卡在 Tomcat 连接/线程层、未执行到 SQL。 - 用户描述补充:点侧边栏「数据导入」菜单 → 无反应、停留主页 → 浏览器弹「应用程序无反应,是等待还是退出」= 渲染/浏览器进程 hang,非 crash。 - 发布机器才是用户运行环境;本机 D:\trafficAudit 仅打包测试(17:39 后停止,日志正常)。 - **本地打包形态压测(headless Chrome,20 轮菜单切换)**:导航 17~89ms、0 JS 错误、DOM 恒 949 节点无累积、堆内存 11~37MB 无泄漏、最长长任务 80ms → 应用 JS 层无死循环/大计算/泄漏。 - **已加 Tomcat 访问日志**(`server.tomcat.basedir: .` + accesslog,jar 同级 `logs/access.*.log`,pattern 含 `%D` 耗时,静态资源也记录)——本地实测 chunk 请求 3ms。**重新打包发布后**,下次崩溃看 access log:chunk 请求是否到达、耗时多少 → 直接定位「服务器慢」还是「客户端没发出请求」。 - 待用户:重新打包发布后,崩溃时看 ①服务器 `logs/access.*.log` ②客户端 F12 Network 中 `chunk-*.js` 请求状态(pending/stalled/耗时)。 - **根因实锤(本机实验复现)**:Tomcat 9 NIO 下慢连接不占线程(slowloris 无效,已测),但**慢速 multipart 上传会占线程**——240 个慢上传占满线程池后,chunk 与 batches 请求全部 pending/超时(与用户现象一致)。**数据导入是同步长任务**(4 万行导入占 1 个线程 5 分钟,前端超时 600s),多用户同时导入/目录导入串行 → 200 线程耗尽 → 新请求(batches、静态文件)排队 pending → 浏览器等 →「无响应」弹窗。**只崩数据导入页**因为它进页即发 2 个 batches 请求且用户多在导入场景操作;其他页面在同样时刻也会 pending 只是未被注意。 - **修复(commit 728b372,已验证)**:① `server.tomcat.threads.max: 500`(application.yml + example);② 数据导入页 `loadBatches`+`loadOverview` 合并为 `loadData`(进页/刷新/导入后只发 1 个 limit=200 请求,表格取前 50,概览复用同批数据)。**修复验证**:同样 240 慢上传压力下,batches 0.2s、chunk 0.1s 正常。 - **遗留(建议下一步根治)**:导入异步化(@Async 独立线程池执行导入、前端轮询进度),彻底不占 Tomcat 请求线程;或 nginx 托管静态资源。对方 dev 机单用户也 pending,需看对方后端日志(可能 DB 连接/锁,或对方当时在跑导入),与本修复不冲突。 ### 3. 待办不变 - `docs/打包部署说明.docx` 第六节「外置 application.yml 需包含完整配置」改为「属性级合并」后重新导出 docx(未做)。 traffic-audit-server/src/main/resources/application.yml.example
@@ -3,6 +3,8 @@ port: 8090 tomcat: basedir: . threads: max: 500 accesslog: enabled: true directory: logs traffic-audit-web/src/views/DataImport.vue
@@ -26,7 +26,7 @@ <el-card shadow="hover" class="import-card" style="margin-bottom:15px"> <div class="card-head">本月导入进度({{ period || '未选择报表期' }}) <el-button type="text" icon="el-icon-refresh" style="float:right" @click="loadOverview">刷新</el-button> <el-button type="text" icon="el-icon-refresh" style="float:right" @click="loadData">刷新</el-button> </div> <div v-if="!period" class="tip">请先在上方选择报表期</div> <div v-else class="overview-grid"> @@ -170,7 +170,7 @@ <el-card shadow="never" style="margin-top:15px" class="batch-card"> <div slot="header" class="card-head"> 导入记录({{ period || '全部报表期' }}) <el-button type="text" icon="el-icon-refresh" style="float:right" @click="loadBatches">刷新</el-button> <el-button type="text" icon="el-icon-refresh" style="float:right" @click="loadData">刷新</el-button> </div> <el-table :data="batches" border size="small" v-loading="batchLoading" style="width:100%"> <el-table-column prop="importType" label="导入类型" width="150"> @@ -394,7 +394,7 @@ this.importType = this.moduleSel[key] || '' this.dirPath = (this.importType && localStorage.getItem('dirLast_' + this.importType)) || '' }, onPeriodChange() { this.loadOverview(); this.loadBatches() }, onPeriodChange() { this.loadData() }, endpoint(t) { const map = { h2032: '/import/h2032', @@ -655,13 +655,17 @@ run(0) }) }, afterImport() { this.loadOverview(); this.loadBatches() }, loadOverview() { if (!this.period) return api.get('/import/batches', { params: { period: this.period, limit: 200 } }) afterImport() { this.loadData() }, loadData() { if (this.batchLoading) return this.batchLoading = true const params = { limit: 200 } if (this.period) params.period = this.period api.get('/import/batches', { params }) .then(res => { const map = {} const records = (res.data && res.data.records) || [] this.batches = records.slice(0, 50) const map = {} records.forEach(r => { const key = this.batchKeyToType(r.importType) if (!key) return @@ -673,6 +677,7 @@ this.overview = map }) .catch(() => {}) .finally(() => { this.batchLoading = false }) }, batchKeyToType(k) { if (BATCH_TYPE_MAP[k]) return BATCH_TYPE_MAP[k] @@ -724,8 +729,7 @@ } }, mounted() { this.loadBatches() this.loadOverview() this.loadData() } } </script>