1. 背景【多段大厂经历高级开发巨佬著作】
这个禁止抄袭!大佬亲笔写的!
比如这个接口 就代码直接上数据库里扣 先不说缓存乱七八糟的 然后咱们再说 加什么 怎么优化
ok
那这个场景下 用户投诉慢了,或者你监控报警了
咱就说 这个项目 是你刚接手的 你还不知道里面有啥 连个文档也没有 其他人都离职了
咱们就假定 现在他里面只有一个数据库 你只知道 抢红包 一点 就慢了
咱们先看怎么分析 然后再怎么往上加各种redis
还是mq
啥的
第一件事 我得找到这个接口对吧
那好 有2种情况 一种监控发现慢了,这种一般建立与咱们对这系统有所了解了,然后去看监控对吧。我说这个只是要说明 监控的几个指标,然后说完这个情况,咱们再按照啥也不知道情况下 怎么查
监控里 有3个指标很重要 第一 qps 每秒请求数 第二rt 响应耗时 第三 eps error per second 就是异常的qps 或者是错误率 错误率就是异常qps除一下总qps算 这个很简单
我们要对系统核心接口有个很明确的认识 你的单机qps
能抗多少流量 通过压测的手段,然后预期的 rt
是多少 能容忍多少 比如 20ms
以内
然后大于 20ms
不管返回的是啥 我们都可以认为是异常 当然你返回异常错误 也算异常 比如http code=5xx 4xx
这些再不同 qps
的量上 都会有不同的表现 这三个指标很重要
好 再说我们对当前啥也不知道 就知道点app
上的按钮或者页面按钮 直接1秒以上才返回成功 或者直接失败
2. 第一件事就是抓包
对于app
端 可以通过charles proxyman
之类的抓 对于网页 可以chrome
的network
抓包 抓包 你能看到到底返回的错误码是啥 或者真正的耗时多久
接口 有3个耗时指标要仔细看 第一 dns
的耗时 第二 建立链接的耗时 第三首字节到达服务器并返回的耗时
通常http接口
你可以copy
一个curl 命令
出来 进行重放 但是要注意是否有损 你接口没做幂等重放会不会有问题 比如脏数据
包括curl命令 也能打印出上面说的不同阶段耗时
2.1. 初步分析 DNS
以及路由问题
首先 你可能服务器一切正常 但是客户端就是慢 很有可能是因为dns
慢 也有可能因为建立链接慢
记得我之前说过 客户端请求打到你服务器 你作为个客户端 请求 redis
然后就一个端口 从你服务器到 redis
tcp
链接四元组端口耗尽 导致选端口慢么
其实这不也是客户端到服务器 发送数据之前的问题么 对于远端的服务器可能看着一切正常 但是客户端键连接慢 dns
慢
然后再看建联的 你抓包是能看出三次握手的 当然chrome charles proxyman
是看不到的 可以用tcpdump wireshark
能很清楚看到 比如你三次握手 有重传 或者数据包来回慢 这都能看到
![8dcf135779e76df541a6d33db9aee82](D:\Git\fanxy_knowledge_graph\Fanxy的学习笔记\8 面试场景题\1 一个红包接口慢怎么排查.assets\8dcf135779e76df541a6d33db9aee82.png)
这里dns
也能看到 咱假设 dns
慢了 那没办法 让用户换dns
或者联系运营商 然后说三次握手 慢了 可能这个与服务器通信过程中 有路由丢包
好 既然你自己机器抓包能发现 用 traceroute
命令看
![f9016e27897a8c91aee728bc59e6695](D:\Git\fanxy_knowledge_graph\Fanxy的学习笔记\8 面试场景题\1 一个红包接口慢怎么排查.assets\f9016e27897a8c91aee728bc59e6695.png)
这不经过多少都打出来了么 你看你要中间路由凉了 还是找运营商啥的
2.2. 服务器响应是否慢?网卡问题?redis
?
然后咱们再说假设路由过程一切正常 那可能你服务器响应慢了 或者你看你数据response
慢 路由没问题 那就是你服务器慢
好 现在咱们跟踪到服务器了 当然 大多数路由过程不会慢 所以很多人 直接查服务器 但是这属于所谓的街灯讹方法 就是说一个喝醉的酒鬼 钥匙丢了 他只记得好像在有光的地方掏出钥匙 随后就没了 那他就跑到各个街灯下面找钥匙 这不对吧
再说服务器握手重传或者耗时慢 如果你熟悉 或者看我前两天发的epoll
和同步阻塞区别 开始不是说了怎么收数据包的么
1 | 网卡收到数据 -> 拷贝到ringbuffer -> 发硬中断 -> 硬中断标记软中断位 -> |
送协议层之前还有tcpdump
抓包的点 到协议层 先是ip层 然后tcp层 咱说抓包看到tcp握手重传了 或者慢了 是不是理论上 到协议层tcp这层 之前的都会有问题
网卡一般不是瓶颈,如果你怀疑是,你可以用 ethtool
查网卡是否开启多队列 没开 给他开了 再观察
然后协议拆包 是个耗费 cpu
的事情 你用 top命令
或者 vmstat
你观察一段时间 比如半分钟 你发现内核耗时高 硬中断也高 证明很可能请求太多 你可以看看网卡拆包 RXO
开没开 也能用ethtool
配置 让网卡帮你把拆包给干了 让cpu
做其他事 让网卡帮你把拆包给干了 让cpu
做其他事 然后硬中断和拷贝到ringbbuffer
一般没啥可干预的
kosftirq
内核线程 你有几个cpu
逻辑核 就有几个线程 但是我们碰上不知道线上哪个2b
把 ksoftirq
的balance
服务给关了 导致就特么一个核在那处理网卡 死慢死慢的 改这玩意的 很可能那机器上 曾经用过Redis
因为redis
单线程的吧 给CPU绑核 taskset
redis
的 pid
固定到一个核上 让l1 l2
缓存热 命中率非常高 速度会更快 这时候 你的网卡如果也在一个核上 是不是就更快了
上面那些 其实大部分没必要排查 只是说 你这么配置上了 会快 理论上 线上机器应该都配置好了
就说三次握手慢啊 然后到协议层拆包 内核基本这块没啥bug了 所以慢 有2个原因 第一 你backlog
配置太小了 第二 你重传定时是不是整大了 第三 查查你端口 是不是真有那种耗尽了
就是第三个 一般不太会出现 因为你是客户端请求 比如app
或者浏览器
四元组都是不同的 第二个 重传不是问题 问题是你给整大了 这个一般也没人调 都是用系统默认的参数 所以你可以看看是不是第一个
有很多人编程的时候不知道backlog
到底咋传 这个你可以当个问题 你自己去查查这玩意到底咋用 里面涉及到systctl
的一些配置
三次握手 常见的问题 就是这样 比如你配置的问题 你链接太多了 netstat
一查 好几十万个
链接太多了 你是要扩容不 或者你仔细想想 是不是你服务代码处理的太慢了 导致客户端请求积压了 更多的请求过来 一直排着呢
3. 接口慢
3.1. 网络IO排查
然后再说你接口慢 你从抓包看返回耗时 是能看出来的 然后咱说 发现接口慢了 你抓包是不是能看到你请求哪个接口 你即使是没文档 没人告诉你 你总能去代码搜吧 但是你光看代码 你肉眼看啥时候去 而且不同请求参数 很可能走不通代码分支 通常啊 我们认为 磁盘io
网络io
是最慢的
但是咱们也不能拍脑门就说这接口慢 一定是有io
导致的
那不又成酒鬼找钥匙了么 咱说慢 对慢这个得下个定义 刚才说监控里rt 里 能容忍20ms 咱们对接口的耗时有了个定义 那咱对代码耗时是不是也得有个定义
我列一下 不用记住 这玩意不好记,都是估计值 有浮动但是不会太大
1 | 1个cpu周期 就是执行指令 0.3ns |
这时候对你代码里能用的耗时有定义了吧
一般是io
慢 其他的可以忽略 你要真感觉你代码指令慢 你可以用perf
命令查
然后再说咱这接口 找到了 但是就是慢 不能光肉眼看代码猜 可以用arthas
的perf
命令去查 挨个调用跟踪 也可以打火焰图 跟踪下来你就知道哪里慢了 先不说是gc
的事 因为gc
是额外的 你代码调用路径慢 你一目了然了
火焰图有2种 一种onlinecpu
一种offlinecpu
onlinecpu
你能看出来你代码哪里慢了 offlinecpu
是让你看到 你代码放弃执行权限后 哪里慢了 比如你做io
内核给你线程挂起了 io
里面慢
3.2. GC排查
内存不够了 堆内存 fullgc stw
直接卡你业务线程
拉gc
日志 gceasy在线网站
heaphero 在线网站
perfma社区在线网站
如果怕泄露服务器信息啥的 用gcviwer离线的
也可以自己肉眼看 gclog
发现了频繁fullgc
怎么算频繁 面试总会问 那玩意一周有一次还算合理
你要发现1分钟1次 几秒钟1次 那肯定频繁了 你服务肯定卡 你cpu
肯定上来
然后你就得分析堆内存了 jmap
dump
我白天说了咋dump
了吧
抢救现场 一定要快 不能等dump
文件拉下来再用 mat
打开 太慢了
dump
文件咱拿出来了 用mat
打开 里面有2个关键的指标 shallow
heap
和reteent
heap
没记错好像是这么拼的
所谓的深堆和潜堆 这俩 你们自己看 是啥玩意
除了堆 还有stackoverflow
问题 这个一般就直接返回错误了 这不是慢 这个就很明显了 因为有异常信息
主要是另外一个问题 我之前在群里问过 死锁 活锁 死循环 哪个会把CPU打满 这是关系到你接口慢不慢
死锁不会打满cpu
为啥 内核都给线程挂起了 或者jvm
给放等待队列了 都不工作了
cpu
不会满 只会非常低 比平常要低 你的服务还请求不动
死循环不一定会高 大部分会 死循环耗费cpu
在于你里面别的代码动作 如果你是个单核的 会占满cpu
你要多核呢
windows
早期内核调度器 一个任务死循环直接给机器干死了 Linux
不会
如果后台接口的超时时间无限长,是不是浏览器请求接口就会一直请求 直到后台response或者报错?
差不多 有默认超时时间的
mat
jmap
jstack
arthas
jstat
jcmd
这都是常用的工具 看gclog
jvm
一般性的问题 都可以用这些查出来 jcmd
甚至可以查堆外内存泄露
但是top
vmstat
这些 指标有很多不一样 内存不够 gc
上手干活了 但是还有直接oom
jvm
能扛得住的时候 oom
会dump文件
出来 或者一些让jvm
死的error
也都会打crash日志
出来
分析那个日志 也是有讲究的 dump文件
用mat
/ 而 crash日志
要么肉眼看 要么用工具
扛不住 没等gc
干活 oomkiller
就上手了 你连crash
和 dump
都可能没有 尤其是容器 内核的肯定是不关心你的oom
问题,肯定不会有详细的日志,他只关心他能做的保底策略 syslog
里打的肯定没jvm
的多 jvm
不一样 他拦截了信号 拦住再打
内核的 oomkiller
会在 syslog
中打出来日志 当然这都挂了 就不只是接口慢了
正常情况下 你new
一个对象 jvm
调glibc
的malloc
这时候没分配内存呢 只是分配了个虚拟地址空间的范围 vma
结构
问题是内核 oomkiller
强制杀了你 他也没你内存对象结构 他只能给你空间范围 继续说内存分配 vma
只有你在操作写的时候 才会做分配内存动作
分配不够 内核回收pagecache
和block buffer
如果回收不了 有swap
分区或者swap
文件 上swap
写 这时候你的接口就慢了
毕竟是磁盘操作 如果swap
没有 直接oomkiller
杀你进程
好 目前能把一般性的接口慢 说差不多了
4. 落实到抢红包业务
如果不是gc
不是内存 不是握手 不是dns乱七八糟的 就单纯业务代码的问题
你用arthas
profile
是不是就能看出来了
你就能发现 是开多线程加速 还是用mq
还是用redis
这些方案 不就是自然而然就能想到了
你发现netstat
一查 单机qps
太多了 是不是得考虑上负载均衡 加机器
你一查 profile
里打的操作数据库 慢 是不是可以优化 sql
了
或者是不是可以上 redis
或者 mq
了
就是能先优化就优化 真达到瓶颈了 再改架构 你引入redis
引入mq
是不是引入了成本 学习成本和机器部署成本
你是不是得改造你的架构和代码 磁盘慢了 iostat
能看 是iops
太高导致的 排队 还是就是读写慢 机械磁盘么 是的话 能不能异步写 能不能顺序追加写
实在不行换固态行不 但是引入成本 现在的企业 宁可耗费大人力改造 也不希望花钱 数据库慢 好 读的慢 慢查询优化 索引 这些老生常谈
1 | 写的慢 第一 考虑buffer pool能不能调大 第二 考虑binlog和redlog刷盘配置能不能改 第三 考虑master是不是到达瓶颈了 第四 观察锁 是不是锁导致排队了 有没有死锁 第五 有没有别的任务 比如备份数据库 或者数据库服务器上还部署了其他的导致的 第六 查数据库的磁盘io 第七 你的索引是不是创建不合理 数据库在大量的写的情况下 频繁更新索引 |
网络io
慢 排查思路就是上面那些 因为还是你服务器此时作为了一个客户端
当然数据库锁 还有那些什么大事务不释放占用你资源、慢查询拖垮整个库、你写呢 有大哥在那改索引 改表结构啥的(虽然有onlineddl
但是也有不是online
的)、或者有大哥直接给你锁表了
- Post title:接口慢怎么优化
- Post author:秋水
- Create time:2024-10-01 14:27:32
- Post link:tai769.github.io2024/10/01/接口慢怎么优化/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.