高并发下服务器宕机怎么排查

并发一上来服务器就宕,第一反应别急着重启。得先分清到底是谁挂了:Nginx、应用、Nginx 误判应用、还是数据库拖垮了应用。这篇顺着 浏览器→WAF→LB→Nginx→应用 整条链路捋排查思路,再讲怎么层层做削峰,把洪峰挡在外面。

并发量突然飙上来,服务器扛不住,宕了。你怎么办?

很多人第一反应是重启。但重启只是把症状按下去,洪峰还在,一会儿又给你宕一次。

真正该做的,是先搞清楚到底是哪一环先崩的,然后再谈怎么让整条链路扛住洪峰。这篇我们就顺着请求链路,把排查和防御都捋一遍。

先把整条链路摆出来

排查之前,得先知道一个请求从用户到应用,中间经过了哪些关卡。典型的链路是这样:

客户端(浏览器)
     |
    WAF        (Web 应用防火墙,挡攻击/恶意流量)
     |
    LB         (负载均衡,把流量分发到多台)
     |
   Nginx       (反向代理,转发给后端应用)
     |
    应用        (真正处理业务的服务)
     |
   数据库       (应用背后的数据存储)
​

请求一层层往里走。宕机,一定是这条链路上某个环节先扛不住了。 排查的本质,就是揪出最先崩的那一环。

别一上来就盯着应用,也别一上来就怪 Nginx。顺着链路一段段查,才不会冤枉好人。

到底是谁挂了

用户看到的可能都是 502、504 或者干脆打不开。但底下的真相,可能是好几种完全不同的情况。我们一个个排。

情况一 Nginx 自己挂了

先看最前面的转发层。Nginx 有没有可能是它自己扛不住?

有。并发太高,Nginx 的连接数打满、worker 进程或文件句柄耗尽,它自己就先趴了。

怎么判断:看 Nginx 进程还在不在、看它的错误日志有没有“too many open files”“worker_connections not enough”这类报错。

如果是 Nginx 自己的资源瓶颈,那问题在它身上,得调它的连接数、句柄数配置。

情况二 应用真的挂了

更常见的是,Nginx 好好的,是后面的应用扛不住了。

并发一大,应用线程池被打满、内存被撑爆(OOM)、或者直接崩溃退出。这时候 Nginx 转发过去,后面没人接,就报 502。

怎么判断:去应用机器上看进程在不在、看应用日志有没有异常、看 CPU 内存是不是被打满。

(这块的具体排查,我之前写过一篇专门讲 502 的,思路是相通的——看日志、查进程端口、直连后端。)

情况三 Nginx 误以为应用挂了

这种最坑,也最容易被忽略:应用其实还活着,只是变慢了,结果被 Nginx 当成挂了。

并发高的时候,应用响应变慢,处理一个请求要好几秒。而 Nginx 对后端是有超时时间的,等超过这个时间应用还没回,Nginx 就认为“这后端不行了”,直接返回 504,甚至把它从可用列表里摘掉。

于是明明应用还在苟延残喘,却被判了死刑。

怎么判断:绕过 Nginx 直连应用,如果直连能通(就是慢),那应用没死,是 Nginx 的超时或健康检查太敏感,误杀了。

情况四 数据库拖垮了应用

还有一种,根子更深:应用本身没毛病,是数据库先扛不住,反过来拖死了应用。

并发一大,慢查询堆积、数据库连接池被占满。应用的每个请求都卡在“等数据库返回”上,一直不释放线程。慢慢地,应用的线程全被这些等待占死,新请求进不来,应用看着就像挂了。

这是最隐蔽的一种:你盯着应用查了半天,发现应用代码没问题,其实病根在更后面的数据库。

怎么判断:看数据库的连接数、慢查询日志、CPU。看应用的线程栈是不是大量卡在数据库调用上。

一句话总结这四种:Nginx 崩、应用崩、应用被误判、数据库拖累——症状可能都是打不开,但病根天差地别。排查的关键,是顺着链路一层层确认每一环的死活,别停在第一个看到的表象上。

光排查不够 得层层削峰

查出病根、救活了这一次,不代表下次洪峰来了不再宕。

真正稳的系统,是不让洪峰一路裸奔冲到最脆弱的那一环(通常是数据库)。思路就一个词:削峰——在链路的每一层都做好拦截,把汹涌的流量一层层削减、摊平。

我们顺着链路,从外往里,每一层都设一道闸:

浏览器   ->  前端限制:按钮点一次就置灰,防重复提交
  |
WAF     ->  挡掉恶意流量、爬虫、明显的攻击请求
  |
LB      ->  把流量均摊到多台,别让一台独扛
  |
Nginx   ->  限流:限制每秒请求数、每 IP 连接数
  |
应用     ->  限流 + 熔断降级 + 队列削峰
  |
数据库   ->  被前面层层保护,只接得住的量
​

一层层拆开说:

  • 浏览器端:最外层就先拦一道。按钮点击后立刻置灰、加防抖,别让用户狂点制造无谓的重复请求。成本最低、最该做
  • WAF:把恶意流量、爬虫、攻击性请求挡在门外,别让这些垃圾流量占用后面的资源
  • LB(负载均衡):把流量摊到多台机器上,一台扛不住就多上几台,别让单点独自硬抗
  • Nginx:这里能做限流,限制每秒进来的请求数、限制单个 IP 的连接数,超出的直接挡回去或排队
  • 应用层:这是削峰的重头戏,几个手段一起上——限流(超过阈值的请求直接拒绝,保护自己)、熔断降级(下游扛不住时,快速返回兜底结果,不硬等)、队列削峰(把瞬时洪峰的请求先塞进消息队列,应用按自己的节奏慢慢消费,把尖峰摊平成平缓的流)
  • 数据库:它是最脆弱、最该被保护的一环。前面每一层都削一点,真正落到数据库的量,才是它接得住的

核心思想:越往里越脆弱,所以越往外越要拦。 每层削掉一部分,层层递减,洪峰到数据库时已经变成小溪了。

队列削峰再多说一句

削峰里最能体现“摊平”思想的,是队列削峰,单拎出来讲讲。

想象一下秒杀:一秒钟涌进来 10 万个请求,应用每秒只能处理 1 万个。硬接必死。

队列削峰的做法:把这 10 万请求先全塞进一个消息队列里排队,应用不慌不忙,按自己每秒 1 万的能力从队列里取出来处理。

瞬时洪峰:  |||||||||||  (一秒 10 万涌入)
              |
           消息队列   (先囤着,排队)
              |
应用消费:  | | | | | |  (按 1 万/秒的稳定节奏取)
​

这样一来,洪峰被队列缓冲住了,应用面对的永远是自己能处理的平稳流量,不会被瞬间冲垮。代价是用户可能要等一会儿(请求在排队),但总比整个系统宕了强。

这就是“削峰填谷”——把陡峭的尖峰削下来,填到后面的平缓时段去。

小结

高并发宕机这道题,答的不是“重启”,而是先排查、再防御两步。

排查——顺着链路揪出最先崩的那一环:

  • Nginx 自己挂(连接数/句柄耗尽)
  • 应用真挂了(线程满、OOM、崩溃)
  • 应用被 Nginx 误判(应用只是慢,被超时误杀,直连能验证)
  • 数据库拖垮应用(慢查询/连接池满,应用线程全卡在等库上)

防御——沿着 浏览器 →WAF→LB→Nginx→ 应用 → 数据库 层层削峰:

  • 越往里越脆弱,越往外越要拦;每层削一点,洪峰到数据库时已成小溪
  • 应用层重点上限流、熔断降级、队列削峰;队列把瞬时尖峰缓冲成平稳流

说到底,扛高并发靠的不是某一处的猛药,是整条链路环环相扣、层层设防。想清楚每一环的死活怎么查、每一层的闸怎么设,再遇到洪峰,你就有底气不慌了。那就照着这条链路,把自己系统的每道闸都盘一遍吧。

更多推荐

章节目录