栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 产品运营 > 运营营销 > 网络营销

网络优化,系统性能调优

网络营销 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

网络优化,系统性能调优

大家好,今天分享一篇关于后台服务器性能优化的网络性能优化的文章。希望你能对Linux网络有更深入的了解。

曾几何时,一切都是那么简单。网卡慢,只有一个队列。当数据包到达时,网卡复制数据包并通过DMA发送中断,Linux内核收集这些数据包并完成中断处理。随着网卡速度的提高,基于中断的模型可能会由于大量的传入数据包而导致IRQ风暴。这将消耗大部分CPU功率,并冻结系统。

为了解决这个问题,NAPI(中断+轮询)被提出。当内核从网卡接收到中断时,它开始轮询设备并尽快收集队列中的数据包。NAPI可以用普通的1 Gbps网卡很好地工作。但是,对于10Gbps、20Gbps甚至40Gbps的网卡,NAPI可能不够用。如果我们仍然使用一个CPU和一个队列来接收数据包,这些卡将需要更快的CPU。

好在现在多核CPU很流行,那为什么不并行处理包呢?

RSS:接收端缩放

接收端缩放(RSS)是机构具有多个RX/TX队列的过程的数据包。当具有RSS的网卡接收到数据包时,它会对数据包应用过滤器,并将数据包分发到RX队列。过滤器通常是一个哈希函数,可以通过“ethtool -X”进行配置。如果要在前3个队列中平均分配流量:

# ethtool -X eth0 equal 3

或者,如果您找到一个特别有用的神奇散列键:

# ethtool -X eth0 hkey <magic hash key>

对于低延迟网络,除了过滤器,CPU亲和力也很重要。最佳设置是为队列分配一个专用CPU。首先,检查/proc/interrupt以找出IRQ号,然后将CPU位掩码设置为/proc/IRQ/< IRQ _ NUMBER & gt;/smp_affinity分配专用CPU。为了防止设置被覆盖,必须禁用守护程序irqbalance。请注意,根据内核文档,超线程对中断处理没有任何好处,所以最好将队列数量与物理CPU内核数量相匹配。

RPS:接收数据包控制

提供了RSS硬件队列,并在Linux内核中实现了一种称为接收包导向(RPS)的软件队列机制。

当驱动程序收到数据包时,它将数据包包装在套接字缓冲区(sk_buff)中,该缓冲区包含数据包的u32哈希值。Hash就是所谓的第四层hash (l4 hash),以源IP、源端口、目的IP、目的端口为基础,通过网卡或者__skb_set_sw_hash()计算。由于同一TCP/UDP连接(流)的每个数据包共享相同的哈希值,因此使用相同的CPU来处理它们是合理的。

RPS的基本思想是根据每个队列的rps_map将同一个流的数据包发送到特定的CPU。rps_map的结构是这样的:map动态更改为/sys/class/net/< dev & gt;/queues/rx-& lt;n & gt/RPS _ CPU .例如,我们希望队列使用前三个CPU。在一个有八个CPU的系统中,我们首先为0x7构造一个位掩码0 0 0 0 0 0 1 1 1,然后

#echo 7 > /sys/class/net /eth0/queues/rx-0/rps_cpus

这样可以保证从eth0中队列0接收到的数据包进入CPU 1~3。驱动在sk_buff中封装一个包后,会到达netif_rx_internal()或者netif_receive_skb_internal(),然后get_rps_cpu()

struct rps_map {

将被调用以将散列映射到rps_map中的条目,即CPU id。获得CPU id后,enqueue_to_backlog()将sk_buff放入特定的CPU队列中,以便进一步处理。每个cpu的队列在每个CPU的变量softnet_data中分配。

使用RPS的优点是包处理的负载可以在CPU之间分担。但如果RSS可用,可能就没必要了,因为网卡已经对每个队列/CPU的包进行了排序。但是,如果队列中有更多的CPU,RPS仍然可以工作。在这种情况下,每个队列可以与多个CPU相关联,并在它们之间分发数据包。

Linux ++后台服务器开发架构师免费学习地址:C/C++Linux服务器开发/后台架构师【零音教育】-学习视频教程-腾讯课堂

【文章福利】:边肖整理了一些我个人觉得比较好的学习书籍和视频资料,分享在群档案里。如果有需要,可以自己添加!~点击832218493加入(需要自己拿)

RFS: Receive Flow Steering

虽然RPS基于流分发数据包,但它不考虑用户空之间的应用。应用程序可能运行在CPU A上,内核会把数据包放到CPU B的队列中,由于CPU A只能使用自己的缓存,缓存在CPU B中的数据包就变得没用了。接收流导向(RFS)进一步扩展到RPS的应用。

RFS维护全局流到CPU的表rps_sock_flow_table,而不是每个队列的散列到CPU的映射:这个掩码用于将散列值映射到表的索引。因为表的大小将被舍入到2的幂,所以掩码被设置为table_size-1。

struct rps_sock_flow_table {

并且很容易找到索引:一个sk_buff和hash & scok _ table->: mask .

条目由rps_cpu_mask分为流id和CPU id。低位用于CPU id,而高位用于流id。当应用程序对socket (inet_recvmsg()、inet_sendmsg()、inet_sendpage()、tcp_splice_read())进行操作时,它会调用sock_rps_record_flow()来更新sock流表。

当包到达时,将调用get_rps_cpu()来决定使用哪个cpu队列。下面是get_rps_cpu()如何确定一个包的cpu。

ident = sock _ flow _ table-& gt;ents[hash & sock _ flow _ table-& gt;面膜];

使用流表掩码查找条目的索引,并检查散列的高位是否与条目匹配。如果是,它将从条目中检索CPU id,并将CPU分配给数据包。如果这个散列与任何条目都不匹配,它就退回到使用RPS映射。

您可以通过rps_sock_flow_entries调整sock流表的大小。例如,如果我们想将表格大小设置为32768:

#echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

sock流表虽然提高了应用的本地性,但是也带来了一个问题。当调度程序将应用程序迁移到新的CPU时,旧CPU队列中的剩余数据包会变得未完成,应用程序可能会得到无序的数据包。为了解决这个问题,RFS使用每个队列的rps_dev_flow_table来跟踪未完成的数据包。

下面的结构是rps_dev_flow_table:在sock流表中,类似的rps_dev_flow_table也是使用table_size-1作为掩码,表的大小必须四舍五入到2的幂。当业务分组被排队时,last_qtail被更新。

struct rps_dev_flow {

到CPU队列的末尾。如果应用程序迁移到新的cpu,sock流表将反映这一变化,get_rps_cpu()将为该流设置新的CPU。在设置一个新的CPU之前,get_rps_cpu()会检查当前队列的头是否已经过了last_qtail。如果是,则意味着队列中不再有未完成的数据包,可以安全地更换CPU。否则get_rps_cpu()仍然会使用RPS _ dev _ flow-->:cpu中记录的旧CPU。

每个队列的流表(rps_dev_flow_table)的大小可以通过sysfs接口进行配置:

/sys/class/net/<dev>/queues/rx-<n>/rps_flow_cnt

建议将rps_flow_cnt设置为(rps_sock_flow_entries/N),N为RX队列数(假设流均匀分布在队列中)。

ARFS:加速接收流量转向

加速接收流导向(aRFS)进一步扩展了RFS,以过滤RX队列硬件。要启用aRFS,它需要一个带有可编程元组过滤器和驱动程序支持的网卡。启用ntuple过滤器。

# ethtool -K eth0 ntuple on

为了使驱动程序支持aRFS,它必须实现ndo_rx_flow_steer来帮助set_rps_cpu()配置硬件过滤器。当Get_rps_cpu()决定为流分配新的cpu时,它将调用set_rps_cpu()。Set_rps_cpu()首先检查网卡是否支持ntuple过滤器。如果是,它将查询rx_cpu_rmap,为该流找到合适的rx队列。

Rx_cpu_rmap是驱动维护的特殊映射。这个映射用于找出哪个RX队列适合CPU。它可以是与给定CPU直接关联的队列,也可以是离缓存位置中的处理CPU最近的队列。获得RX队列索引后,set_rps_cpu()调用NDO _ Rx _ flow _ steel()通知驱动程序为给定的流创建一个新的过滤器。O _ Rx _ flow _ steer()将返回过滤器id,它将存储在每个队列的流表中。

除了实现ndo_rx_flow_steer(),驱动程序还必须调用rps_may_expire_flow()来定期检查过滤器是否仍然有效,并删除过期的过滤器。

SO_REUSEPORT

linux man文档中的一段描述了它的功能:

新的套接字选项允许同一主机上的多个套接字绑定到同一端口,旨在提高运行在多核系统上的多线程网络服务器应用程序的性能。

简单来说,SO_REUSEPORT支持多个进程或线程绑定到同一个端口,以提高服务器程序的性能。我们想知道这个功能为什么这么受欢迎(经常被大厂的面试官问到),要解决什么问题。

Linux系统上的后台应用,为了利用多核的优势,一般使用以下几种典型的多进程/多线程服务器模型:

单线程listen/accept,多个工作线程接收任务分发,虽CPU的工作负载不再是问题,但会存在:

1.单线程监听器,在处理高速海量连接时,也会成为瓶颈;

2.CPU缓存线故障(丢失套接字结构)是严重现象;

所有工作线程都accept()在同一个服务器套接字上呢,一样存在问题:

1.多线程访问服务器套接字锁存在严重竞争;

2.在高负载下,线程间的处理是不平衡的,有时会达到3:1的不平衡比例;

3.导致CPU缓存线跳跃;

4.在繁忙的CPU上有很大的延迟;

虽然上面的模型可以绑定线程和CPU核,但是会有以下问题:

单一listener工作线程在高速的连接接入处理时会成为瓶颈缓存行跳跃很难做到CPU之间的负载均衡随着核数的扩展,性能并没有随着提升

SO_REUSEPORT支持多个进程或线程绑定到同一个端口:

允许多个套接字 bind()/listen() 同一个TCP/UDP端口 1.每一个线程拥有自己的服务器套接字。 2.在服务器套接字上没有了锁的竞争。内核层面实现负载均衡。安全层面,监听同一个端口的套接字只能位于同一个用户下面。

其核心的实现主要包括三点:

扩展socket option,增加 SO_REUSEPORT选项,用来设置 reuseport。修改 bind 系统调用实现,以便支持可以绑定到相同的 IP 和端口。修改处理新建连接的实现,查找 listener 的时候,能够支持在监听相同 IP 和端口的多个 sock 之间均衡选择

带来意义

CPU之间平衡处理,水平扩展,模型简单,维护方便了,进程的管理和应用逻辑解耦,进程的管理水平扩展权限下放给程序员/管理员,可以根据实际进行控制进程启动/关闭,增加了灵活性。这带来了一个较为微观的水平扩展思路,线程多少是否合适,状态是否存在共享,降低单个进程的资源依赖,针对无状态的服务器架构最为适合。针对对客户端而言,表面上感受不到其变动,因为这些工作完全在服务器端进行。服务器无缝重启/切换,热更新,提供新的可能性。我们迭代了一版本,需要部署到线上,为之启动一个新的进程后,稍后关闭旧版本进程程序,服务一直在运行中不间断,需要平衡过度。这就像Erlang语言层面所提供的热更新一样。

SO_REUSEPORT已知问题

SO_REUSEPORT分为两种模式,即热备份模式和负载均衡模式,在早期的内核版本中,即便是加入对reuseport选项的支持,也仅仅为热备份模式,而在3.9内核之后,则全部改为了负载均衡模式,两种模式没有共存,虽然我一直都希望它们可以共存。SO_REUSEPORT根据数据包的四元组{src ip, src port, dst ip, dst port}和当前绑定同一个端口的服务器套接字数量进行数据包分发。若服务器套接字数量产生变化,内核会把本该上一个服务器套接字所处理的客户端连接所发送的数据包(比如三次握手期间的半连接,以及已经完成握手但在队列中排队的连接)分发到其它的服务器套接字上面,可能会导致客户端请求失败。

如何预防以上已知问题,一般的解决方法如下:1。使用固定数量的服务器插座,在负载繁忙时不要轻易更换。2.允许多个服务器套接字共享Tcp请求表。3.选择本地套接字处理,不使用四元组作为哈希值,如选择会话ID或进程ID,选择属于同一个CPU的套接字。4.使用一致的哈希算法。

与其他特征的关系

1.SO_REUSEADDR:主要是地址复用1.1允许time_wait状态下的socket快速复用原来的ip+port 1.2,使0.0.0.0(ipv4通配符地址)不与其他地址(127.0.0.1和10.0.0.x)冲突。1.3 SO_REUSEADDR的缺点是2。配合RFS/RPS/XPS-mq,可以获得更进一步的性能。2.1.服务器线程绑定到CPUs 2.2.RPS将TCP SYN数据包分发到相应的CPU核心。2.3.TCP连接绑定到CPU线程Accept () 2.4。XPS-MQ(多队列传输数据包控制)、传输队列和CPU。发送数据2.5。RFS/RPS保证了同一个连接的后续数据包会被分发到同一个CPU上,而且网卡的接收队列已经绑定到CPU上,所以RFS/RPS不用设置,要注意硬件是否支持。目的是软、硬中断的接收、处理等。的数据包在一个CPU内核上并行处理,以尽可能最大限度地提高资源利用率。

SO_REUSEPORT的演变

3.9之前内核,能够让多个socket同时绑定完全相同的ip+port,但不能实现负载均衡,实现是热备。Linux 3.9之后,能够让多个socket同时绑定完全相同的ip+port,可以实现负载均衡。Linux4.5版本后,内核引入了reuseport groups,它将绑定到同一个IP和Port,并且设置了SO_REUSEPORT选项的socket组织到一个group内部。目的是加快socket查询。

总结

Linux网络协议栈存在问题

TCP处理&多核一个完整的TCP连接,中断发生在一个CPU核上,但应用数据处理可能会在另外一个核上不同CPU核心处理,带来了锁竞争和CPU Cache Miss(波动不平衡)多个进程监听一个TCP套接字,共享一个listen queue队列用于连接管理全局哈希表格,存在资源竞争epoll IO模型多进程的惊群现象Linux VFS的同步损耗严重Socket被VFS管理VFS对文件节点Inode和目录Dentry有同步需求SOCKET只需要在内存中存在即可,非严格意义上文件系统,不需要Inode和Dentry代码层面略过不必须的常规锁,但又保持了足够的兼容性

RSS,RPS,RFS和aRFS,这些机制都是在Linux 3.0之前引入的。SO_REUSEPORT选项是在Linux 3.9中引入内核的,所以大多数发行版都已经包含并启用了它们。了解它们,以便为我们的服务器系统找到最佳性能配置。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/819739.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号