Linux网络性能优化

网络栈示意图
在这里插入图片描述
网卡是发送和接收网络包的基本设备。在系统启动过程中,网卡通过内核中的网卡驱动程序注册到系统中的,而在网络收发过程中,内核通过中断跟网卡进行交互。
网卡的硬中断只处理最核心的网卡数据的读取或发送。而网络中大部分逻辑都放在了软中断中进行处理。

网络包的接收流程
当一个网络包到达网卡后,网卡通过DMA方式把这个数据包放入到收包队列中。然后通过硬中断告诉中断处理程序已经收到了网络包了,接着中断处理程序会为网络帧分配内核数据结构(sk_buff),并将其拷贝到sk_buff中;然后再通过软中断通知内核收到新的网络帧。
接下来,
内核协议栈从缓冲区中取出网络帧,并通过网络协议栈从上到下逐层处理这个网络帧:在链路层检查报文的合法性,找出上层协议的类型(比如 IPv4 还是 IPv6),再去掉帧头、帧尾,然后交给网络层。
网络层取出 IP 头,判断网络包下一步的走向,比如是交给上层处理还是转发。当网络层确认这个包是要发送到本机后,就会取出上层协议的类型(比如 TCP 还是 UDP),去掉 IP 头,再交给传输层处理。
传输层取出 TCP 头或者 UDP 头后,根据 < 源 IP、源端口、目的 IP、目的端口 > 四元组作为标识,找到对应的Socket,并把数据拷贝到Socket的接收缓存中。最后,应用程序就可以使用socket接口,读取新接收的数据了。在这里插入图片描述
性能衡量指标
1.带宽:表示链路的最大传输速率,单位是b/s
2.吞吐量:表示单位时间内成功传输的数据量,单位b/s。吞吐量受限于带宽,而吞吐量/带宽就是该网络的使用率
3.延时:从网络请求发出一直到接收远端响应所需要的时间延迟。
4.PPS:表示以网络包为单位的传输速率。PPS通常可以用来衡量网络的转发能力。
除了以上的这些指标还有:网络可用性、并发连接数(Tcp连接数量)、丢包率、重传率

网络配置

$ ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
      inet 10.240.0.30 netmask 255.240.0.0 broadcast 10.255.255.255
      inet6 fe80::20d:3aff:fe07:cf2a prefixlen 64 scopeid 0x20<link>
      ether 78:0d:3a:07:cf:3a txqueuelen 1000 (Ethernet)
      RX packets 40809142 bytes 9542369803 (9.5 GB)
      RX errors 0 dropped 0 overruns 0 frame 0
      TX packets 32637401 bytes 4815573306 (4.8 GB)
      TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
​
$ ip -s addr show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
  link/ether 78:0d:3a:07:cf:3a brd ff:ff:ff:ff:ff:ff
  inet 10.240.0.30/12 brd 10.255.255.255 scope global eth0
      valid_lft forever preferred_lft forever
  inet6 fe80::20d:3aff:fe07:cf2a/64 scope link
      valid_lft forever preferred_lft forever
  RX: bytes packets errors dropped overrun mcast
   9542432350 40809397 0       0       0       193
  TX: bytes packets errors dropped carrier collsns
   4815625265 32637658 0       0       0       0

errors;代表发生错误的数据包数,比如校验错误、帧同步错误等
dropped:表示丢弃的数据包数,即数据包已经接收到Ring Buffer,但是由于内存不足等原因丢包
overuns: 表示超限数据包数,即网络io速度过快导致,Ring Buffer中的数据来不及处理(队列满)导致的丢包
carrier 表示发生 carrirer 错误的数据包数,比如双工模式不匹配、物理电缆出现问题等;
collisions 表示碰撞数据包数

# head -n 3 表示只显示前面3行
# -l 表示只显示监听套接字
# -n 表示显示数字地址和端口(而不是名字)
# -p 表示显示进程信息
$ netstat -nlp | head -n 3
Active Internet connections (only servers)
Proto  Recv-Q Send-Q Local Address   Foreign Address  State   PID/Program name
tcp    0      0      127.0.0.53:53   0.0.0.0:*        LISTEN  840/systemd-resolve

当Recv-Q和 Send-Q不为0的时候说明网络包有堆积发生
当套接字处于Established时,Recv-Q表示套接字缓冲还没被应用程序取走的字节数(接收队列的长度)。Send-Q表示还没被远端主机确认的字节数(发送队列的长度)。
当套接字处于Listening时,Recv-Q表示tcp协议栈中半连接队列(syn backlog)的当前值。而Send-Q表示最大的syn backlog值

网络吞吐和PPS

查看网卡速度:

# ethtool em1 | grep Speed
	Speed: 1000Mb/s
#  sar -n DEV 1
Linux 3.10.0-957.el7.x86_64 (pftest241) 	2022年01月01日 	_x86_64_	(20 CPU)
16时33分56秒     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
16时33分57秒 calid3fa3151657    160.00    120.00     14.73      8.44      0.00      0.00      0.00
16时33分57秒 cali56a50ee573b    240.00    320.00     16.88     29.45      0.00      0.00      0.00 

rxpck和txpck:发送数据包数和接收数据包数
rxkB和txkB:发送和接收的吞吐量
rxcmp和txcmp:发送和接收的压缩数据包数

两种io事件通知的方式:
1.水平触发:只要文件描述符可以非阻塞地执行io,就会触发通知。也就是说,应用程序可以随时检查文件描述符的状态,再根据状态进行io操作。
2.边缘触发:只有在文件描述符状态发生改变(也就是io请求到达)的时候,才会发送一次通知。这时候,应用程序会尽可能多的执行io,直到无法继续读写才会停止io操作。
如果io没执行完,或者因为某种原因没有来得及处理,那么这次通知就丢弃了。

io多路复用
1.使用非阻塞io和水平触发通知,比如使用select和poll
select对描述符列表进行轮询,但是有固定长度的限制
而poll没有最大描述符数量的限制(当然还是会受到系统描述符的最大限制)
同时应用程序每次调用select和poll的时候还会把文件描述符的集合从用户空间传向内核空间,由内核空间修改后再传回用户空间

2.使用非阻塞io和边缘触发通知,比如epoll
epoll使用红黑树,在内核中管理文件描述符集合,这样既不需要每次传入传出这个集合
epoll使用事件驱动机制,只需要关注有io事件发生的文件描述符,而不需要轮询扫描整个集合

3.使用异步io
异步io允许应用程序同时发起很多io操作而不需要等待这些操作完成

工作模型优化

第一种:主进程➕多个worker子进程
主进程执行bind()+listen()后创建多个子进程;
每个子进程通过accept()+epoll_wait()来处理相同的套接字在这里插入图片描述
第二种:监听相同端口的多进程模型
在这种方式下,所有进程都监听相同接口,并开启 SO_REUSEPORT选项,由内核将请求都负载均衡到这些监听进程中去在这里插入图片描述

如何评估网络的性能?

iperf3:测试TCP和UDP的性能
ab: 测试Http的性能在这里插入图片描述
第一行的Time per request表示平均延迟,包含线程运行的调度时间和网络请求的响应时间
第二行的Time per request表示实际请求的响应时间
Transfer rate:表示吞吐量为2108kb/s

如何优化网络性能?

1.如何优化网络地址转换(NAT)?
NAT主要用于实现内部网络的主机访问外部网络的功能。
NAT原理:NAT技术可以重写IP数据包的源IP或者目的IP,被普遍用来解决公网IP地址短缺的问题。它的主要原理就是网络中多台主机通过共享同一公网IP地址。
在这里插入图片描述
iptables与NAT
在这里插入图片描述

网络性能优化方法:

网络优化的整体目标是降低网络延迟(如RTT)和提高吞吐量(如BPS和PPS),但是到不同应用中,每个指标的优化标准可能不同,优先级顺序也不相同。
首先是网络接口层和网络层:它们主要负责网络包的封装、寻址、路由以及发送和接收。每秒可处理的网络包数PPS,就是它们最重要的性能指标。可以用pktgen来测试PPS的性能

再往上到传输层的TCP和UDP,它们主要负责网络传输。对它们而言吞吐量(BPS)、连接数、延迟就是最重要的性能指标,可以用iperf或netperf来测试传输层的性能。

再往上到应用层,最重要关注吞吐量、每秒请求数以及延迟等指标,你可以用wrk、ab等工具来测试应用层的性能
在这里插入图片描述
网络io优化思路:
应用层:

1.最常用的io多路复用epoll取代select和poll
2.使用异步io
3.工作模型优化:主进程+多个worker子进程
监听相同端口的多进程模型
4.使用长连接代替短连接,可以显著降低Tcp连接的成本
5.使用内存来缓存不常变化的数据
6.使用Protocol Buffer等序列化的方式,压缩网络io的数据量
7.使用DNS缓存、预取、HTTPDNS等方式,减少DNS解析的延迟
套接字层
套接字可以屏蔽掉Linux内核中不同协议的差异,为应用程序提供统一的访问接口,每一个套接字都有一个读写缓冲区。
在这里插入图片描述
除此之外,套接字接口还提供了一些配置选项,用来修改网络连接的行为:
为 TCP 连接设置 TCP_NODELAY 后,就可以禁用 Nagle 算法;
为 TCP 连接开启 TCP_CORK 后,可以让小包聚合成大包后再发送(注意会阻塞小包的发送);
使用 SO_SNDBUF 和 SO_RCVBUF ,可以分别调整套接字发送缓冲区和接收缓冲区的大小。

传输层:
tcp协议的优化:
一.在请求数比较大的时候会存在大量处于TIME_WAIT状态的连接,他们会占用大量的内存和端口资源,我们可以优化与TIME_WAIT状态相关的内核选项。
1.增大处于TIME_WAIT状态的连接数量
2.减小 net.ipv4.tcp_fin_timeout 和 net.netfilter.nf_conntrack_tcp_timeout_time_wait ,让系统尽快释放它们所占用的资源。
3.开启端口复用
4.增大本地端口范围
5.增大文件描述符数量
二.为了缓解SYN FLOOD等,利用tcp协议特定引发的性能攻击等问题,你可以考虑优化与 SYN 状态相关的内核选项
1.增大tcp半连接的最大数量
2.减少 SYN_RECV 状态的连接重传 SYN+ACK 包的次数
三.在长连接的场景中,通常使用keepalive来检查tcp的连接状态,以便对端连接断开时,可以自动回收。但是系统默认的探活时间间隔和重试次数一般都不能满足应用程序的性能需求,优化方法:1.缩短最后一次数据包到keepalive探活包的时间间隔
2.缩短发送keepalive探活包的时间间隔
3.减少 Keepalive 探测失败后,一直到通知应用程序前的重试次数
网络层
网络层负责网络包的封装、寻址、路由,包含IP、ICMP等协议,最主要的优化是对路由、ip分片、ICMP等优化
1.在需要转发的服务器中,比如作为NAT网关的服务器或者使用Docker容器时,开启IP转发
2.调整数据包的生存周期TTL
3.开启数据包的反向地址校验,比如设置 net.ipv4.conf.eth0.rp_filter = 1。这样可以防止 IP 欺骗,并减少伪造 IP 带来的 DDoS 问题。
第二种:调整数据包分片MTU的大小
第三种:从 ICMP 的角度出发,为了避免 ICMP 主机探测、ICMP Flood 等各种网络问题,你可以通过内核选项,来限制 ICMP 的行为。
链路层
由于网卡收包后调用中断处理程序需要消耗大量的cpu,所以将这些中断处理程序调度到不同的cpu上执行,就可以显著的提高网络吞吐量
比如你可以为网卡硬中断配置cpu亲和性或者开启 irqbalance 服务。
再如,你可以开启 RPS(Receive Packet Steering)和 RFS(Receive Flow Steering),将应用程序和软中断的处理,调度到相同 CPU 上,这样就可以增加 CPU 缓存命中率,减少网络延迟。

实战案例

一.服务器丢包分析:
丢包通常会带来严重的性能问题,特别对于tcp来说,丢包会导致网络拥塞和重传,进而导致网络延迟增加,吞吐量降低。在这里插入图片描述
丢包可能的原因:
1.在两台VM之间可能发生传输的错误,比如网络拥塞、线路错误等
2.在网卡收包后,kennel存在环形缓冲区可能因为溢出而丢包

通过netstat -i查看
在这里插入图片描述RX-OK代表接收时的总数
RX-ERR代表接收时的总错误数
RX-DRP代表进入Ring Buffer后因为某些原因导致的丢包数(如内存不足)
RX-OVR代表Ring Buffer内存溢出导致的丢包数

3.在链路层可能因为网络帧校验失败,QoS等而丢包
通过tc -s qdisc show dev eth0查看是否配置tc规则,并查看有没有丢包

4.在IP层可能因为路由失败、组包大小超过MTU而丢包

root@nginx:/# netstat -s
Ip:
    Forwarding: 1          //开启转发
    31 total packets received    //总收包数
    0 forwarded            //转发包数
    0 incoming packets discarded  //接收丢包数
    25 incoming packets delivered  //接收的数据包数
    15 requests sent out      //发出的数据包数
Icmp:
    0 ICMP messages received    //收到的ICMP包数
    0 input ICMP message failed    //收到ICMP失败数
    ICMP input histogram:
    0 ICMP messages sent      //ICMP发送数
    0 ICMP messages failed      //ICMP失败数
    ICMP output histogram:
Tcp:
    0 active connection openings  //主动连接数
    0 passive connection openings  //被动连接数
    11 failed connection attempts  //失败连接尝试数
    0 connection resets received  //接收的连接重置数
    0 connections established    //建立连接数
    25 segments received      //已接收报文数
    21 segments sent out      //已发送报文数
    4 segments retransmitted    //重传报文数
    0 bad segments received      //错误报文数
    0 resets sent          //发出的连接重置数
Udp:
    0 packets received
    ...
TcpExt:
    11 resets received for embryonic SYN_RECV sockets  //半连接重置数
    0 packet headers predicted
    TCPTimeouts: 7    //超时数
    TCPSynRetrans: 4  //SYN重传数
  ...

以上告诉我们,TCP 协议有多次超时和失败重试,并且主要错误是半连接重置。换句话说,主要的失败,都是三次握手失败。

5.在传输层可能因为端口未监听、资源占用超过内核限制而丢包
6.在套接字层可能因为套接字缓冲区溢出而丢包
7.应用层可能因为应用异常而丢包
8.此外,如果配置了 iptables 规则,这些网络包也可能因为 iptables 过滤规则而丢包。

二.DNS解析时快时慢?
DNS协议在TCP/IP协议栈中属于应用层,不过实际传输过程中还是基于UDP或者TCP协议,并且域名服务器一般监听端口53上。
通过dig +trace +nodnssec 来查看整个DNS递归查询过程。
通过cat /etc/resolv.conf 查看本机配置的DNS服务器
开启DNS缓存的方法:/etc/init.d/dnsmasq start
DNS解析不稳定可能的原因:
1.DNS服务器本身的原因,响应慢且不稳定
2.客户端到DNS服务器的网络延迟比较大
3.DNS请求或者响应包,在某些情况下被链路层的网络设备弄丢了。
排查方法:
1.执行nslookup 查看到DNS是114.114.114.114在这里插入图片描述
2.执行ping -c3 114.114.114.114 查看延迟

对DNS的优化方法:
1.对DNS结果进行缓存。
2.对DNS解析的结果进行预取。这是浏览器等web应用程序最常用的方法。也就是不等用户点击页面的超链接,浏览器就会在后台自行解析域名,把结果缓存起来
3.使用HTTPDNS取代常规的DNS解析。这是很多移动应用会选择的方法,特别是如今域名劫持普遍存在。使用HTTP协议绕过链路中的DNS服务器,可以有效的避免域名劫持的问题。
4.基于DNS的负载均衡:这不仅能够为服务提供负载均衡和高可用的功能,还可以根据用户的位置选择距离最近的IP地址。

tcpdump和Wireshark的使用:

# ping 3 次(默认每次发送间隔1秒)
# 假设DNS服务器还是上一期配置的114.114.114.114
$ ping -c3 geektime.org
PING geektime.org (35.190.27.188) 56(84) bytes of data.
64 bytes from 35.190.27.188 (35.190.27.188): icmp_seq=1 ttl=43 time=36.8 ms
64 bytes from 35.190.27.188 (35.190.27.188): icmp_seq=2 ttl=43 time=31.1 ms
64 bytes from 35.190.27.188 (35.190.27.188): icmp_seq=3 ttl=43 time=31.2 ms

--- geektime.org ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 11049ms
rtt min/avg/max/mdev = 31.146/33.074/36.809/2.649 ms

我们可以看到以上的ping命令每次耗时都只有30多毫秒,但是总共的耗时却有11s多。
执行以下命令:

$ tcpdump -nn udp port 53 or host 35.190.27.188
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:02:31.100564 IP 172.16.3.4.56669 > 114.114.114.114.53: 36909+ A? geektime.org. (30)
14:02:31.507699 IP 114.114.114.114.53 > 172.16.3.4.56669: 36909 1/0/0 A 35.190.27.188 (46)
14:02:31.508164 IP 172.16.3.4 > 35.190.27.188: ICMP echo request, id 4356, seq 1, length 64
14:02:31.539667 IP 35.190.27.188 > 172.16.3.4: ICMP echo reply, id 4356, seq 1, length 64
14:02:31.539995 IP 172.16.3.4.60254 > 114.114.114.114.53: 49932+ PTR? 188.27.190.35.in-addr.arpa. (44)
14:02:36.545104 IP 172.16.3.4.60254 > 114.114.114.114.53: 49932+ PTR? 188.27.190.35.in-addr.arpa. (44)
14:02:41.551284 IP 172.16.3.4 > 35.190.27.188: ICMP echo request, id 4356, seq 2, length 64
14:02:41.582363 IP 35.190.27.188 > 172.16.3.4: ICMP echo reply, id 4356, seq 2, length 64
14:02:42.552506 IP 172.16.3.4 > 35.190.27.188: ICMP echo request, id 4356, seq 3, length 64
14:02:42.583646 IP 35.190.27.188 > 172.16.3.4: ICMP echo reply, id 4356, seq 3, length 64

例如第一条内容:

14:02:31.100564 IP 172.16.3.4.56669 > 114.114.114.114.53: 36909+ A? geektime.org. (30)

36909+代表查询标识值,它也会出现在响应中,+表示启用递归查询
A?表示查询A记录(A记录是指把域名转换为IP地址)
geektime.org.表示待查询的域名
30表示报文长度

下一条内容则是从114.114.114.114发送回来的响应包-域名geektime.org.的A记录为35.190.27.188

14:02:31.507699 IP 114.114.114.114.53 > 172.16.3.4.56669: 36909 1/0/0 A 35.190.27.188 (46)

以下两条的记录耗时就超过了10s,并且都是PTR发送的请求包,

14:02:31.539995 IP 172.16.3.4.60254 > 114.114.114.114.53: 49932+ PTR? 188.27.190.35.in-addr.arpa. (44)
14:02:36.545104 IP 172.16.3.4.60254 > 114.114.114.114.53: 49932+ PTR? 188.27.190.35.in-addr.arpa. (44)

所以ping缓慢的原因正是两次PTR请求没有得到响应而超时导致的。
PTR反向地址解析的目的是从IP地址反查出域名,但事实上,并非所有的IP地址都会定义PTR记录。所以PTR记录很可能失败。
了解网络包的详细信息:https://www.rfc-editor.org/rfc-index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值