Linux性能优化从入门到实战:17 网络篇:网络基础
网络模型
为了解决网络互联中异构设备的兼容性问题,并解耦复杂的网络包处理流程,国际标准化组织制定了开放式系统互联通信参考模型(Open System Interconnection Reference Model),简称 OSI 网络模型。OSI 模型把网络互联的框架分为应用层、表示层、会话层、传输层、网络层、数据链路层以及物理层等 七层网络模型,每个层负责不同的功能。
- 应用层 Application,负责为应用程序提供统一的接口。
- 表示层 Presentation,负责把数据转换成兼容接收系统的格式。
- 会话层 Session,负责维护计算机之间的通信连接。
- 传输层 Transport,负责为数据加上传输表头,形成数据包。
- 网络层 Network,负责数据的路由和转发。
- 数据链路层 Data Link,负责 MAC 寻址、错误侦测和改错。
- 物理层 Physical,负责在物理网络中传输数据帧。
更为实用的 四层网络模型,即 TCP/IP 网络模型。
- 应用层,负责向用户提供一组应用程序,比如 HTTP、FTP、DNS 等。
- 传输层,负责端到端的通信,比如 TCP、UDP 等。
- 网络层,负责网络包的封装、寻址和路由,比如 IP、ICMP 等。
- 网络接口层,负责网络包在物理网络中的传输,比如 MAC 寻址、错误侦测以及通过网卡传输网络帧等。
Linux 网络栈
TCP/IP 模型中的数据包进行网络传输时,会按照协议栈,对上一层发来的数据进行逐层处理;然后封装上该层的协议头,再发送给下一层。
- 处理:逻辑取决于各层采用的网络协议,如将 json 数据处理成 HTTP 协议;
- 封装:只是在原来的负载前后,增加固定格式的元数据,原始的负载数据并不会被修改。
物理链路中不能传输任意大小的数据包,网络接口配置的最大传输单元(MTU)规定了最大的 IP 包大小。在我们最常用的以太网中,MTU 默认值是 1500(这也是 Linux 的默认值)。一旦网络包超过 MTU 的大小,就会在网络层分片,以保证分片后的 IP 包不大于 MTU 值。显然,MTU 越大,需要的分包也就越少,自然,网络吞吐能力就越好。
在系统启动过程中,网卡通过内核中的网卡驱动程序注册到系统中。而在网络收发过程中,内核通过中断跟网卡进行交互。网卡硬中断只处理最核心的网卡数据读取或发送,而协议栈中的大部分逻辑,都会放到软中断中处理。
Linux 网络收发流程
网络性能指标
- 带宽,表示链路的最大传输速率,单位通常为 b/s (比特 / 秒),如 1000M、10G、40G、100G 等。
- 吞吐量,表示单位时间内成功传输的数据量,单位通常为 b/s(比特 / 秒)或者 B/s(字节 / 秒)。吞吐量受带宽限制,而吞吐量 / 带宽,也就是该网络的使用率。
- 延时,表示从网络请求发出后,一直到收到远端响应,所需要的时间延迟。在不同场景中,这一指标可能会有不同含义。比如,它可以表示,建立连接需要的时间(比如 TCP 握手延时),或一个数据包往返所需的时间(比如 RTT)。
- PPS,是 Packet Per Second(包 / 秒)的缩写,表示以网络包为单位的传输速率。PPS 通常用来评估网络的转发能力,比如硬件交换机,通常可以达到线性转发(即 PPS 可以达到或者接近理论最大值)。而基于 Linux 服务器的转发,则容易受网络包大小的影响。对于 TCP 或者 Web 服务,并发连接数和每秒请求数(QPS,Query per Second)更适合。
- 网络的可用性(网络能否正常通信)、并发连接数(TCP 连接数量)、丢包率(丢包百分比)、重传率(重新传输的网络包比例)
网络接口的配置和状态
ifconfig 和 ip
分别属于软件包 net-tools 和 iproute2,但 iproute2 是 net-tools 的下一代,更推荐使用。
$ 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
- (1)网络接口的状态标志。ifconfig 输出中的 RUNNING ,或 ip 输出中的 LOWER_UP ,都表示物理网络是连通的,即网卡已经连接到了交换机或者路由器中。如果你看不到它们,通常表示网线被拔掉了。
- (2)MTU 的大小。MTU 默认大小是 1500,根据网络架构的不同(比如是否使用了 VXLAN 等叠加网络),你可能需要调大或者调小 MTU 的数值。
- (3)网络接口的 IP 地址、子网以及 MAC 地址。这些都是保障网络功能正常工作所必需的,你需要确保配置正确。
- (4)网络收发的字节数、包数、错误数以及丢包情况,特别是 TX 和 RX 部分的 errors、dropped、overruns、carrier 以及 collisions 等指标不为 0 时,通常表示出现了网络 I/O 问题。
- errors 表示发生错误的数据包数,比如校验错误、帧同步错误等;
- dropped 表示丢弃的数据包数,即数据包已经收到了 Ring Buffer,但因为内存不足等原因丢包;
- overruns 表示超限数据包数,即网络 I/O 速度过快,导致 Ring Buffer 中的数据包来不及处理(队列满)而导致的丢包;
- carrier 表示发生 carrirer 错误的数据包数,比如双工模式不匹配、物理电缆出现问题等;
- collisions 表示碰撞数据包数。
套接字信息
netstat / ss
可用来查看套套接字的状态、接收队列、发送队列、本地地址、远端地址、进程 PID 和进程名称等。
$ netstat -nlp | head -n 3
# -n 表示显示数字地址和端口 (而不是名字)
# -l 表示只显示监听套接字
# -p 表示显示进程信息
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
$ ss -nlpt | head -n 3
# -n 表示显示数字地址和端口 (而不是名字)
# -l 表示只显示监听套接字
# -p 表示显示进程信息
# -t 表示只显示 TCP 套接字
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=840,fd=13))
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1459,fd=3))
接收队列(Recv-Q)和发送队列(Send-Q)通常应该是 0。当你发现它们不是 0 时,说明有网络包的堆积发生。
- 当套接字处于连接状态(Established)时,Recv-Q 表示套接字缓冲还没有被应用程序取走的字节数(即接收队列长度)。而 Send-Q 表示还没有被远端主机确认的字节数(即发送队列长度)。
- 当套接字处于监听状态(Listening)时,Recv-Q 表示 syn backlog 的当前值。而 Send-Q 表示最大的 syn backlog 值。
- syn backlog 是 TCP 协议栈中的半连接队列长度,相应的也有一个全连接队列(accept queue),它们都是维护 TCP 状态的重要机制。
- 半连接,是还没有完成 TCP 三次握手的连接,连接只进行了一半,而服务器收到了客户端的 SYN 包后,就会把这个连接放到半连接队列中,然后再向客户端发送 SYN+ACK 包。
- 全连接,是指服务器收到了客户端的 ACK,完成了 TCP 三次握手,然后就会把这个连接挪到全连接队列中。这些全连接中的套接字,还需要再被 accept() 系统调用取走,这样,服务器就可以开始真正处理客户端的请求了。
协议栈统计信息
查看协议栈的统计信息:
$ netstat -s
...
Tcp:
3244906 active connection openings
23143 passive connection openings
115732 failed connection attempts
2964 connection resets received
1 connections established
13025010 segments received
17606946 segments sent out
44438 segments retransmitted
42 bad segments received
5315 resets sent
InCsumErrors: 42
...
$ ss -s
Total: 186 (kernel 1446)
TCP: 4 (estab 1, closed 0, orphaned 0, synrecv 0, timewait 0/0), ports 0查看协议栈
Transport Total IP IPv6
* 1446 - -
RAW 2 1 1
UDP 2 2 0
TCP 4 3 1
...
网络吞吐和 PPS
sar -n 参数
查看网络的统计信息:网络接口(DEV)、网络接口错误(EDEV)、TCP、UDP、ICMP 等。
$ sar -n DEV 1 # 数字 1 表示每隔 1 秒输出一组数据
Linux 4.15.0-1035-azure (ubuntu) 01/06/19 _x86_64_ (2 CPU)
13:21:40 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
13:21:41 eth0 18.00 20.00 5.79 4.25 0.00 0.00 0.00 0.00
13:21:41 docker0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
13:21:41 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
- rxpck/s 和 txpck/s 分别是接收和发送的 PPS,单位为包 / 秒。
- rxkB/s 和 txkB/s 分别是接收和发送的吞吐量,单位是 KB/ 秒。
- rxcmp/s 和 txcmp/s 分别是接收和发送的压缩数据包数,单位是包 / 秒。
- %ifutil 是网络接口的使用率,即半双工模式下为 (rxkB/s+txkB/s)/Bandwidth,而全双工模式下为 max(rxkB/s, txkB/s)/Bandwidth。
- Bandwidth 通过
ethtool eth0 | grep Speed
查询,单位通常是 Gb/s 或者 Mb/s,是bit不是字节,还有千兆网卡、万兆网卡都是bit。
- Bandwidth 通过
连通性和延时
ping 基于 ICMP 协议,测试远程主机的连通性和延时。
$ ping -c3 114.114.114.114 # -c3 表示发送三次 ICMP 包后停止
PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.
64 bytes from 114.114.114.114: icmp_seq=1 ttl=54 time=244 ms
64 bytes from 114.114.114.114: icmp_seq=2 ttl=47 time=244 ms
64 bytes from 114.114.114.114: icmp_seq=3 ttl=67 time=244 ms
--- 114.114.114.114 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 244.023/244.070/244.105/0.034 ms
- TTL,生存时间,或者跳数
- RTT,平均往返延时
基础问题 C10K、C1000K、C10M
C 指的是 Client,
C10K 就是单机同时处理 1 万个请求(并发连接 1 万)的问题;
C1000K 就是单机支持处理 100 万个请求(并发连接 100 万)的问题;
C10M 就是单机支持处理 1 千万个请求(并发连接 1 千万)的问题。
(1)C10K
对 2GB 内存和千兆网卡这等资源有限的服务器来说,只要每个请求处理占用不到 200KB(2GB/10000)的内存和 100Kbit (1000Mbit/10000)的网络带宽就可以。所以,物理资源是足够的,接下来自然是软件的问题,特别是网络的 I/O 模型问题。
在 C10K 以前,Linux 中网络处理都用同步阻塞的方式,也就是每个请求都分配一个进程或者线程,但并发量增加到 10K 后进程或线程的调度、上下文切换等都会成为瓶颈。解决方法是:
(1)一个线程内响应多个网络 I/O:非阻塞 I/O 或者异步 I/O 来处理多个网络请求(I/O 多路复用)。
(2)用更少的线程来服务这些请求。
两种 I/O 事件通知的方式:
(1)水平触发:只要文件描述符可以非阻塞地执行 I/O ,就会触发通知。也就是说,应用程序可以随时检查文件描述符的状态,然后再根据状态,进行 I/O 操作。
(2)边缘触发:只有在文件描述符的状态发生改变(也就是 I/O 请求达到)时,才发送一次通知。这时候,应用程序需要尽可能多地执行 I/O,直到无法继续读写,才可以停止。如果 I/O 没执行完,或者因为某种原因没来得及处理,那么这次通知也就丢失了。
I/O 多路复用(I/O Multiplexing)
(1)使用非阻塞 I/O 和水平触发通知,如 select 或者 poll。水平触发:select 或者 poll 轮询文件描述符列表,找出可以执行的 I/O,然后进行真正的网络 I/O 读写;非阻塞:一个线程同时监控一批套接字的文件描述符。但请求数多时,比较耗时,并且 select 有最大描述符数量的限制,select和poll需要把文件描述符从用户空间传入内核空间,再传出用户空间。
(2)使用非阻塞 I/O 和边缘触发通知,如 epoll。使用红黑树,在内核中管理文件描述符的集合,就不需要应用程序在每次操作时都传入、传出这个集合。使用事件驱动的机制,只关注有 I/O 事件发生的文件描述符,不需要轮询扫描整个集合。由于边缘触发只在文件描述符可读或可写事件发生时才通知,那么应用程序就需要尽可能多地执行 I/O,并要处理更多的异常事件。
(3)使用异步 I/O(Asynchronous I/O,简称为 AIO)。允许应用程序同时发起很多 I/O 操作,而不用等待这些操作完成。而在 I/O 完成后,系统会用事件通知(比如信号或者回调函数)的方式,告诉应用程序。使用难度比较高。
工作模型优化
在 I/O 多路复用中,有两种不同的工作模型:
(1)主进程 + 多个 worker 子进程。主进程执行 bind() + listen() 后,创建多个子进程;每个子进程通过 accept() 或 epoll_wait() ,来处理相同的套接字。主进程主要用来初始化套接字,并管理子进程的生命周期;而 worker 进程,则负责实际的请求处理。
accept() 和 epoll_wait() 调用,还存在一个惊群的问题,nginx 通过竞争全局锁来唤醒子进程。
(2)监听到相同端口的多进程模型。所有的进程都监听相同的接口,并且开启 SO_REUSEPORT 选项,由内核负责将请求负载均衡到这些监听进程中去。
(2)C1000K
epoll 配合线程池,再加上 CPU、内存和网络接口的性能和容量提升,大部分情况下,C100K 可以达到。进一步还可以加上多队列网卡、中断负载均衡、CPU 绑定、RPS/RFS(软中断负载均衡到多个 CPU 核上),以及将网络包的处理卸载(Offload)到网络设备(如 TSO/GSO、LRO/GRO、VXLAN OFFLOAD)等各种硬件和软件的优化。
C1000K 的解决方法,本质上还是构建在 epoll 的非阻塞 I/O 模型上。只不过,除了 I/O 模型之外,还需要从应用程序到 Linux 内核、再到 CPU、内存和网络等各个层次的深度优化,特别是需要借助硬件,来卸载那些原来通过软件处理的大量功能。
(3)C10M
实际上,在 C1000K 问题中,各种软件、硬件的优化很可能都已经做到头了,想实现 1000 万请求的并发,都是极其困难的。究其根本是 Linux 内核协议栈做了太多太繁重的工作。解决方法:
(1)DPDK,跳过内核协议栈,直接由用户态进程通过轮询的方式,来处理网络接收。DPDK 还通过大页、CPU 绑定、内存对齐、流水线并发等多种机制,优化网络包的处理效率。
(2)XDP(eXpress Data Path),Linux 内核提供的一种高性能网络数据路径,允许网络包,在进入内核协议栈之前,就进行处理,也可以带来更高的性能。
但是在大多数场景中,我们并不需要单机并发 1000 万请求。通过调整系统架构,把请求分发到多台服务器中并行处理,才是更简单、扩展性更好的方案。
Linux性能优化从入门到实战:17 网络篇:网络基础的更多相关文章
- Linux性能优化从入门到实战:01 Linux性能优化学习路线
我通过阅读各种相关书籍,从操作系统原理.到 Linux内核,再到硬件驱动程序等等. 把观察到的性能问题跟系统原理关联起来,特别是把系统从应用程序.库函数.系统调用.再到内核和硬件等不同的层级贯 ...
- Linux性能优化从入门到实战:16 文件系统篇:总结磁盘I/O指标/工具、问题定位和调优
(1)磁盘 I/O 性能指标 文件系统和磁盘 I/O 指标对应的工具 文件系统和磁盘 I/O 工具对应的指标 (2)磁盘 I/O 问题定位分析思路 (3)I/O 性能优化思路 Step 1:首先采用 ...
- Linux性能优化从入门到实战:07 CPU篇:CPU性能优化方法
性能优化方法论 动手优化性能之前,需要明确以下三个问题: (1)如何评估性能优化的效果? 确定性能的量化指标.测试优化前的性能指标.测试优化后的性能指标. 量化指标的选择.至少要从应用程序 ...
- Linux性能优化从入门到实战:09 内存篇:Buffer和Cache
Buffer 是缓冲区,而 Cache 是缓存,两者都是数据在内存中的临时存储. 避免跟文中的"缓存"一词混淆,而文中的"缓存",则通指内存中的临时存储 ...
- Linux性能优化从入门到实战:10 内存篇:如何利用Buffer和Cache优化程序的运行效率?
缓存命中率 缓存命中率,是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比,可以衡量缓存使用的好坏.命中率越高,表示使用缓存带来的收益越高,应用程序的性能也就越好. 实际上,缓存是 ...
- Linux性能优化从入门到实战:04 CPU篇:CPU使用率
CPU使用率是单位时间内CPU使用情况的统计,以百分比方式展示. $ top top - 11:46:45 up 7 days, 11:52, 1 user, load average: 0.00 ...
- Linux性能优化从入门到实战:15 文件系统篇:磁盘 I/O
磁盘 磁盘是可以持久化存储的设备,按照存储介质来分类: (1)机械磁盘(硬盘驱动器,Hard Disk Driver,HDD),主要由盘片和读写磁头组成,数据就存储在盘片的环状磁道中.在读写数 ...
- Linux性能优化从入门到实战:08 内存篇:内存基础
内存主要用来存储系统和应用程序的指令.数据.缓存等. 内存映射 物理内存也称为主存,动态随机访问内存(DRAM).只有内核才可以直接访问物理内存. Linux 内核给每个进程都提供了一个独立的 ...
- Linux性能优化从入门到实战:03 CPU篇:CPU上下文切换
linux操作系统是将CPU轮流分配给任务,分时执行的.而每次执行任务时,CPU需要知道CPU寄存器(CPU内置的内存)和程序计数器PC(CPU正在执行指令和下一条指令的位置)值,这些值是CPU执 ...
随机推荐
- 配置maven方法
配置maven方法 1.官网下载maven, http://maven.apache.org/download.cgi Only the binaries are required, so look ...
- [CF46D]Parking Lot
题目:Parking Lot 传送门:http://codeforces.com/problemset/problem/46/D 分析: 做法一: 1)这题和Hotel那题一样,也可以看做是求区间空位 ...
- Beyond Compare4 激活
当你使用过一段时间后会提示有问题,需要激活或者什么. 解决办法: 找到这个路径并删除其下Beyond Compare 4文件夹即可正常使用. C:\Users\******\AppData\Roami ...
- optistruct非线性分析步子步设置
The CNTNLSUB command can be used in the Subcase Information section to continue a nonlinear solution ...
- 主流架构 : MVP
1 背景 MVC 平时开发APP时会发现,activity职责非常重.以MVC角度来看: M:model数据操作层(网络请求,耗时操作,数据存取,其他逻辑操作) V:view,指xml布局文件,其实并 ...
- 关于.NET Core的一些问题和疑惑
1 为什么会出现.NET Core这个东西?即它为了解决什么问题. .NET Core是NET的ECMA标准的一种新的实现.目前.NET已有Framework,Mono,Unity等实现. 原先所有的 ...
- java设置RabbitMQ的消费处理出现:ConditionalRejectingErrorHandler : Execution of Rabbit message listener failed.
WARN 7868 --- [cTaskExecutor-1] s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit messa ...
- 认识Dow(下)
节点属性 在文档对象模型 (DOM) 中,每个节点都是一个对象.DOM 节点有三个重要的属性 : 1. nodeName : 节点的名称 2. nodeValue :节点的值 3. nodeType ...
- 类Runtime
Runtime类的概述和使用 Runtime类概述 每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接.可以通过getRuntime方法获取当前运行时. 应用程序不能 ...
- bash shell:获取当前脚本的绝对路径(pwd/readlink)
有时候,我们需要知道当前执行的输出shell脚本的所在绝对路径,可以用dirname实现. 我们知道 dirname 可以获取一个文件所在的路径,dirname的用处是: 输出已经去除了尾部的”/”字 ...