linux服务器性能调优(udp为主)
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)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
2022-07-26 error verifying int64_t uses long long
2019-07-26 opencv编译