You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
14 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 19 案例篇 | 网络吞吐高的业务是否需要开启网卡特性呢?
你好,我是邵亚方。
通过上一讲我们对CPU利用率的细化相信你已经知道对于应用而言它的目标是让CPU的开销尽量用在执行用户代码上而非其他方面。usr利用率越高说明CPU的效率越高。如果usr低就说明CPU执行应用的效率不高。在[第18讲](https://time.geekbang.org/column/article/292060)里我们还讲了CPU时间浪费在sys里的案例。那今天这一讲我们一起来看看CPU在softirq上花费过多时间所引起的业务性能下降问题这也是我们在生产环境中经常遇到的一类问题。接下来我会为你讲解相关案例以及这类问题常用的观察方法。
## 中断与业务进程之间是如何相互干扰的?
这是我多年以前遇到的一个案例当时业务反馈说为了提升QPSQuery per Second他们开启了RPSReceivce Packet Steering来模拟网卡多队列没想到开启RPS反而导致了QPS明显下降不知道是什么原因。
其实这类特定行为改变引起的性能下降问题相对好分析一些。最简单的方式就是去对比这个行为前后的性能数据。即使你不清楚RPS是什么也不知道它背后的机制你也可以采集需要的性能指标进行对比分析然后判断问题可能出在哪里。这些性能指标包括CPU指标内存指标I/O指标网络指标等我们可以使用dstat来观察它们的变化。
在业务打开RPS之前的性能指标
```
$ dstat
You did not select any stats, using -cdngy by default.
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read writ| recv send| in out | int csw
64 23 6 0 0 7| 0 8192B|7917k 12M| 0 0 | 27k 1922
64 22 6 0 0 8| 0 0 |7739k 12M| 0 0 | 26k 2210
61 23 9 0 0 7| 0 0 |7397k 11M| 0 0 | 25k 2267
```
打开了RPS之后的性能指标
```
$ dstat
You did not select any stats, using -cdngy by default.
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read writ| recv send| in out | int csw
62 23 4 0 0 12| 0 0 |7096k 11M| 0 0 | 49k 2261
74 13 4 0 0 9| 0 0 |4003k 6543k| 0 0 | 31k 2004
59 22 5 0 0 13| 0 4096B|6710k 10M| 0 0 | 48k 2220
```
我们可以看到打开RPS后CPU的利用率有所升高。其中siq即软中断利用率明显增加int即硬中断频率也明显升高而net这一项里的网络吞吐数据则有所下降。也就是说在网络吞吐不升反降的情况下系统的硬中断和软中断都明显增加。由此我们可以推断出网络吞吐的下降应该是中断增加导致的结果。
那么,接下来我们就需要分析到底是什么类型的软中断和硬中断增加了,以便于找到问题的源头。
系统中存在很多硬中断,这些硬中断及其发生频率我们都可以通过/proc/interruptes这个文件来查看
```
$ cat /proc/interrupts
```
如果你想要了解某个中断的详细情况,比如中断亲和性,那你可以通过/proc/irq/\[irq\_num\]来查看,比如:
```
$ cat /proc/irq/123/smp_affinity
```
软中断可以通过/proc/softirq来查看
```
$ cat /proc/softirqs
```
当然了,你也可以写一些脚本来观察各个硬中断和软中断的发生频率,从而更加直观地查看是哪些中断发生得太频繁。
关于硬中断和软中断的区别,你可能多少有些了解,软中断是用来处理硬中断在短时间内无法完成的任务的。硬中断由于执行时间短,所以如果它的发生频率不高的话,一般不会给业务带来明显影响。但是由于内核里关中断的地方太多,所以进程往往会给硬中断带来一些影响,比如进程关中断时间太长会导致网络报文无法及时处理,进而引起业务性能抖动。
我们在生产环境中就遇到过这种关中断时间太长引起的抖动案例比如cat /proc/slabinfo这个操作里的逻辑关中断太长它会致使业务RT抖动。这是因为该命令会统计系统中所有的slab数量并显示出来在统计的过程中会关中断。如果系统中的slab数量太多就会导致关中断的时间太长进而引起网络包阻塞ping延迟也会因此明显变大。所以在生产环境中我们要尽量避免去采集/proc/slabinfo否则可能会引起业务抖动。
由于/proc/slabinfo很危险所以它的访问权限也从2.6.32版本时的0644[更改为了后来](https://lwn.net/Articles/431818/)[的](https://lwn.net/Articles/431818/)[0400](https://lwn.net/Articles/431818/)也就是说只有root用户才能够读取它这在一定程度上避免了一些问题。如果你的系统是2.6.32版本的内核,你就需要特别注意该问题。
如果你要分析因中断关闭时间太长而引发的问题有一种最简单的方式就是使用ftrace的irqsoff功能。它不仅可以统计出中断被关闭了多长时间还可以统计出为什么会关闭中断。不过你需要注意的是irqsoff功能依赖于CONFIG\_IRQSOFF\_TRACER这个配置项如果你的内核没有打开该配置项那你就需要使用其他方式来去追踪了。
如何使用irqsoff呢首先你需要去查看你的系统是否支持了irqsoff这个tracer
```
$ cat /sys/kernel/debug/tracing/available_tracers
```
如果显示的内容包含了irqsoff说明系统支持该功能你就可以打开它进行追踪了
```
$ echo irqsoff > /sys/kernel/debug/tracing/current_tracer
```
接下来,你就可以通过/sys/kernel/debug/tracing/trace\_pipe和trace这两个文件来观察系统中的irqsoff事件了。
我们知道相比硬中断软中断的执行时间会长一些而且它也会抢占正在执行进程的CPU从而导致进程在它运行期间只能等待。所以相对而言它会更容易给业务带来延迟。那我们怎么对软中断的执行频率以及执行耗时进行观测呢你可以通过如下两个tracepoints来进行观测
```
/sys/kernel/debug/tracing/events/irq/softirq_entry
/sys/kernel/debug/tracing/events/irq/softirq_exit
```
这两个tracepoint分别表示软中断的进入和退出退出时间减去进入时间就是该软中断这一次的耗时。关于tracepoint采集数据的分析方式我们在之前的课程里已经讲过多次所以就不在这里继续描述了。
如果你的内核版本比较新支持eBPF功能那你同样可以使用bcc里的[softirqs.py](https://github.com/iovisor/bcc/blob/master/tools/softirqs.py)这个工具来进行观测。它会统计软中断的次数和耗时,这对我们分析软中断引起的业务延迟来说,是比较方便的。
为了避免软中断太过频繁进程无法得到CPU而被饿死的情况内核引入了ksoftirqd这个机制。如果所有的软中断在短时间内无法被处理完内核就会唤醒ksoftirqd处理接下来的软中断。ksoftirqd与普通进程的优先级一样也就是说它会和普通进程公平地使用CPU这在一定程度上可以避免用户进程被饿死的情况特别是对于那些更高优先级的实时用户进程而言。
不过这也会带来一些问题。如果ksoftrirqd长时间得不到CPU就会致使软中断的延迟变得很大它引起的典型问题也是ping延迟。如果ping包无法在软中断里得到处理就会被ksoftirqd处理。而ksoftirqd的实时性是很难得到保障的可能需要等其他线程执行完ksoftirqd才能得到执行这就会导致ping延迟变得很大。
要观测ksoftirqd延迟的问题你可以使用bcc里的[runqlat.py](https://github.com/iovisor/bcc/blob/master/tools/runqlat.py)。这里我们以网卡中断为例它唤醒ksoftirqd的逻辑大致如下图所示
![](https://static001.geekbang.org/resource/image/8b/01/8b3f5bfa5571dcc855c4f6cd7dc4ce01.jpg "中断与ksoftirqd")
我们具体来看看这个过程软中断被唤醒后会检查一遍软中断向量表逐个处理这些软中断处理完一遍后它会再次检查如果又有新的软中断要处理就会唤醒ksoftrqd来处理。ksoftirqd是per-cpu的内核线程每个CPU都有一个。对于CPU1而言它运行的是ksoftirqd/1这个线程。ksoftirqd/1被唤醒后会检查软中断向量表并进行处理。如果你使用ps来查看ksoftirqd/1的优先级会发现它其实就是一个普通线程对应的Nice值为0
```
$ ps -eo "pid,comm,ni" | grep softirqd
9 ksoftirqd/0 0
16 ksoftirqd/1 0
21 ksoftirqd/2 0
26 ksoftirqd/3 0
```
总之,在软中断处理这部分,内核需要改进的地方还有很多。
## softirq是如何影响业务的
在我们对硬中断和软中断进行观察后发现使能RPS后增加了很多CALFunction Call Interrupts硬中断。CAL是通过软件触发硬中断的一种方式可以指定CPU以及需要执行的中断处理程序。它也常被用来进行CPU间通信IPI当一个CPU需要其他CPU来执行特定中断处理程序时就可以通过CAL中断来进行。
如果你对RPS的机制有所了解的话应该清楚RPS就是通过CAL这种方式来让其他CPU去接收网络包的。为了验证这一点我们可以通过mpstat这个命令来观察各个CPU利用率情况。
使能RPS之前的CPU利用率如下所示
```
$ mpstat -P ALL 1
Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
Average: all 70.18 0.00 19.28 0.00 0.00 5.86 0.00 0.00 0.00 4.68
Average: 0 73.25 0.00 21.50 0.00 0.00 0.00 0.00 0.00 0.00 5.25
Average: 1 58.85 0.00 14.46 0.00 0.00 23.44 0.00 0.00 0.00 3.24
Average: 2 74.50 0.00 20.00 0.00 0.00 0.00 0.00 0.00 0.00 5.50
Average: 3 74.25 0.00 21.00 0.00 0.00 0.00 0.00 0.00 0.00 4.75
```
使能RPS之后各个CPU的利用率情况为
```
$ mpstat -P ALL 1
Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
Average: all 66.21 0.00 17.73 0.00 0.00 11.15 0.00 0.00 0.00 4.91
Average: 0 68.17 0.00 18.33 0.00 0.00 7.67 0.00 0.00 0.00 5.83
Average: 1 60.57 0.00 15.81 0.00 0.00 20.80 0.00 0.00 0.00 2.83
Average: 2 69.95 0.00 19.20 0.00 0.00 7.01 0.00 0.00 0.00 3.84
Average: 3 66.39 0.00 17.64 0.00 0.00 8.99 0.00 0.00 0.00 6.99
```
我们可以看到使能RPS之后softirq在各个CPU之间更加均衡了一些本来只有CPU1在处理softirq使能后每个CPU都会处理softirq并且CPU1的softirq利用率降低了一些。这就是RPS的作用让网络收包软中断处理在各个CPU间更加均衡以防止其在某个CPU上达到瓶颈。你可以看到使能RPS后整体的%soft比原来高了很多。
理论上处理网络收包软中断的CPU变多那么在单位时间内这些CPU应该可以处理更多的网络包从而提升系统整体的吞吐。可是在我们的案例中为什么会引起业务的QPS不升反降呢
其实答案同样可以从CPU利用率中得出。我们可以看到在使能RPS之前CPU利用率已经很高了达到了90%以上也就是说CPU已经在超负荷工作了。而打开RPSRPS又会消耗额外的CPU时间来模拟网卡多队列特性这就会导致CPU更加超负荷地工作从而进一步挤压用户进程的处理时间。因此我们会发现在打开RPS后%usr的利用率下降了一些。
我们知道%usr是衡量用户进程执行时间的一个指标%usr越高意味着业务代码的运行时间越多。如果%usr下降那就意味着业务代码的运行时间变少了在业务没有进行代码优化的前提下这显然是一个危险的信号。
由此我们可以发现RPS的本质就是把网卡特性网卡多队列给upload到CPU通过牺牲CPU时间来提升网络吞吐。如果你的系统已经很繁忙了那么再使用该特性无疑是雪上加霜。所以你需要注意使用RPS的前提条件是系统的整体CPU利用率不能太高。
找到问题后我们就把该系统的RPS特性关闭了。如果你的网卡比较新它可能会支持硬件多队列。硬件多队列是在网卡里做负载均衡在这种场景下硬件多队列会很有帮助。我们知道与upload相反的方向是offload就是把CPU的工作给offload到网卡上去处理这样可以把CPU解放出来让它有更多的时间执行用户代码。关于网卡的offload特性我们就不在此讨论了。
好了,这节课就讲到这里。
## 课堂总结
我们来简单回顾一下这节课的重点:
* 硬中断、软中断以及ksoftirqd这个内核线程它们与用户线程之间的关系是相对容易引发业务抖动的地方你需要掌握它们的观测方式
* 硬中断对业务的主要影响体现在硬中断的发生频率上,但是它也容易受线程影响,因为内核里关中断的地方有很多;
* 软中断的执行时间如果太长,就会给用户线程带来延迟,你需要避免你的系统中存在耗时较大的软中断处理程序。如果有的话,你需要去做优化;
* ksoftirqd的优先级与用户线程是一致的因此如果软中断处理函数是在ksoftirqd里执行的那它可能会有一些延迟
* RPS的本质是网卡特性unload到CPU靠牺牲CPU时间来提升吞吐你需要结合你的业务场景来评估是否需要开启它。如果你的网卡支持了硬件多队列那么就可以直接使用硬件多队列了。
## 课后作业
我们这节课的作业有两种,你可以根据自己的情况进行选择。
* 入门:
请问如果软中断以及硬中断被关闭的时间太长,会发生什么事?
* 高级:
如果想要追踪网络数据包在内核缓冲区停留了多长时间才被应用读走,你觉得应该如何来追踪?
欢迎你在留言区与我讨论。
感谢你的阅读,如果你认为这节课的内容有收获,也欢迎把它分享给你的朋友,我们下一讲见。