接口慢怎么优化
秋水 Lv5

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之类的抓 对于网页 可以chromenetwork抓包 抓包 你能看到到底返回的错误码是啥 或者真正的耗时多久

接口 有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
2
网卡收到数据 -> 拷贝到ringbuffer -> 发硬中断 -> 硬中断标记软中断位 -> 
ksoftirq内核线程 轮训软中断位 -> 摘ringbuffer数据封装成内核结构体skb -> 送给协议层处理

送协议层之前还有tcpdump抓包的点 到协议层 先是ip层 然后tcp层 咱说抓包看到tcp握手重传了 或者慢了 是不是理论上 到协议层tcp这层 之前的都会有问题

网卡一般不是瓶颈,如果你怀疑是,你可以用 ethtool 查网卡是否开启多队列 没开 给他开了 再观察

然后协议拆包 是个耗费 cpu 的事情 你用 top命令 或者 vmstat 你观察一段时间 比如半分钟 你发现内核耗时高 硬中断也高 证明很可能请求太多 你可以看看网卡拆包 RXO 开没开 也能用ethtool配置 让网卡帮你把拆包给干了 让cpu做其他事 让网卡帮你把拆包给干了 让cpu做其他事 然后硬中断和拷贝到ringbbuffer 一般没啥可干预的

kosftirq内核线程 你有几个cpu逻辑核 就有几个线程 但是我们碰上不知道线上哪个2bksoftirqbalance服务给关了 导致就特么一个核在那处理网卡 死慢死慢的 改这玩意的 很可能那机器上 曾经用过Redis

因为redis单线程的吧 给CPU绑核 taskset redispid 固定到一个核上 让l1 l2缓存热 命中率非常高 速度会更快 这时候 你的网卡如果也在一个核上 是不是就更快了

上面那些 其实大部分没必要排查 只是说 你这么配置上了 会快 理论上 线上机器应该都配置好了

就说三次握手慢啊 然后到协议层拆包 内核基本这块没啥bug了 所以慢 有2个原因 第一 你backlog 配置太小了 第二 你重传定时是不是整大了 第三 查查你端口 是不是真有那种耗尽了

就是第三个 一般不太会出现 因为你是客户端请求 比如app或者浏览器 四元组都是不同的 第二个 重传不是问题 问题是你给整大了 这个一般也没人调 都是用系统默认的参数 所以你可以看看是不是第一个

有很多人编程的时候不知道backlog到底咋传 这个你可以当个问题 你自己去查查这玩意到底咋用 里面涉及到systctl的一些配置

三次握手 常见的问题 就是这样 比如你配置的问题 你链接太多了 netstat一查 好几十万个

链接太多了 你是要扩容不 或者你仔细想想 是不是你服务代码处理的太慢了 导致客户端请求积压了 更多的请求过来 一直排着呢

3. 接口慢

3.1. 网络IO排查

然后再说你接口慢 你从抓包看返回耗时 是能看出来的 然后咱说 发现接口慢了 你抓包是不是能看到你请求哪个接口 你即使是没文档 没人告诉你 你总能去代码搜吧 但是你光看代码 你肉眼看啥时候去 而且不同请求参数 很可能走不通代码分支 通常啊 我们认为 磁盘io 网络io是最慢的

但是咱们也不能拍脑门就说这接口慢 一定是有io导致的

那不又成酒鬼找钥匙了么 咱说慢 对慢这个得下个定义 刚才说监控里rt 里 能容忍20ms 咱们对接口的耗时有了个定义 那咱对代码耗时是不是也得有个定义

我列一下 不用记住 这玩意不好记,都是估计值 有浮动但是不会太大

1
2
3
4
5
6
7
8
9
10
1个cpu周期 就是执行指令 0.3ns
L1缓存 0.9ns-1ns
L2缓存 2.5ns-2.8ns
L3缓存 12.5ns-13ns
cpu到内存 120ns
固态硬盘 50-150微妙(记得我之前说的随机读写和顺序读写么,这个范围就很大了吧)
机械磁盘 1-10ms(同上)
公网网络传输 10ms以上 那就没准了 上百都有 比如从中国到纽约
tcp重传 1-3s
函数调用 java的我没统计过 c的大概0.4ns 系统调用(分内核版本,因为老版本用int 0x80中断,新的内核用的intel的sysenter)大概是200ns多的是十几微妙

这时候对你代码里能用的耗时有定义了吧

一般是io慢 其他的可以忽略 你要真感觉你代码指令慢 你可以用perf命令查

然后再说咱这接口 找到了 但是就是慢 不能光肉眼看代码猜 可以用arthasperf命令去查 挨个调用跟踪 也可以打火焰图 跟踪下来你就知道哪里慢了 先不说是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 heapreteent 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能扛得住的时候 oomdump文件 出来 或者一些让jvm死的error 也都会打crash日志出来

分析那个日志 也是有讲究的 dump文件mat / 而 crash日志 要么肉眼看 要么用工具

扛不住 没等gc干活 oomkiller就上手了 你连crashdump 都可能没有 尤其是容器 内核的肯定是不关心你的oom问题,肯定不会有详细的日志,他只关心他能做的保底策略 syslog里打的肯定没jvm的多 jvm不一样 他拦截了信号 拦住再打

内核的 oomkiller 会在 syslog 中打出来日志 当然这都挂了 就不只是接口慢了

正常情况下 你new一个对象 jvmglibcmalloc 这时候没分配内存呢 只是分配了个虚拟地址空间的范围 vma结构

问题是内核 oomkiller 强制杀了你 他也没你内存对象结构 他只能给你空间范围 继续说内存分配 vma 只有你在操作写的时候 才会做分配内存动作

分配不够 内核回收pagecacheblock 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.