服务明明活着,却谁也连不上它——我看着 1024 张“号牌“被一张张领光 DNS 那事儿刚消停没几天另一个客户的系统就给我寄来一份新剧本比上回还邪门服务进程活着心跳正常端口也在监听用 telnet 一连通可业务请求发过去全部超时。客户运维在群里发了句我至今记得的话它好像醒着但在装睡。我到现场那天已经是这毛病发作的第三天。规律准得像闹钟每天下午两点四十左右开始批量超时重启服务立刻满血复活第二天照旧。客户的老运维已经总结出经验了——到点重启别问为什么。我问日志呢他说日志干干净净连个 error 都翻不出来。这话反而让我起了疑心。你想啊一个服务每天准时装死却一声 error 都不喊要么是它真没毛病要么是它想喊喊不出来。CPU 不忙、内存正常、磁盘剩一大把我把常规嫌疑人挨个排了一遍最后剩下一个平时最没人搭理的指标文件描述符。这里先补个课。文件描述符File Descriptor简称 fd是 Linux 给每个打开的东西发的号牌——不光是文件网络连接、管道、读写日志都得先领号牌才能干活。Linux 说一切皆文件我那天对这句话有了生理性理解连 TCP 连接都在排队领号牌。一个进程能领多少张是有额度的管这个额度的就是 ulimituser limit用户级资源上限默认值往往就 1024。我用 lsoflist open files把进程手里所有号牌列出来的命令数了一下1018。过十分钟再看1020。我干脆搬了个凳子坐在那儿盯着看它一分钟涨一两张稳稳当当爬到 1024然后一动不动。那一刻我基本就明白了号牌领光了。新连接进门系统想发新号牌发不出来accept服务端接收新连接的动作直接报 Too many open files——客人堵在门口门就是不开。老连接还撑着场面所以进程活着新客一个进不来所以它装睡。顺便打岔一句有个细节特别损打日志这个动作本身也得先领一张号牌。号牌没了报错根本写不进文件。所以日志干干净净不是没出事是它喊救命的那张嘴恰好被它自己弄丢的号牌堵上了。这是我那次学到的最反常识的一课。接下来就是揪出谁在偷偷领号牌不还。lsof 往下翻九成多的号牌都指向同一个目录/data/tmp/req_dump_*.json。我盯着这串文件名愣了三秒脸有点热——这是我两周前写的。当时客户反馈一个接口返回内容诡异我为了留证据塞了个诊断开关每个请求进来把请求体 dump 成文件存到临时目录。文件确实存了可写文件的流我只管开、忘了关。平时没人发现是因为漏得慢QPS每秒请求数低的时候一小时也就漏百来张1024 张额度够撑一整天。偏偏那阵子客户搞活动流量翻了一倍多泄漏点提前引爆于是每天下午两点四十准时发病。重启能好也好解释——进程一死号牌全数上缴第二天从头再漏。客户的到点重启大法本质上是用改密码的办法对付一个不停借钱的账户。修复说穿了就两步先把漏点堵上再把额度放宽。漏点那头我把诊断开关改成 try-with-resourcesJava 里用完自动还的写法借了号牌自动归还顺手给临时目录加了定时清理额度那头在 systemdLinux 上负责拉起和管理后台服务的管家的服务配置里加 LimitNOFILE65535等于告诉管家这个服务的号牌额度放大到 65535。这里有个坑替你踩过了我一开始改的是 /etc/security/limits.conf改完重启服务上限纹丝不动。后来才搞明白systemd 拉起来的服务压根不看那个文件人家只认自己配置里的 LimitNOFILE。号牌谁发的提额就得找谁。打岔第二回我一直觉得重启治百病是运维界的止痛药不治病但确实不疼。那三天客户靠它续命我笑不出来——因为顺手一查我们自己网关服务的 ulimit 也还是默认的 1024量一大就是同一颗雷只是还没轮到它炸。多说一句fd 泄漏不止这一种长相。有人是 HTTP 客户端每次请求都新建一个不复用连接关不掉有人是连接池配了上限却没配回收号牌借出去就不还还有的更隐蔽——某个库在抛异常的路径上没写 close平时一点事没有一报错就开始漏。所以案例你背不完记一个动作就行服务上线后隔三差五跑一下 lsof -p 进程号 | wc -l这个数应该是一条平稳的波浪线而不是一条只涨不跌的楼梯。这套东西适合谁只要你写过后端服务或者管着别人写的服务都值得花半小时搞懂——它不挑语言Java、Go、Python 的进程一样会漏。但如果你现在只在本地跑跑脚本或者服务流量小、额度闲置一大把倒不用焦虑知道号牌这回事就行等你第一次撞上进程活着却没人连得上今天这篇就是排查地图。我现在养成了个习惯跟上篇记物业电话一样土但好用新接手一个服务先看一眼 ulimit -n 的值再看一眼进程当前的 fd 数。你呢有没有遇到过进程活着、端口通着、就是不干活的灵异现场最后揪出来的凶手是谁评论区聊聊——我猜不少人会写重启之后就再没复现过那不是好了那是它在憋大招。下一篇预告号牌是进程的事还有一种更安静的死法——磁盘明明没存多少东西空间却满了查到最后发现连文件名本身都成了负担。下篇聊聊 inode 耗尽的故事。