ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

从“土豆服务器”到性能调优:高延迟、掉线与根因排查实战

从“土豆服务器”到性能调优:高延迟、掉线与根因排查实战 玩家群里开始刷“冰岛土豆服务器”时服务器并不会真的变成一颗土豆但用户体验往往已经站在崩溃边缘。这个系列前两篇记录的是故障前兆和巡检思路这一篇作为第三篇不再停留在解释现象而是完整走一遍从吐槽到指标、从指标到根因、从根因到优化验证的排障流程。文章里会用一套可复现的最小服务器环境和压测命令说明高延迟、卡顿、掉线这些“土豆”症状应该怎么查、怎么修、怎么确认修复有效。在写命令和配置之前先说明一个原则不要把“玩家吐槽”当成一个段子处理它通常意味着延迟升高、错误率上升、吞吐下降或连接异常。后面所有工作都是把主观体验翻译成可量化的技术指标。1. 先把“土豆服务器”从吐槽翻译成技术指标1.1 卡顿、延迟高、掉线分别对应什么“土豆服务器”不是一个标准技术名词但它描述的体验非常一致点一下按钮半天才有反应游戏或页面操作频繁卡住连接经常断开。从排障角度看这三类体验要拆成不同的问题去查。卡顿通常对应单次请求耗时上升。玩家感觉是“技能放不出来”技术上是接口响应时间变长尤其是 P95、P99 这类尾延迟分位数升高。只观察平均延迟看不出问题因为少量慢请求会把长尾掩盖掉但玩家恰恰最容易感受到最慢的那几次。延迟高则更偏向网络和设备距离。一个请求从客户端到服务器再返回数据中间会经过很多路由节点。如果节点绕路、带宽打满或客户端与服务器物理距离过远RTT 就会明显上升表现就是操作有“空窗期”。掉线可以对应连接建立失败、连接被重置或连接超时。服务器端连接数达到上限、应用线程池被占满、负载过高导致进程重启、代理层主动断开空闲连接都会让玩家退出时感觉“掉了”。这里的关键判断是同一个“土豆”现象背后可能有三层瓶颈同时存在。网络层、系统层、应用层和数据库层都可能成为短板所以不能只盯着某一个指标。1.2 玩家体感、技术指标和排查工具的映射关系可以用一张表把吐槽翻译成排障语言玩家体感对应技术指标常见瓶颈第一层排查工具卡顿请求 P95/P99 上升、平均延迟上升应用线程阻塞、CPU 竞争、数据库慢查询压测工具、链路追踪、慢查询日志延迟高RTT 高、首字节时间上升网络绕路、带宽打满、DNS 慢、跨地域链路ping、mtr、iftop、tcpdump掉线连接失败、连接重置、超时退出连接数满、负载过高、代理超时、安全策略ss、netstat、应用错误日志整体像“土豆”CPU、内存、磁盘、网络使用率接近上限突增流量、资源争抢、配置不足top、vmstat、iostat、sar这张表的价值在于统一团队语言。后端开发说“接口没慢”运维说“网络没丢包”测试说“复现不了”很多时候是因为大家看的指标不一样。先把现象映射到指标再决定用哪套工具排障才不会变成各说各话。2. 复现环境准备先搭一台能被“压成土豆”的最小服务器2.1 最小拓扑设计为了不把问题快速引入多台机器的复杂度建议先用最简单拓扑复现一台压测机、一台应用服务器、一台数据库。三台机器可以都是虚拟机性能不需要强目的是让瓶颈更容易暴露而不是追求压出很高的 QPS。这种拓扑能还原大多数“土豆服务器”场景。应用服务器负责处理请求数据库负责持久化压测机负责模拟客户端流量。如果只有一台机器应用、数据库混在一起压测时 CPU 和内存数据会互相干扰很难判断瓶颈来自应用还是数据库。生产环境的链路通常更长会有反向代理、负载均衡、缓存、消息队列等组件但排障时仍然遵循同样的隔离原则从最简链路入手逐个环节加压找到第一个打满的节点。2.2 安装基础监控工具应用服务器和压测机建议提前安装以下工具避免压测中途才发现没有观测手段。# CentOS/RHEL 系列 yum install -y epel-release yum install -y htop iotop iftop sysstat tcpdump mtr nmap-ncat # Debian/Ubuntu 系列 apt update apt install -y htop iotop iftop sysstat tcpdump mtr-tools netcat-openbsdsysstat 提供 sar、iostat 等历史资源统计工具可以观察问题发生前后的趋势iftop 用来实时查看网络流量tcpdump 用来抓包分析 TCP 重传、握手耗时常。压测机还需要安装 wrk 或 ab用来制造负载。2.3 压测工具的最小用法wrk 的常用压测方式如下# 使用 4 个线程保持 100 个并发连接压测 30 秒 wrk -t4 -c100 -d30s http://127.0.0.1:8080/api/demo参数含义-t表示压测线程数不要盲目调大线程数通常和 CPU 核数相当-c表示并发连接数模拟同时有多少客户端在访问-d表示压测持续时间。wrk 会输出 QPS、平均延迟、延迟分布等结果适合快速定位服务吞吐上限。ab 适合更简单的 HTTP 接口测试# 总请求 10000并发 100 ab -n 10000 -c 100 http://127.0.0.1:8080/api/demo-n是总请求数-c是并发数。ab 的输出里有Failed requests、Requests per second、Time per request等字段可以直接判断错误率和吞吐。压测开始前先单独压一个不需要查询数据库的接口再压一个查询数据库的接口通过对比就能初步判断瓶颈是否在应用逻辑或数据库访问层。2.4 环境基线检查压测前先检查操作系统限制否则会出现“压测一上来连接数报错”的假象。# 查看当前文件句柄限制 ulimit -n # 查看最大可打开文件数 cat /proc/sys/fs/file-max # 查看监听队列大小 cat /proc/sys/net/core/somaxconn # 查看当前 TCP 连接统计 ss -s文件句柄限制过小时高并发连接会直接报Too many open filessomaxconn过大或过小需要和后端应用配合避免连接排队失败。环境基线记录下来后面优化后再对比才能确定改动是否有效。3. 从现象反推根因高延迟、掉线、CPU 飙高怎么查3.1 网络链路排查从 ping、mtr 到 tcpdump远程设备和本机不通或延迟非常高先分两步先看网络链路再看服务器资源。先用 ping 看基础连通性和网络延迟# 持续 ping 30 次输出统计 ping -c 30 target.server.ip如果平均延迟明显高于同机房其他机器说明网络路径可能有问题。再用 mtr 看每一跳的延迟和丢包# 持续 10 次显示每跳路由 mtr -rwz -c 10 target.server.ipmtr 输出中如果某一跳持续丢包或延迟飙升就说明网络瓶颈在该节点附近。但要注意某些节点 ICMP 限速会显示 100% 丢包但不一定影响真实 TCP 流量。因此还需要结合 TCP 层的验证。最常见和直观的方法是抓包看重传# 在应用服务器上抓取 8080 端口的 TCP 流量限制数量 tcpdump -i eth0 -nn tcp port 8080 -c 200抓包输出里如果大量出现Retransmission说明数据包在网络上发生了丢失TCP 协议会自动重传这会导致延迟升高和连接不稳定。这时候问题往往不在应用代码而在带宽、路由或防火墙策略。3.2 系统资源排查CPU、内存、磁盘和句柄网络没问题时回头看服务器自身。# 实时刷新进程和 CPU 占用 top # 每 1 秒输出一次系统运行队列和内存状况 vmstat 1 5top 输出中要重点关注 CPU 行里的us、sy、wa和st。us高说明用户态进程吃 CPUsy高说明内核态消耗大常见原因包括系统调用频繁、上下文切换太多wa高说明磁盘 I/O 成为瓶颈st高说明宿主机资源争抢常见于虚拟化环境。vmstat 的r列代表运行队列长度如果持续大于 CPU 核数说明 CPU 已经饱和。si和so如果持续不为 0说明内存不足导致换页应用会明显变慢。磁盘 I/O 可以用 iostat 查看# 每 2 秒输出一次磁盘使用情况 iostat -x 2 5重点看%util和await。%util接近 100% 时磁盘已经持续繁忙await升高说明 I/O 请求排队时间变长数据库或日志写入会显著变慢。3.3 应用和数据库排查慢请求、线程池和连接池系统资源没有打满时瓶颈往往在应用内部。先看应用日志里有没有超时、连接池耗尽、线程拒绝的异常再观察线程状态。以 JVM 类应用为例最快速的方式是打印线程栈# 获取 Java 进程 PID jps -l # 打印线程信息 jstack pid /tmp/jstack_$(date %s).txt线程栈里如果大量线程处于BLOCKED或WAITING说明线程互相等待锁或都在等待外部服务返回。GC 日志也要同步查看频繁 Full GC 会导致 CPU 飙升和请求停顿。启动参数中增加 GC 日志常见的做法是-Xlog:gc*:/tmp/gc.log:time,uptime,level日志路径和格式按 JDK 版本调整旧版本可能使用-Xloggc:/tmp/gc.log方式。数据库侧重点看慢查询-- MySQL 查看当前正在执行的语句 SHOW PROCESSLIST; -- 查看慢查询日志是否开启 SHOW VARIABLES LIKE slow_query_log%;慢查询日志里如果出现大量执行时间超过 1 秒的 SQL就要用EXPLAIN分析执行计划。type为ALL表示全表扫描rows估算值很大时索引缺失几乎是明确结论。3.4 一个可复用的排查顺序步骤检查对象判定方式下一步1网络链路ping/mtr 丢包、RTT 高tcpdump 确认 TCP 重传2TCP 连接ss -s 看 TIME_WAIT、连接数超限调整文件句柄或连接回收参数3CPU/内存/磁盘top/vmstat/iostat 指标异常定位是用户态、内核态还是 I/O wait4应用线程池日志出现拒绝执行、线程池满导出线程栈检查锁和外部调用5数据库慢查询日志、SHOW PROCESSLISTEXPLAIN 分析索引和执行计划按这个顺序能避免“CPU 跑到 100% 就盲目加机器”的误区。因为 CPU 高可能只是现象真正原因可能是网络超时后大量重试重连也可能是数据库慢查询拖住了线程池。4. 三类典型“土豆化”场景和对应优化4.1 场景一带宽打满入口流量排队现象是压测开始后延迟快速上涨但服务器 CPU 占用并不高。用 iftop 能看到某个网卡的实时流量逼近带宽上限。iftop -i eth0 -n带宽打满后数据包会排队发送RTT 上升TCP 重传增加。优化方向不是立刻加机器而是先减少不必要的流量。常见手段包括开启接口响应压缩、去掉冗余字段、降低静态资源和日志传输体积。压缩在 Nginx 层可以这样配置gzip on; gzip_comp_level 5; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript;压缩等级不是越高越好。gzip_comp_level调高会占用更多 CPU实际收益在等级 5 到 6 之后明显下降。生产环境需要根据 CPU 负载和带宽成本权衡。4.2 场景二应用线程池、连接池配置不合理现象是接口偶尔很慢日志中出现类似Connection pool is exhausted或Task rejected的异常。这通常说明应用等待外部资源的时间太长线程池和连接池参数又不匹配。以 Spring Boot 场景为例常见配置如下但版本不同具体参数名和默认值要先确认后再改# Tomcat 线程池配置 server.tomcat.threads.max200 server.tomcat.threads.min-spare20 server.tomcat.accept-count100 # HikariCP 数据库连接池配置 spring.datasource.hikari.maximum-pool-size20 spring.datasource.hikari.minimum-idle5 spring.datasource.hikari.connection-timeout30000线程池调大不是万能药。如果每个请求都阻塞 3 秒等待第三方接口线程池再大也会被占满正确方向是给外部调用设置短超时、增加缓存、降级或异步化。另一个常见坑是 HTTP 客户端没有复用连接每次请求都新建 TCP 连接。高并发下会出现大量TIME_WAIT连接表现为端口不足或响应变慢。连接池的作用是复用连接减少握手开销生产环境必须配置并验证。4.3 场景三数据库慢查询和热点行竞争现象是压测时吞吐先升后跌数据库 CPU 占用高应用日志大量超时。打开慢查询日志后发现某条 SQL 频繁上榜。SELECT * FROM user_reward WHERE reward_status 0 ORDER BY created_at LIMIT 20;如果这张表数据量很大且reward_status字段没有索引查询就需要全表扫描。通过EXPLAIN确认后加上联合索引ALTER TABLE user_reward ADD INDEX idx_status_created (reward_status, created_at);索引不是越多越好。每个索引都会占用磁盘空间并降低写入性能。真正有效的方式是根据查询条件、排序字段和分组字段设计索引而不是把每一列都建上。热点行竞争常见于秒杀、积分、余额等高频更新场景。所有请求都更新同一行记录时数据库行锁会变成串行操作。优化方向包括将更新拆成累加明细、削峰、用 Redis 预扣减或改为异步批量落库。5. 内核参数、中间件配置和部署层面的稳定化5.1 常用内核参数怎么调整高并发连接下内核参数会直接影响服务器表现。先看当前值再决定是否调整。# 查看当前内核参数 sysctl net.core.somaxconn sysctl net.ipv4.tcp_max_syn_backlog sysctl fs.file-max sysctl net.ipv4.ip_local_port_range常见的调整方式是在/etc/sysctl.conf中加入配置net.core.somaxconn 1024 net.ipv4.tcp_max_syn_backlog 2048 fs.file-max 100000 net.ipv4.ip_local_port_range 1024 65535net.core.somaxconn控制全连接队列长度Nginx、Tomcat 等软件也会有自己的 backlog 参数两者需要匹配。tcp_max_syn_backlog控制半连接队列长度SYN 洪峰时容易成为瓶颈。需要注意net.ipv4.tcp_tw_reuse这类参数在不同内核版本中的默认行为和安全性不一致生产环境不要照搬网上的“优化大全”。先理解参数语义做小范围变更并通过压测验证后再批量应用。5.2 Nginx 反向代理的超时和重试策略反代层是流量入口它的超时和重试策略直接影响客户端体验。Nginx 配置中常见的几个参数如下upstream backend_app { server 127.0.0.1:8080; keepalive 64; } server { listen 80; location / { proxy_pass http://backend_app; proxy_connect_timeout 5s; proxy_read_timeout 30s; proxy_send_timeout 30s; proxy_next_upstream error timeout; proxy_next_upstream_tries 2; } }proxy_connect_timeout是建立上游连接的超时时间不要设置过大否则后端无响应时前端会一直挂着等待。proxy_read_timeout是两次读操作之间的超时适合按业务接口预期耗时配置。proxy_next_upstream决定哪些情况会重试下一个后端节点错误重试可以提高可用性但也会在故障时放大流量因此要限制proxy_next_upstream_tries。后端服务做发布时如果从 Nginx 摘除节点不够及时健康检查会继续把请求打到下线中的节点导致短暂 5xx。配合慢开始和健康检查能降低发布期间的错误率。5.3 发布、扩容和限流怎样降低“土豆感”一台服务器变成土豆最常见诱因是流量突增超过容量。优化不能只在代码层打补丁还要在部署层做节制。发布时应该采用滚动发布或分批发布并保证新节点通过健康检查后再接入流量。扩容要有提前量不能等 CPU 被打满再执行命令。自动化扩容规则建议基于多个指标例如 CPU 使用率持续 70% 以上 5 分钟且请求 P99 延迟开始上升再触发新增实例。限流是保护系统底线的有效方式。网关或应用层按 QPS、并发数或用户维度限流超限后返回快速失败或降级提示而不是让请求全部堆积到数据库。实际项目中宁可让少量请求快速失败也不能让所有请求全部超时。6. 优化效果验证与上线前检查清单6.1 重新压测不能只看平均延迟优化完成后用和优化前相同的压测参数再压一轮。没有基线数据就没有办法判断优化是否有效。对比时至少看四类指标QPS、P50、P95、P99 延迟和错误率。# 保持和优化前一致的参数 wrk -t4 -c100 -d30s http://127.0.0.1:8080/api/demo输出示例只用于说明字段含义实际数值取决于压测机性能和服务端配置Requests/sec: 1200.43 Transfer/sec: 1.02MB Latency 50.00%: 45.12ms Latency 95.00%: 98.77ms Latency 99.00%: 145.30ms直观判断标准是 P95 和 P99 是否明显下降错误率是否归零。如果只是平均延迟下降但 P99 仍然很高说明系统中仍有少量请求吃到了很差的尾延迟需要继续排查热点线程、垃圾回收或网络重传。6.2 上线前检查清单优化要真正上线建议先过一遍清单避免改了一个参数又引入新问题。检查项具体内容是否完成网络链路ping/mtr 稳定无持续丢包和异常高延迟是 / 否系统限制文件句柄、somaxconn、端口范围已调整并验证是 / 否依赖版本压测环境和生产环境所用组件版本一致是 / 否配置外置修改项已经进入配置中心或发布配置而不是手工改服务器是 / 否日志留痕关键请求有 requestId异常日志包含完整堆栈是 / 否监控告警QPS、P99、错误率、CPU、连接池指标已接入告警是 / 否容量评估已按历史峰值 2 倍流量做过压测是 / 否回滚方案明确回滚到上个版本的步骤涉及数据库变更时有回滚脚本是 / 否6.3 短周期和长周期观察上线后不能只看压测结果。建议分三个粒度观察小时内观察错误率和 P99 延迟确认没有突发尖峰。如果在分钟级监控面板上看到错误率从 0 突然跳到 1%要立刻看当时是否有发布、限流或网络抖动。按天观察资源趋势确认新参数上线后 CPU、内存、磁盘 I/O 没有缓慢上涨。有些问题不会马上暴露比如连接回收参数不一致导致 TIME_WAIT 越来越多。按周或按月观察容量增长曲线提前评估何时需要扩容或拆分服务。优化不是一次性工作而是一个持续跟踪的过程。7. 排查误区和长期运维实践7.1 三个容易让问题更严重的操作第一个误区是一卡就重启。重启会清理掉当前线程状态和网络连接看起来症状消失了但导致问题的根因还在。正确做法是先保留现场保存 jstack、top 快照、网络抓包和错误日志再考虑重启恢复。第二个误区是只看平均延迟。平均延迟会被少数慢请求拉高也可能被大量快请求掩盖。必须同时看 P50、P95、P99 和错误率。优化前如果 P99 明显高于 P95表示存在一批异常慢的请求它们才是玩家感知到的“土豆感”来源。第三个误区是一味加机器。如果瓶颈是数据库的慢查询或网络入口带宽加应用实例反而会让压力更快打到数据库。扩容前先定位瓶颈扩容后也要通过压测确认整体吞吐确实上升。7.2 日志与监控留痕怎么做才有效要支撑快速排障日志不能只是一段字符串。建议按统一格式输出结构化日志至少要包含时间、请求 ID、调用链 ID、接口名、耗时、状态码、错误类型和关键业务参数。聚合日志平台不是必须立刻搭建但至少要保证日志分散在各服务器时可以按关键字和 requestId 检索。异常日志不要只记录error级别就完事要把调用外部服务时的目标地址、超时时间、返回码记录下来否则事后很难判断是哪一个环节超时。监控大盘至少包含四块流量指标、性能指标、资源指标和依赖指标。流量指标看 QPS、在线连接数性能指标看 P50、P95、P99、错误率资源指标看 CPU、内存、磁盘、网络依赖指标看数据库连接池、缓存命中率、第三方接口耗时。任何一个指标单独看都可能失真组合起来才能定位问题。7.3 从救火到预防的路径“土豆服务器”反复出现往往说明团队长期处于救火状态。要摆脱这个循环需要把排障经验固化成制度。建议定期在预发环境执行一次故障演练模拟网络延迟升高、数据库连接池耗尽、后端节点宕机等场景观察监控告警是否及时、接入层是否会雪崩、回滚步骤是否有效。容量评估要留出冗余。很多线上事故不是因为流量突然大到不可控而是因为常态流量已经接近容量上限一个小波动就会击穿。给 CPU、内存、带宽都定义合理水位接近水位就提前处理。值班文档要覆盖典型故障的排查链路包括如何登录跳板、如何查看日志、如何调用平台接口、如何回滚。文档不需要华丽但必须真实可执行。新人拿到文档以后能在 10 分钟内完成第一步定位才算合格。回到标题上的“爱情故事”运维工程师和一台“土豆服务器”之间谈不上浪漫但确实要长期相处。相处的方式不是靠重启和祈祷而是靠指标、工具、规范和持续压测。排查完这一套流程之后如果 P99 不再飙升、错误率归零、连接数稳定那段“互相折磨”的日子才算告一段落。对于新手最有价值的练习不是反复调大参数而是从一次完整压测开始记录每个环节的指标变化直到能准确说出“它为什么变卡”。
返回列表