14 | 用户信息查询:如何解决网络软中断瓶颈问题?

你好,我是高楼。

这节课我们接着来整另一个接口:用户信息查询。通过这个接口,我们一起来看看,当网络软中断过高时,会对TPS产生什么样的影响。其实对于这一点的判断,在很多性能项目中都会出现,而其中的难点就在于,很多人都无法将软中断跟响应时间慢和TPS所受到的影响关联起来。今天我就带你来解决这个问题。

同时,我也会给你讲解如何根据硬件配置及软件部署情况,做纯网络层的基准验证,以确定我们判断方向的正确性,进而提出具有针对性的优化方案。而我们最终优化的效果,会通过TPS对比来体现。

压力数据

我们先来看用户信息查询的压力数据情况如何。因为我们现在测试的是单接口,而用户信息查询又需要有登录态,所以我们要先跑一部分Token数据出来,再执行用户信息查询接口。

准备好Token数据后,第一次用户信息查询如下:

这个步骤只是试验一下,持续时间长是为了查找问题。从上图来看,这个接口的起点不错,已经达到750左右。

不过,性能瓶颈也比较明显:响应时间随着压力线程的增加而增加了,TPS也达到了上限。对于这样的接口,我们可以调优,也可以不调优,因为这个接口当前的TPS可以达到我们的要求。只不过,本着“活着不就是为了折腾的原则,我们还是要分析一下这个接口的瓶颈到底在哪里。

还是按照我们之前讲过的分析思路,下面我们来分析这个问题。

看架构图

从链路监控工具中,我们拉出架构图来,这样简单直接,又不用再画图了,真的是懒人必知技能。

从上图可以知道,用户信息查询的路径是User - Gateway - Member - MySQL。

你也许会问,图中不是还有Redis、MongoDB、Monitor吗?是的,这些我们也要记在脑子里。这个接口用到了Redis,如果接口有问题,变慢了,我们也要分析;MongoDB并没有用上,所以我们不管它;Monitor服务是Spring Boot Admin服务,我们也暂且不管它,后面需要用到的时候再说。

注意,这一步是分析的铺垫,是为了让我们后面分析时不会混乱。

拆分响应时间

在场景数据中,我们明显看到响应时间慢了,那我们就要知道慢在了哪里。我们根据上面的架构图知道了用户信息查询接口的路径,现在就要拆分这个响应时间,看一看每一段消耗了多长时间。

如果你有APM工具,那可以直接用它查看每一段消耗的时间。如果你没有,也没关系,只要能把架构图画出来,并把时间拆分了就行,不管你用什么招。

另外,我啰嗦一句,请你不要过分相信APM工具厂商的广告,咱们还是要看疗效。在追逐技术的同时,我们也需要理智地判断到底是不是需要。

具体的拆分时间如下:

  • User -Gateway

  • Gateway上消耗的时间

  • Gateway -Member

  • Member上消耗的时间

  • Member到DB的时间

我把上述拆分后的时间都整理到我们的架构图中:

看到这张图,思路变得特别清晰了,有没有?根据上图的时间拆分,我们明显看到Member服务上消耗时间更多一点,所以下一步我们去关注Member服务。

全局监控分析

还是一样,我们先看全局监控:

其中,worker-8的CPU用得最多,我们先从这里下手。

这里我要跟你强调一下,在全局监控的思路中,不是说我们看了哪些数据,而是我们要去看哪些数据。这时候,你就必须先有一个全局计数器的东西。比如说在Kubernetes里,我们就要有这样的思路:

也就是说,我们要先把全局监控的计数器都罗列出来,然后再一个一个查去

其实,这里面不止是罗列那么简单,它还要有相应的逻辑。那怎么弄懂这个逻辑呢?这就要依赖于性能分析人员的基础知识了。我经常说,要想做全面的性能分析,就必须具备计算机基础知识,而这个知识的范围是很大的。之前我画过一张图,现在我做了一些修正,如下所示:

图中这些内容,都是我们做性能分析时会遇到的东西。有人可能会说,这些已经远远超出性能工程师的技能范围了。所以我要再强调一下,我讲的一直都是性能工程。在整个项目的性能分析中,我并不限定技术的范围,只要是用得上,我们都需要拿出来分析。

前面我们说是worker-8上的CPU资源用得最多,所以我们来查一下被测的服务,也就是Member服务,是不是在worker-8上。

从上图看,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高,那必然是要看一下软中断的计数器了:

上图是一张瞬间的截图,而在实际的观察过程中,我们是要多看一会儿时间的。请你注意图中这些有白底的数字,在观察中,这些数值增加的越大说明中断越高。而我在观察的过程中,看到的是NET_RX变化的最大。

现在,从si cpu高到NET_RX中断多的逻辑基本上清楚了:因为NET_RX都是网络的接收,所以NET_RX会不断往上跳。

不过,请你注意,这个中断即使是正常的,也需要不断增加。我们要判断它合理不合理,一定要结合si cpu一起来看。并且在网络中断中,不止是软中断,硬中断也会不断增加。

从上图来看,网络中断已经均衡了,没有单队列网卡的问题。我们再看一下网络带宽。

总共用了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高是因为网卡中断多引起的,那我们还是要从网络层下手。所以,我做了网络带宽能达到多高的验证。我先列一下当前的硬件配置。

我们通过iperf3直接测试网络,试验内容如下:

从上面的数据可以看到,在不同的层面进行纯网络测试,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模式之后,先测试下纯网络的区别,做这一步是为了看到在没有应用压力流量时,网络本身的传输效率如何:

根据上面的测试结果,将带宽在不同的网络模式和包大小时的具体数值整理如下:

可以看到,BGP的网络能力确实要强一些,差别好大呀。

我们再接着回去测试下接口,结果如下:

再看软中断,看一下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的效果。

si cpu有降低:

总结

当我们看到一个接口已经满足了业务要求时,从成本上来说,我们不应该花时间再去收拾它。但是,从技术上来说,我们对每一个接口的性能结果,都要达到“知道最终瓶颈在哪里”的程度。这样才方便我们在后续的工作中继续优化。

在这节课的例子中,我们从si cpu开始分析,经过软中断查找和纯网络测试,定位到了Kubernetes的网络模式,进而我们选择了更加合理的网络模式。整个过程穿过了很长的链路,而这个思维也是在我在宣讲中一贯提到的“证据链”。

最后,我还是要强调一遍,性能分析一定要有证据链,没有证据链的性能分析就是耍流氓。我们要做正派的老司机。

课后作业

我给你留两道题,请你思考一下:

  1. 为什么看到NET_RX中断高的时候,我们会想到去测试一下纯网络带宽?
  2. 你能总结一下,这节课案例的证据链吗?

记得在留言区和我讨论、交流你的想法,每一次思考都会让你更进一步。

如果你读完这篇文章有所收获,也欢迎你分享给你的朋友,共同学习进步。我们下一讲再见!

精选留言

  • 分清云淡

    2021-06-04 16:50:36

    很好的案例。但是我有几点不同的意见和解读:
    1)si% 应该只和包的数量正相关,BGP只是比IPIP多了少了一次封包解包;当然IPIP包的payload肯定要小,极少部分在IPIP模式下的2个包到BGP 合并到了1个,这样才能降si%
    2)优化后TPS 提升了但是 US%却下降了,这么不太符合逻辑

    我认为改成BGP后,用在网络封包解包的CPU去掉了,这样让出了更多的CPU给US,这才是这次优化的根因,si%只是顺带的一个极小的因素 :)
    作者回复

    1. 你的理解是没错的。这和文中的描述似乎也没有冲突。后面这句“我认为改成BGP后,用在网络封包解包的CPU去掉了,这样让出了更多的CPU给US,这才是这次优化的根因,si%只是顺带的一个极小的因素”,不知道是我们思路上的不同还是对我们对中断的理解不同,在网络中的IP、TCP之间的数据传递是要靠中断来实现的,你说的封包解包的减少,也是中断的减少。所以SI CPU不是顺带的因素。
    2. 这个是具体的监控数据,我并没有更改。并且这个也是符合逻辑的。你可以加我微信,我们详细讨论。

    2021-06-05 22:32:03

  • Geek_c2089d

    2021-10-21 11:54:16

    为什么看到 NET_RX 中断高的时候,我们会想到去测试一下纯网络带宽?
    回答:我们的NET_rx的中断高就说明了我们的系统在不断的做网络处理把包给上一层,然而我们在查看当前流量的时候我们发现使用的流量并不大。出现这情况是可能系统都在处理小的网络包,才会造成高频率的网络请求,但是上文已经对队列缓存都设置大了系统能处理合拼更大的包了,但是我们的处理包的频率还是不高,那么只能说我们的网络链路吞吐量就限制在这里,分析了系统中k8s的网络模型,瓶颈在系统接收包后的处理,是这样理解吗?
    作者回复

    逻辑上似乎是合理的哟。

    2021-10-21 17:29:49

  • 道长

    2021-04-23 19:35:41

    先通过top,查看到si高,再通过cat /proc/soft…(查看导致软中断),查询这条的时候想看动态的变化情况,在命令前加watch cat…,查看到是RX(网络接收导致),再查到中断消耗的带宽大小,并不大,进而想到是是由于网络小包导致,后面有点没太理解为什么想到了网络模式了问题。
    作者回复

    因为在这样的结构中。网络消耗不多,中断又高,所以,我们要查一下网络中断为什么那么高。对于底层网络的传输来说,中断是为了让数据可以正常让上层应用接收而用来一层层往上通知用的。
    我们用的是KVM上面部署了K8s,k8s中的网络插件是有不同的网络模式的,所以这里只要想清楚这个层级关系,就明白了为什么要看网络模式了。对于k8s的优化,网络是非常重要的一块内容,在学习k8s的时候,你就会知道k8s网络模式有不同的实现方式,而这些实现方式的区别会导致中断数量有不同。

    2021-04-24 10:53:36

  • Geek_f93234

    2021-06-07 10:03:25

    老师,请教2个问题:
    1.拆分响应时间中,Gateway -Member 中的两个图的右上角:侦察端 ,服务端,客户端这3个怎么理解?
    2.为什么Gateway -Member之间消耗的时间取的是服务端消耗的40ms,而不是客户端消耗的60ms呢?
    作者回复

    1. 这个问题,你先查一下skywalking的具体知识吧。
    2. 标识的就是被调用方的时间。

    2021-06-07 12:38:20

  • jy

    2021-07-28 14:29:31

    老师,请问下,多小算小包?如何判断出来是小包的?
    作者回复

    以太网包大小从64-1518字节。抓包就能看到大小。

    2021-07-28 19:59:32

  • Beyond

    2021-05-23 10:35:36

    老师,问一个小白问题就是:
    1. 用户信息查询,我要先做登录的脚本,拿到token,然后在用户信息查询脚本中对应使用,从而才能获得对应用户信息,那在这个基准场景中,登录和获取用户信息脚本要同时跑,那这对单业务用户查询压测有影响吗,这样串联的话,有点感觉像容量场景,可能我对基准场景压测做法就是单接口但业务压测
    作者回复

    如果你只想压用户信息查询,可以先造出一堆token来嘛。

    2021-05-24 08:41:58

  • 小鱼儿吐泡泡

    2021-05-20 08:47:21

    你好 请教下

    — si 消耗的 CPU 有点高了 —

    怎么判断si是否合理? 这个依据是 ?
    作者回复

    这个高低倒没有确切的标准,不过这里超过10%了。根据我的经验在3-5%之间比较正常。

    2021-05-20 13:06:33

  • xhkk

    2021-04-21 20:56:05

    总共用了 50Mb 的带宽,中断就已经达到 10%,也就是说带宽没有完全用满,可是中断已经不低了,这说明我们的数据包中还是小包居多。

    这段话的内容是从该段话上面的图片得到的,高老师,请问图片中哪里显示了是50Mb带宽?中断达到10%又是从那个数字判断的?

    另外,这个图是通过什么命令得到的?
    作者回复

    50M的带宽在上面图中的最右下角。10%在上面的top命令中的si列。
    命令是iftop。

    2021-04-22 15:51:05

  • 亚林

    2024-04-18 10:06:57

    KVM劝退之旅
    作者回复

    应该是劝退k8s,哈哈。

    2024-10-09 11:44:39

  • 测试中的战斗机

    2021-08-14 10:59:42

    老师,请教您两个问题,还望解答:
    1.我们做性能测试时有时候需要直接在生产环境去压,但生产环境有时候没法安装那些监控软件,像这种情况有什么好的办法通过别的途径获取到监控数据吗?
    2. 上文中你截图的那些监控网络图是用什么来监控的?
    作者回复

    1. 不明白你说的哪些监控软件不能在生产上去用。生产上的监控应该比测试环境更全才对。你要具体说出哪个不能用,我才能告诉你有没有替换工具。
    2. 我不清楚你说的具体是哪个图。我猜你说的应该是iftop。

    2021-08-16 09:13:28