gitbook/高楼的性能工程实战课/docs/365426.md
2022-09-03 22:05:03 +08:00

246 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 14 | 用户信息查询:如何解决网络软中断瓶颈问题?
你好,我是高楼。
这节课我们接着来整另一个接口用户信息查询。通过这个接口我们一起来看看当网络软中断过高时会对TPS产生什么样的影响。其实对于这一点的判断在很多性能项目中都会出现而其中的难点就在于很多人都无法将软中断跟响应时间慢和TPS所受到的影响关联起来。今天我就带你来解决这个问题。
同时我也会给你讲解如何根据硬件配置及软件部署情况做纯网络层的基准验证以确定我们判断方向的正确性进而提出具有针对性的优化方案。而我们最终优化的效果会通过TPS对比来体现。
## 压力数据
我们先来看用户信息查询的压力数据情况如何。因为我们现在测试的是单接口而用户信息查询又需要有登录态所以我们要先跑一部分Token数据出来再执行用户信息查询接口。
准备好Token数据后第一次用户信息查询如下
![](https://static001.geekbang.org/resource/image/fd/7b/fd2eb1c3a0fa2b5520a1bc93f986a37b.png)
这个步骤只是试验一下持续时间长是为了查找问题。从上图来看这个接口的起点不错已经达到750左右。
不过性能瓶颈也比较明显响应时间随着压力线程的增加而增加了TPS也达到了上限。对于这样的接口我们可以调优也可以不调优因为这个接口当前的TPS可以达到我们的要求。只不过本着“**活着不就是为了折腾****”**的原则,我们还是要分析一下这个接口的瓶颈到底在哪里。
还是按照我们之前讲过的分析思路,下面我们来分析这个问题。
## 看架构图
从链路监控工具中,我们拉出架构图来,这样简单直接,又不用再画图了,真的是懒人必知技能。
![](https://static001.geekbang.org/resource/image/5c/b9/5c44dfd49128498753cd1e7dfbdb28b9.png)
从上图可以知道用户信息查询的路径是User - Gateway - Member - MySQL。
你也许会问图中不是还有Redis、MongoDB、Monitor吗是的这些我们也要记在脑子里。这个接口用到了Redis如果接口有问题变慢了我们也要分析MongoDB并没有用上所以我们不管它Monitor服务是Spring Boot Admin服务我们也暂且不管它后面需要用到的时候再说。
注意,这一步是分析的铺垫,是为了让我们后面分析时不会混乱。
## 拆分响应时间
在场景数据中,我们明显看到响应时间慢了,那我们就要知道慢在了哪里。我们根据上面的架构图知道了用户信息查询接口的路径,现在就要拆分这个响应时间,看一看每一段消耗了多长时间。
如果你有APM工具那可以直接用它查看每一段消耗的时间。如果你没有也没关系只要能把架构图画出来并把时间拆分了就行不管你用什么招。
另外我啰嗦一句请你不要过分相信APM工具厂商的广告咱们还是要看疗效。在追逐技术的同时我们也需要理智地判断到底是不是需要。
具体的拆分时间如下:
* User -Gateway
![](https://static001.geekbang.org/resource/image/f2/07/f2bf1bd9788d1e76f8e35622ea848907.png)
* Gateway上消耗的时间
![](https://static001.geekbang.org/resource/image/5f/20/5f64cyy3efd8f411ebdfcb293dd5d920.png)
* Gateway -Member
![](https://static001.geekbang.org/resource/image/8b/y4/8b992f2331135edacf0e4b93a9d19yy4.png)![](https://static001.geekbang.org/resource/image/43/f4/432ed240c57dcd81c1c13a23yy750cf4.png)
* Member上消耗的时间
![](https://static001.geekbang.org/resource/image/ae/4b/ae85fb6af7324a24d2abf2f8e679664b.png)
* Member到DB的时间
![](https://static001.geekbang.org/resource/image/da/e0/dac8d5b2c1eb5fb2c628d118567bfce0.png)
我把上述拆分后的时间都整理到我们的架构图中:
![](https://static001.geekbang.org/resource/image/bb/13/bb3676e3b24053b5b489b95012dd0613.png)
看到这张图思路变得特别清晰了有没有根据上图的时间拆分我们明显看到Member服务上消耗时间更多一点所以下一步我们去关注Member服务。
## 全局监控分析
还是一样,我们先看全局监控:
![](https://static001.geekbang.org/resource/image/7e/22/7e24929e3bb2baeaa00be336d6545d22.png)
其中worker-8的CPU用得最多我们先从这里下手。
这里我要跟你强调一下,**在全局监控的思路中,不是说我们看了哪些数据,而是我们要去看哪些数据**。这时候你就必须先有一个全局计数器的东西。比如说在Kubernetes里我们就要有这样的思路
![](https://static001.geekbang.org/resource/image/7c/e8/7c76b4796a4b28f2165b7fa4366d42e8.jpg)
也就是说,**我们要先把全局监控的计数器都罗列出来,然后再一个一个查去**。
其实,这里面不止是罗列那么简单,它还要有相应的逻辑。那怎么弄懂这个逻辑呢?这就要依赖于性能分析人员的基础知识了。我经常说,要想做全面的性能分析,就必须具备计算机基础知识,而这个知识的范围是很大的。之前我画过一张图,现在我做了一些修正,如下所示:
![](https://static001.geekbang.org/resource/image/08/49/08978ac7ecc8ba84045d15c48f5b7e49.jpg)
图中这些内容,都是我们做性能分析时会遇到的东西。有人可能会说,这些已经远远超出性能工程师的技能范围了。所以我要再强调一下,我讲的一直都是性能工程。在整个项目的性能分析中,我并不限定技术的范围,只要是用得上,我们都需要拿出来分析。
前面我们说是worker-8上的CPU资源用得最多所以我们来查一下被测的服务也就是Member服务是不是在worker-8上。
![](https://static001.geekbang.org/resource/image/ef/9f/ef618ccfedfcde3c16eaafd68710e89f.png)
从上图看Member服务确实是在worker-8上。
那下一步我们就要进到这个节点查看一下。查看之后如果全是us cpu消耗那我觉得这个基准测试就可以结束了。因为对于一个应用来说us cpu高本来就是非常合理的情况。
之前一个做第三方测试的人跑过来跟我说甲方爸爸不喜欢看到CPU使用率太高让他想尽一切办法把CPU降下来。可是他没有什么招所以就来问我该怎么办。
我问他测试的目标是什么。他回答客户并不关心TPS啥的只说要把CPU降下来。我说这简单你把压力降下来CPU不就降下来了吗本来以为只是一句调侃的话结果他真去做了并且还被客户接受了后来我反思了一下因为自己错误引导了性能行业的发展方向。
从职业的角度来说,我们对一些不懂的客户,最好要有一个良好的沟通,用对方能听懂的语言来解释。不过,在不该让步的时候,我们也不能让步。这才是专业的价值,不能是客户要什么,我们就给什么。
现在我们来看一下这个节点的top数据
```
[root@k8s-worker-8 ~]# top
top - 02:32:26 up 1 day, 13:56, 3 users, load average: 26.46, 22.37, 14.54
Tasks: 289 total, 1 running, 288 sleeping, 0 stopped, 0 zombie
%Cpu0 : 73.9 us, 9.4 sy, 0.0 ni, 3.5 id, 0.0 wa, 0.0 hi, 12.5 si, 0.7 st
%Cpu1 : 69.8 us, 12.5 sy, 0.0 ni, 4.3 id, 0.0 wa, 0.0 hi, 12.8 si, 0.7 st
%Cpu2 : 71.5 us, 12.7 sy, 0.0 ni, 4.2 id, 0.0 wa, 0.0 hi, 10.9 si, 0.7 st
%Cpu3 : 70.3 us, 11.5 sy, 0.0 ni, 6.1 id, 0.0 wa, 0.0 hi, 11.5 si, 0.7 st
KiB Mem : 16266296 total, 3803848 free, 6779796 used, 5682652 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 9072592 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
30890 root 20 0 7791868 549328 15732 S 276.1 3.4 23:17.90 java -Dapp.id=svc-mall-member -javaagent:/opt/skywalking/agent/sky+
18934 root 20 0 3716376 1.6g 18904 S 43.9 10.3 899:31.21 java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitFor+
1059 root 20 0 2576944 109856 38508 S 11.1 0.7 264:59.42 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-
1069 root 20 0 1260592 117572 29736 S 10.8 0.7 213:48.18 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.
15018 root 20 0 5943032 1.3g 16496 S 6.9 8.6 144:47.90 /usr/lib/jvm/java-1.8.0-openjdk/bin/java -Xms2g -Xmx2g -Xmn1g -Dna+
4723 root 20 0 1484184 43396 17700 S 5.9 0.3 89:53.45 calico-node -felix
```
在这个例子中我们看到si cpu软中断消耗的CPU有10%多其实这只是一个瞬间值在不断跳跃的数据中有很多次数据都比这个值大说明si消耗的CPU有点高了。对于这种数据我们就要关心一下了。
## 定向监控分析
我们进一步来看软中断的变化既然软中断消耗的CPU高那必然是要看一下软中断的计数器了
![](https://static001.geekbang.org/resource/image/45/5d/459c1yy79b39824f7b35d7e2c61ca85d.png)
上图是一张瞬间的截图而在实际的观察过程中我们是要多看一会儿时间的。请你注意图中这些有白底的数字在观察中这些数值增加的越大说明中断越高。而我在观察的过程中看到的是NET\_RX变化的最大。
现在从si cpu高到NET\_RX中断多的逻辑基本上清楚了因为NET\_RX都是网络的接收所以NET\_RX会不断往上跳。
不过请你注意这个中断即使是正常的也需要不断增加。我们要判断它合理不合理一定要结合si cpu一起来看。并且在网络中断中不止是软中断硬中断也会不断增加。
从上图来看,网络中断已经均衡了,没有单队列网卡的问题。我们再看一下网络带宽。
![](https://static001.geekbang.org/resource/image/cf/09/cfe3cfb6835a90f97d64589090616b09.png)
总共用了50Mb的带宽中断就已经达到10%,也就是说带宽没有完全用满,可是中断已经不低了,这说明我们的数据包中还是小包居多。
于是我们做如下调整。调整的方向 就是增加队列长度和缓冲区大小,让应用可以接收更多的数据包。
```
-- 增加网络的队列长度
net.core.netdev_max_backlog = 10000 原值1000
- 增加tomcat的队列长度为10000(原值1000)
server:
port: 8083
tomcat
accept-count: 10000
-- 改变设备一次可接收的数据包数量
net.core.dev_weight = 128 原值64
-- 控制socket 读取位于等待设备队列中数据包的微秒数
net.core.busy_poll = 100 原值0
-- 控制 socket 使用的接收缓冲区的默认大小
net.core.rmem_default = 2129920 原值212992
net.core.rmem_max = 2129920 原值212992
-- 繁忙轮询
net.core.busy_poll = 100
这个参数是用来控制了socket 读取位于等待设备队列中数据包的微秒数
```
一顿操作猛如虎之后原本满怀希望然而再次查了TPS曲线之后发现并没有什么卵用让我们把一首《凉凉》唱出来。
我仔细想了一遍发送和接收数据的逻辑。既然上层应用会导致us cpu高而si cpu高是因为网卡中断多引起的那我们还是要从网络层下手。所以我做了网络带宽能达到多高的验证。我先列一下当前的硬件配置。
![](https://static001.geekbang.org/resource/image/56/5f/5695abb87f22c463a5f3ffef5475015f.jpg)
我们通过iperf3直接测试网络试验内容如下
![](https://static001.geekbang.org/resource/image/fb/36/fb30edf2e6e5768631e7fa15d40e6a36.jpg)
从上面的数据可以看到在不同的层面进行纯网络测试si是有很大区别的。当网络流量走了KVM+Kubernetes+Docker的结构之后网络损失居然这么高si cpu也上升了很多。
这也解释了为什么现在很多企业放弃虚拟化直接用物理机来跑Kubernetes了。
由于当前K8s用的是Calico插件中的IPIP模式考虑到BGP模式的效率会高一些我们把IPIP模式改为BGP。这一步也是为了降低网络接收产生的软中断。
那IPIP和BGP到底有什么区别呢对于IPIP来说它套了两次IP包相当于用了一个IP层后还要用另一个IP层做网桥。在通常情况下IP是基于MAC的不需要网桥而BGP是通过维护路由表来实现对端可达的不需要多套一层。但是BGP不是路由协议而是矢量性协议。关于IPIP和BGP更多原理上的区别如果你不清楚我建议你自学一下相关的网络基础知识。
我们把IPIP修改为BGP模式之后先测试下纯网络的区别做这一步是为了看到在没有应用压力流量时网络本身的传输效率如何
![](https://static001.geekbang.org/resource/image/b7/6a/b7a3aec6f1f2630e50f1a8958cd3486a.png)
根据上面的测试结果,将带宽在不同的网络模式和包大小时的具体数值整理如下:
![](https://static001.geekbang.org/resource/image/b2/2b/b2578dc107ef96f9111380d3a4eacc2b.jpg)
可以看到BGP的网络能力确实要强一些差别好大呀。
我们再接着回去测试下接口,结果如下:
![](https://static001.geekbang.org/resource/image/f9/eb/f982b49f5f5d4d8fb654a49770aa0eeb.png)
再看软中断看一下BGP模式下的软中断有没有降低
```
top - 22:34:09 up 3 days, 55 min, 2 users, load average: 10.62, 6.18, 2.76
Tasks: 270 total, 2 running, 268 sleeping, 0 stopped, 0 zombie
%Cpu0 : 51.6 us, 11.5 sy, 0.0 ni, 30.0 id, 0.0 wa, 0.0 hi, 6.6 si, 0.3 st
%Cpu1 : 54.4 us, 9.4 sy, 0.0 ni, 28.2 id, 0.0 wa, 0.0 hi, 7.7 si, 0.3 st
%Cpu2 : 55.9 us, 11.4 sy, 0.0 ni, 26.9 id, 0.0 wa, 0.0 hi, 5.9 si, 0.0 st
%Cpu3 : 49.0 us, 12.4 sy, 0.0 ni, 32.8 id, 0.3 wa, 0.0 hi, 5.2 si, 0.3 st
KiB Mem : 16266296 total, 7186564 free, 4655012 used, 4424720 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 11163216 avail Mem
```
## 优化效果
通过上面的调整结果我们看到了软中断确实降低了不少但是我们还是希望这样的优化体现到TPS上来所以我们看一下优化之后TPS的效果。
![](https://static001.geekbang.org/resource/image/9c/f3/9cfdc1287577f637e4e5912961f64df3.png)
si cpu有降低
![](https://static001.geekbang.org/resource/image/20/04/2074ec8982f130c9ee8b6cf572ff7b04.png)
## 总结
当我们看到一个接口已经满足了业务要求时,从成本上来说,我们不应该花时间再去收拾它。但是,**从技术上来说,我们对每一个接口的性能结果,都要达到“知道最终瓶颈在哪里”的程度**。这样才方便我们在后续的工作中继续优化。
在这节课的例子中我们从si cpu开始分析经过软中断查找和纯网络测试定位到了Kubernetes的网络模式进而我们选择了更加合理的网络模式。整个过程穿过了很长的链路而这个思维也是在我在宣讲中一贯提到的“**证据链**”。
最后,我还是要强调一遍,**性能分析一定要有证据链,没有证据链的性能分析就是耍流氓。**我们要做正派的老司机。
## 课后作业
我给你留两道题,请你思考一下:
1. 为什么看到NET\_RX中断高的时候我们会想到去测试一下纯网络带宽
2. 你能总结一下,这节课案例的证据链吗?
记得在留言区和我讨论、交流你的想法,每一次思考都会让你更进一步。
如果你读完这篇文章有所收获,也欢迎你分享给你的朋友,共同学习进步。我们下一讲再见!