linux服务器性能调优(udp为主)

发布时间 2023-07-26 10:25:01作者: 秋来叶黄

udp的好处很明显,效率高,减少了建立连接的流程,减少了报文头的占比,也减少了维护连接的开销。缺点就是不稳定,会丢包。还有就是由于udp的高效,导致用于udp的一些应用开发,并发都比较大,更容易丢包。

io复用 SO_REUSEADDR SO_REUSEPORT

创建listener用来监听数据时,有时候需要配置io复用。也就是可以创建多个监听相同端口的listener,分别在不同的进程/线程接收数据。

修改socket SO_NO_CHECK

系统接收到数据包后,会进行校验,如果对性能要求极致,可以设置改参数,不进行校验。可能性能提升不明显。

负载均衡

使用不同系统下的高性能模型,提高对系统资源的利用,提高吞吐量。Windows-IOCP, Linux-epoll, Unix-kqueue。

接收线程逻辑简单

接收数据的线程尽可能简单,减少复杂逻辑处理,避免影响接收速度。

使用缓存池

所谓的缓存池就是接收数据,肯定需要申请一块内存保存起来,使用完之后,这块内存不释放,而是放到一个列表中,下次再需要空间保存接收的数据,直接从列表中找可用的数据块,避免内存的重复申请释放。

避免数据拷贝

从socket接收数据时,可以使用零拷贝技术,让系统从内核态,直接把数据放到用户空间的内存块,避免内核态与用户态切换时进行一次数据拷贝。

获取到数据后,尽可能使用这块数据,通过传递指针的方式,避免数据拷贝。

使用线程池/协程池

实际上就是程序启动后,创建几个工作线程/协程,当有数据需要处理是,发数据发送到工作线程/协程,工作线程/协程处理完数据并不结束,而是继续等待数据。避免线程/协程频繁的申请释放。

修改网卡缓冲区 ring buffer

数据进来,第一步先到网卡的缓冲区,每张网卡都有其最大值,不要超过其最大值,如果ring buffer太小,也会影响接收,导致数据还没接收进入系统内核就被丢弃了。

如果ring buffer的设置没有达到最大值或者比最大值小很多,可以先设置这一步,看看性能有没有提升。

查看网卡参数

ethtool -g enp4s0 
Ring parameters for enp4s0:
Pre-set maximums:
RX:             256
RX Mini:        0
RX Jumbo:       0
TX:             256
Current hardware settings:
RX:             256
RX Mini:        0
RX Jumbo:       0
TX:             256

pre-set是网卡限制的值大小,current hardware settings是当前设置的大小。

设置网卡参数

ethtool -G enp4s0 rx 100 tx 200

修改接收缓冲区

数据到达网卡ring buffer后,下一步是系统缓冲区,也要根据情况配置。系统(UDP)默认值经常是212992=208K,很明显无法满足高并发的情形。建议设置为当前接收数据流量相通的值。比如用dstat看到是20M,那么就把这个值设置为20M。
实际上这个数值是可以计算的,比如当前是20MB,那么一秒钟接收几次?比如2次,那么每次就是10MB的数据量,假设每次都能处理完,socket buffer是空的,就只需要设置为10MB即可。不过平时没必要设置这么精确,预估一下,多调试几次,找到一个合适的值即可。

查看缓冲配置

查看文件

#udp默认读缓冲区大小
cat /proc/sys/net/core/rmem_default 
2097152

#udp最大读缓冲区大小
cat /proc/sys/net/core/rmem_max
2097152

#udp默认写缓冲区大小
cat /proc/sys/net/core/wmem_default 
212992

#udp最大写缓冲区大小
cat /proc/sys/net/core/wmem_max 
2097152

#tcp读缓冲区
cat /proc/sys/net/ipv4/tcp_rmem 
4096    131072  6291456
最小值   默认值  最大值

#tcp写缓冲区
cat /proc/sys/net/ipv4/tcp_wmem 
4096    16384   4194304
最小值   默认值  最大值

命令查看

sysctl -a可以显示所有的配置,可以使用grep过滤查看对应的设置数值。

修改缓冲配置

修改文件

/etc/sysctl.conf文件末尾,加上net.core.rmem_max = 2097152,然后执行sysctl -p让其生效。可以通过上面的方式查看一下是否已经修改。

程序修改socket SO_RCVBUF SO_RCVBUFFORCE

每个socket有对应的默认接收缓冲区,很显然默认设置不适合高并发的情况,可以重新设置该值,理论上设置为流量的相同数额,比如流量是20MB也就是20*8=160Mb,那么设置为20MB,测试一下性能是否有提升。不过具体设置为多少,需要调试。
这个设置会受系统的配置限制或者影响,虽然SO_RCVBUFFORCE可以超过系统限制,但是还是会被系统设置覆盖。所以最好还是先配置好系统的设置。
https://man7.org/linux/man-pages/man7/socket.7.html

修改打开文件句柄数

如果是tcp,需要为每个socket创建一个句柄,如果是高并发,有可能会到达句柄上限,可以设置/proc/sys/fs/file-max进行修改。

查看当前使用句柄详情

cat /proc/net/sockstat
sockets: used 301
socket使用数量

TCP: inuse 10 orphan 0 tw 0 alloc 17 mem 2
inuse-在使用 orphan-孤儿tcp,不属于任何进程的tcp tw-time wait,等待关闭的tcp alloc-已申请的tcp mem-内存使用量,单位是页,一页常见的是4096 bytes

UDP: inuse 2 mem 14
inuse-在用的udp mem-使用的内存

UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0

查看当前是否有丢包

ifconfig

ifconfig enp4s0
enp4s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.10.181  netmask 255.255.255.0  broadcast 192.168.10.255
        inet6 fe80::f2ca:f6f8:55f:1d01  prefixlen 64  scopeid 0x20<link>
        ether 84:a9:38:77:8b:5e  txqueuelen 1000  (Ethernet)
        RX packets 1778583  bytes 1501914135 (1.3 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1146991  bytes 150339414 (143.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

rx表示接收
tx表示发送
dropped表示数据从网卡到系统或者接收程序的时候丢了,数据已经进入到网卡的ring buffer。具体原因可能是内存等资源不足,被系统丢掉的。
overruns表示网卡的fifo的overruns,数据没有进入ring buffer,表示网卡的ring buffer可能已经满了。具体原因可能是程序响应太慢,导致数据接收太慢,ring buffer被填满。

间隔几秒钟或一段时间再次查询,看一下丢包数量是否在增加

也可以使用watch ifconfig enp4s0,自动刷新

netstat

watch netstat -su
IcmpMsg:
    InType3: 3958
    InType8: 31
    OutType0: 31
    OutType3: 13793
    OutType8: 4
    OutType11: 2
Udp:
    59277 packets received
    10596 packets to unknown port received
    0 packet receive errors
    57572 packets sent
    0 receive buffer errors
    0 send buffer errors
    IgnoredMulti: 499
UdpLite:
IpExt:
    InMcastPkts: 959
    OutMcastPkts: 244
    InBcastPkts: 1805
    OutBcastPkts: 920
    InOctets: 2508346835
    OutOctets: 1731630616
    InMcastOctets: 118082
    OutMcastOctets: 35569
    InBcastOctets: 410940
    OutBcastOctets: 157801
    InNoECTPkts: 2982210

/proc/net/snmp

cat /proc/net/snmp
Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates
Ip: 2 64 996028174 0 32 0 0 0 995326101 31825816 0 139 0 0 0 0 0 0 0
Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps
Icmp: 2596 0 0 12 0 0 0 0 2584 0 0 0 0 0 64876 0 62292 0 0 0 0 0 2584 0 0 0 0
IcmpMsg: InType3 InType8 OutType0 OutType3
IcmpMsg: 12 2584 2584 62292
Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors
Tcp: 1 200 120000 -1 254245 127 250988 90 6 16656876 32336601 12644 42 252103 0
Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti
Udp: 916978907 3177158 58760728 13821 58760728 0 0 0
UdpLite: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti
UdpLite: 0 0 0 0 0 0 0 0

调查具体丢包函数

dropwatch

运行dropwatch -l kas,起来后,输入start,就会不断监听具体丢包是哪个系统函数导致的。

dropwatch -l kas
Initalizing kallsyms db
dropwatch> start
Enabling monitoring...
Kernel monitoring activated.
Issue Ctrl-C to stop monitoring
1 drops at tcp_v6_rcv+45 (0xffffffffb32e8ef5)
1 drops at tcp_v4_rcv+48 (0xffffffffb3252778)
3 drops at __udp4_lib_rcv+a42 (0xffffffffb3260d52)
1 drops at sk_stream_kill_queues+50 (0xffffffffb3189530)
2 drops at nf_hook_slow+a7 (0xffffffffb321ab57)
2814 drops at udp_queue_rcv_one_skb+3a6 (0xffffffffb325f676)
68792 drops at udp_queue_rcv_one_skb+3a6 (0xffffffffb325f676)
1 drops at sk_stream_kill_queues+50 (0xffffffffb3189530)
^CGot a stop message
dropwatch> exit
Shutting down ...

查看系统版本

uname -a
Linux 4.18.0-305.3.1.el8.x86_64 #1 SMP Tue Jun 1 16:14:33 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

查看相邻的系统调用

grep -w "udp_queue_rcv_one_skb" /boot/System.map-4.18.0-305.3.1.el8.x86_64 -A2
ffffffff8185f2d0 t udp_queue_rcv_one_skb
ffffffff8185f7d0 t udp_queue_rcv_skb
ffffffff8185f9a0 t udp_unicast_rcv_skb.isra.66

可以到源码中查看具体函数,确认丢包原因

其他操作

多核系统可以进行taskset绑核 isolation隔离 cgroup分组
还可以设置Receive Side Scaling (RSS)