第三方接口回写无声停摆:一次HTTP无超时引发的线程池耗尽故障复盘
概述
T 日下午,一套在产 MES 系统的第三方排产系统(下称 APS)工单状态回写链路发生静默失效:MES 侧所有业务功能(开工、报工、结单)完全正常,但对 APS 的状态推送全部停止,回写失败记录表自当日起不再产生任何新数据。故障持续约两天后被发现,重启服务即恢复,无业务数据损坏。
本文完整记录该故障的定位过程、根因机制、修复方案与验证方法。该故障的典型性在于:四个各自不算缺陷的设计组合在一起,形成了一个无任何错误输出、无任何自愈可能、且监控系统完全盲区的失效模式。
- 直接根因:HTTP 客户端未配置连接与读取超时,第三方接口瞬时半死(TCP 连接建立但不再回包)后,10 个推送工作线程永久阻塞在 socket 读取上;
- 放大因素:推送经固定大小线程池异步执行、队列无界、异常被静默吞掉;
- 恢复手段:仅能重启进程(无超时的 socketRead 永不返回,线程无法自行释放)。
大文件下载必失败、小文件全正常:一行removeChild引发的网关Broken pipe排查实录
老 MES 系统新上了一套异步导出:后台游标逐行读、流式写 Excel,完成后落下载记录并弹桌面提醒,用户去”下载中心”页面取件。上线当天就收到一条诡异的反馈:
- 几十 KB 的小文件,点击即下,一切正常;
- 72MB 的大文件,一点下载立刻失败,下载栏直接飘红。
与此同时,网关(Spring Cloud Netflix Zuul 1.x)日志里躺着与失败时刻精准对应的 WARN:
1 | o.s.c.n.z.f.post.SendResponseFilter : Error while sending response to client: java.io.IOException: Broken pipe |
报错在网关,现象在前端,最后”凶手”却锁定在页面里一行毫不起眼的”善后”代码上。这次排查横跨浏览器、nginx、网关、后端四层,值得完整复盘——因为每一层的日志都”无罪”,唯一有罪的代码行离报错现场隔了三层。
从选项截断到全局自适应:EasyUI下拉面板通用机制与三重陷阱
服役超过十年的 MES 前端,技术栈是 EasyUI 1.4.3 + jQuery 1.11.3 这对”考古级”组合。它有一个存在了同样十年的体验缺陷:combobox 下拉面板的宽度永远等于输入框宽度——这是框架默认行为。于是所有长选项都逃不过被截断的命运:”SMT贴片车间-第一生产线-回流焊工段-NS-04高端精密贴片机设备”在下拉里只剩前半截,用户靠脑补补全。
这类问题单看是小事,逐页修是不归路:全系统几十个页面、数不清的下拉框,每个页面写一遍 panelWidth 计算?下一个新页面照样忘。正确的解法是在全局扩展层做一个通用机制:改一处,全系统生效,且给页面留逃逸口。本文记录这个机制的完整设计、实现、三重陷阱踩坑实录,以及一套无头浏览器验证矩阵——作为年度复盘,这也是”老系统增强”这类工作方法论的一个样本。
一个后台线程断言异常如何杀掉整个插件功能:read-action线程模型踩坑实录
IntelliJ平台PsiReference双向导航:从isReferenceTo到word-index的架构权衡
需求:让框架式调用也能双向跳转
业务代码里大量存在这种框架式调用——方法名藏在字符串字面量里:
1 | // MyBatis 风格:字符串 = Dao 接口方法名 |
IDE 原生对这种字符串无能为力。插件的诉求是四向导航:
| 位置 | 操作 | 期望 |
|---|---|---|
Java "getXxx" 字符串 |
Ctrl+B | 跳 Dao 方法 / Moc XML |
| Dao 方法声明处 | Ctrl+B / Find Usages | 反查字符串 |
Mapper XML <select id> |
Ctrl+B | 弹「Dao 方法 + 调用点」 |
Moc XML name 属性 |
Ctrl+B | 弹 Java 调用点 |
这篇文章记录实现过程中的三次架构演进——每一次都是被真实现象(选择框不弹、进度条死循环、几万文件搜索)逼出来的重新设计。
IDEA插件内存泄漏与GC风暴:一次jstat驱动的完整排查与修复
背景:装了插件的 IDEA 卡到不能用
团队自研的 IDEA 插件更新后,出现了一个很诡异的症状:什么都没做,只是打开了几个文件,IDEA 就卡得不行。CPU 风扇狂转、界面掉帧、时不时冻结几秒。
比”卡”更有价值的是手里的 GC 数据。连续抓了几轮 jstat -gcutil:
1 | S0 S1 E O M CCS YGC YGCT FGC FGCT GCT |
只看数字就能拆出三层信号。这篇文章完整复盘这次排查:从 jstat 解读、到沿执行路径找代码证据、到三类修复的设计与验证。
从同步超时到异步流式:一次MES报表导出链路的完整改造
老 MES 系统里”导出”大概是最容易被低估的功能:早期数据量小,一个同步接口把数据查出来写成 Excel 直接回传,皆大欢喜。等到单表日志类数据涨到千万行、报表动辄几十万行时,这条链路开始连环爆雷。本文记录一次完整的导出链路改造:从同步超时丢文件,到「提交即返回 + 游标流式读取 + 异步写入 + 桌面通知取件」的全套架构,包括关键实现细节、踩过的每一个坑,以及清楚留着的优化债。
一、同步导出的四宗罪
改造前的链路是典型的”一把梭”:
1 | 用户点导出 → 请求线程分页循环查全量 → 攒成大 List → 写 Excel → HTTP 响应回传 |
数据量上来后暴露的问题按出现频率排序:
- 网关超时:内部走 Ribbon/Zuul 网关,读超时默认 10 秒,导出耗时一超,连接被网关掐断——用户拿到半个文件或一个报错页;
- 内存压力:全量行同时驻留 JVM 堆(
List<Map>+ 写缓冲),几个人并发导出大报表,堆直接见顶; - 体验差:用户必须挂着页面等,手一抖刷新就前功尽弃;
- 不可回溯:一旦超时,服务端即便把文件写出来了也没法交给用户,白白浪费一次全量查询。
期间打过一层补丁:同步导出超时后转后台继续写,完成后上传文件、发通知。这解决了一部分问题,但”用户先同步等 30 秒”的体验和分页翻页式读取的额外开销还在。于是有了这次彻底的改造。