linux服务端程序tunning过程简要总结

如何定位性能瓶颈问题?

步骤1:网络丢包的原因定位

ethtool -S eth0

rx_dropped代表丢包发生在linux 内核缓冲区,丢包发生在docker的虚拟网卡上

cat /proc/net/softnet_stat

1
2
3
4
5
root@xnet-controller1:~# cat /proc/net/softnet_stat
0f77ed22 00000000 00000b03 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
101a1151 00000000 000007d9 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1fa636a0 00000000 0000639d 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0f9afaf9 00000000 00000f08 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

第一列:中断处理程序接收的帧数

第二列:由于超过cpu队列 netdev_max_backlog 1000 丢弃帧数

第三列:net_rx_action 函数中处理数据包超过300 netdev_budget 或者 运行时间超过2个时间片

内核为每个cpu都存储一个softnet_data对象,如果所有中断都由一个cpu核处理,如果接受数据包量非常大,超过中断处理速度,就会导致超过netdev_max_backlog而丢包

top查看%si软中断分布情况

cpu0比其它要高很多

查看网卡型号确认网卡队列

1
lspci -vvv | grep Eth

中断分布均匀?

方法1: cat /proc/interrupts

方法2: systemtap工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
global hard, soft, wq

probe irq_handler.entry {
hard[irq, dev_name]++;
}

probe timer.s(1) {
println("==irq number:dev_name")
foreach( [irq, dev_name] in hard- limit 5) {
printf("%d,%s->%d\n", irq, kernel_string(dev_name), hard[irq, dev_name]);
}

println("==softirq cpu:h:vec:action")
foreach( [c,h,vec,action] in soft- limit 5) {
printf("%d:%x:%x:%s->%d\n", c, h, vec, symdata(action), soft[c,h,vec,action]);
}


println("==workqueue wq_thread:work_func")
foreach( [wq_thread,work_func] in wq- limit 5) {
printf("%x:%x->%d\n", wq_thread, work_func, wq[wq_thread, work_func]);
}

println("\n")
delete hard
delete soft
delete wq
}

probe softirq.entry {
soft[cpu(), h,vec,action]++;
}

probe workqueue.execute {
wq[wq_thread, work_func]++
}


probe begin {
println("~")
}

中断分布非常不均匀,主要集中在CPU0上

步骤2:如何优化

首先是数据包超过了netdev_max_backlog造成了丢弃,那么调大netdev_max_backlog? 不靠谱,根据生产消费模型来讲,问题点出在消费的慢,所以应该直面问题本质,问题本质是中断处理过慢,即使不丢包问题也会体现在其它方面,比如服务器响应慢。

  • 配置多个core处理中断
  • 处理中断的CPU和业务的CPU分开,这样不回相互中断影响
  • 考虑NUMA架构因素,将中断和应用程序使用相同NUMA

步骤3:如果在经过了步骤1和步骤2,你对性能目标没有达到,那就说明问题出在应用程序本身

使用如下方法去定位吧

  • perf
  • systap
  • valgrind
  • dropwatch
  • netstat -s
  • ethtool -S
  • netstat -nat
  • ifconfig
  • ftrace
  • valgrind -e -b查看线程调用栈找到性能瓶颈
  • 频繁gdb查看调用栈找到性能瓶颈
  • 查看内核调用栈找到性能瓶颈

非常有意思的点Linux wake affinity特性

当两个NUMA节点处理中断时,CPU实例化的softnet_data以及驱动分配的sk_buffer都可能是跨node的,数据接收后对上层应用程序来说,跨node访问的几率也大大提高,并且无法充分利用L2、L3 cache,增加了延时。

同时,由于Linux wake affinity 特性,如果两个进程频繁互动,调度系统会觉得它们很有可能共享同样的数据,把它们放到同一CPU核心或NUMA Node有助于提高缓存和内存的访问性能,所以当一个进程唤醒另一个的时候,被唤醒的进程可能会被放到相同的CPU core或者相同的NUMA节点上。此特性对中断唤醒进程时也起作用,在上一节所述的现象中,所有的网络中断都分配给CPU 0去处理,当中断处理完成时,由于wakeup affinity特性的作用,所唤醒的用户进程也被安排给CPU 0或其所在的numa节点上其他core。而当两个NUMA node处理中断时,这种调度特性有可能导致应用进程在CPU core之间频繁迁移,造成性能损失。

参考

https://tech.meituan.com/Redis_High_Concurrency_Optimization.html

https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf