大家好,欢迎来到IT知识分享网。
主要性能指标
为了能有的放矢,在探讨负载均衡器的性能优化问题之前,我们先要明确如何衡量、评价负载均衡器的性能。下面结合负载均衡器的应用场景,简单介绍网络设备的主要性能指标。
1.带宽
带宽(Bandwidth)一般是衡量网络速度的标志。当我们讨论通信链路的带宽时,一般指链路上每秒传输的最大比特数;当我们讨论网络设备的带宽时,一般指该设备的网络接口支持的每秒传输的最大比特数。
带宽的单位是bps(bits per second)。负载均衡器作为一种网络设备,其网卡速度可以作为带宽的衡量指标。随着网卡技术的进步,千兆、万兆网卡已成为数据中心的主流配置,25GB/40GB/100GB网卡也开始逐步在实际中应用。对于大部分业务场景,网卡的物理带宽对负载均衡器性能的约束已经不再是问题。
2.吞吐量
吞吐量(Throughput)是系统在给定时间内能够处理的信息量的一种度量。在网络数据传输领域,吞吐量是网络从一个节点到另一个节点每秒传输的实际有效比特数,单位是bps(bits per second)。
我们常用“吞吐量”评价一个系统的观测性能,即系统在一段时间内测量到的传输有效比特数的平均值。网络的吞吐量不仅受网络带宽、延时等链路物理性能约束,而且受应用服务软件性能的影响。在一个复杂的系统中,吞吐量最小的节点决定着整个系统的总体吞吐量。
3.延时
延时(Latency)是数据在系统中传输需要的时间,即数据从开始发送到送达对端需要的时间,单位一般用ms(millisecond,毫秒)。延时分为单向延时(One-way Latency)和往返延时(Round-trip Latency),在实际测量时一般采用往返延时。
延时和吞吐量有什么关系?很多人会认为低延时意味着高吞吐量,其实这种观点是不正确的。比如,高负载运行的长肥管道网络(Long FatNetwork)的网络延时很高,但其吞吐量也很大。打个比方,如果把吐吞量看作人的胖瘦,那么延时就相当于人的高矮,这两个指标没有必然的联系。
4.包转发率
包转发率是网络在单位时间内转发的包的数量,单位是pps(packets persecond)。负载均衡器的主要功能是数据转发,作为无状态的四层负载均衡器,包转发率体现了其处理数据包的速度和效率,因此一般将包转发率作为衡量四层负载均衡器最关键的性能指标。网络链路支持的最大包转发率称为“线速”(Wire Speed),根据网络带宽、传输介质和相关协议规范,可以计算出网 络 链 路 的 最 大 包 转 发 率 ( 线 速 ) 和 最 小 包 转 发 率 。
以 万 兆 以 太 网(Ethernet)为例,其网络带宽为10Gbit/s,因为不可能每个比特都是有效数据,以太网两个帧之间有默认12字节的帧间距(Inter-Frame Gap,IFG),每个帧之前还有7字节的前导(Preamble)、1字节的帧首定界符(Start FrameDelimiter,SFD),所以以太网的包转发率为:
在以太网中,一个帧的物理长度的计算方法如表7-1所示。
因 此 , 对 于 万 兆 以 太 网 络 来 说 , 最 大 包 转 发 速 率 范 围 是 0.08 ~14.88Mpps。即,当网络PDU为最小值46字节时,理论线速可达14.88Mpps;当网络PDU为最大值1500字节时,包转发率最大能达到0.08Mpps。对于实际的以太网,每帧的网络PDU不可能完全一样,因此万兆以太网络的实际最大包转发率的范围为0.08~14.88Mpps。
万兆以太网络的最大包转发率为0.08~14.88Mpps,实际包转发率其实为0~14.88Mpps。14.88Mpps是万兆以太网络理论可达的最大线速,要“实现”这个转发速度,至少要满足两个条件:一个条件是数据包发送无间隔,网络有充足的数据包来源;另一个条件是转发设备对数据包进行处理的时间低于67.2ns,即1/(14.88Mpps)s。
5.最大并发连接数
最大并发连接数是网络设备能够同时维护的最大会话连接的数量,其主要受如下几个因素的影响。
● 地址空间:可以用套接字对(SocketPair)唯一标识一个网络连接,一个套接字对由五元组“协议、本地IP地址、本地端口、远端IP地址、远端端口”组成。远端IP地址和远端端口作为服务的访问地址通常是固定的,因此在不考虑连接复用的情况下,一个网络节点可以支持的最大并发连接数不超过“本地IP地址”和“本地可用端口范围”的乘积。
● 内存大小:每个连接的维持都需要一定的内存开销,对于内存较小的设备,内存很可能成为最大连接数的制约因素。
● CPU性能:在负载均衡器中,连接通常以哈希表的形式存在。连接数的增加意味着哈希表冲突的概率增加,而解决哈希表冲突需要消耗额外的CPU时间。因此连接数越大,哈希表冲突的概率就越大,新建连接的速度也越低。在使用短连接形式的服务中,当新建连接的速度下降到小于连接消亡的速度时,系统的最大并发连接数就不能再增加了。
6.每秒事务数
每秒事务数是指在单位时间内能够完成的特定类型的操作数量,单位是tps(transactions per second)。“事务”用于表示由一系列动作组成的具有特定完整意义的操作。对于负载均衡器,我们主要关心如下两种事务性能指标。
● 每秒请求查询数(qps):负载均衡器每秒能转发的用户查询和处理的数量,单位是qps(queries per second),主要用于评价七层负载均衡器的性能。
● 每秒新建连接数(cps):负载均衡器每秒能接受的用户新建连接的数量,单位是cps(connections per second),主要用于评价四层负载均衡器的性能。
性能挑战与分析
中国互联网用户的规模巨大且还在不断增长,这就要求作为流量分发的负载均衡器要有很高的性能。下面具体分析一下传统的负载均衡器的性能问题。
7.2.1 C10K问题及C10M问题
从前文介绍的性能指标我们可以看到,评价系统的性能是有多个维度的。
随着互联网规模的迅猛发展,客户端数目及同一时间内建立的连接数目越来越多,这对服务器处理并发连接的能力提出了挑战。而对于一个大型网站的高性能网络服务器来说,其主要需求和瓶颈往往在于并发。
在互联网的发展初期,1999年Dan Kegel提出了C10K问题,当时服务器以1Gbit/s的速率为10000个客户端提供服务,当硬件没有瓶颈时软件能否抗住高并发?随着“事件驱动模型”的出现,C10K问题得到了有效的解决,例如,事件驱动在Epoll、Kqueue等系统调用上的实现和在以Lighttpd、Nginx、Libevent等应用上的实现。它们改善了原来基于多进程和多线程的模型,大幅提高了服务器的并发处理能力。
如今,大家担心的早已不是C10K问题,而是C10M问题:服务器的软件性能能否跟上业务的发展和硬件的发展?能否每秒处理千万数量级的并发连接和10Gbit、25Gbit,甚至100Gbit的流量,达到14.88Mpps万兆网卡的理论线速或更高的线速极限?SMP(Symmetric Multiprocessing,对称多处理)架构、NUMA(Non-Uniform Memory Access,非统一内存访问)架构下多核心的服务器性能的可扩展性如何?
关于C10K问题和C10M问题,互联网上有大量的文章介绍,写得都很不错,所以这里不再深入探讨,大家可以参考互联网上的文章。下面分析一下传统负载均衡器LVS(Linux Virtual Server,Linux虚拟服务器)的性能瓶颈及其产生的原因,然后介绍一下实现高性能负载均衡的关键技术,再以一个基于DPDK(Data Plane Development Kit,数据平面开发套件)实现的高性能四层负载均衡器为例,展示如何实践这些技术。
7.2.2 LVS性能瓶颈分析
LVS最早由章文嵩开发,核心转发部分是Linux内核Netfilter网络架构中的一个名为IPVS的子模块。随后国内多家公司对其进行了改造,如增加FullNAT转发模式和SYNPROXY功能(见链接[25])、SNAT转发功能等。
其中,FullNAT转发模式被广泛使用的主要原因是其部署和运维十分简单、方便,该转发模式对LVS服务器和后端服务器所处的网络环境没有要求,可以跨二层网络部署,同时不需要在后端服务器上进行额外的IP地址、路由策略、ARP(Address Resolution Protocol,地址解析协议)抑制等复杂配置。
既然LVS的核心模块IPVS是基于Linux内核实现的,那么我们就不得不面对这样一个现实,在高性能网络环境下,内核会成为性能瓶颈。这么说也许有人会奇怪,毕竟内核给人的印象一向是高水准及高性能。实际上,多年来对内核网络部分的优化也从未停止,为何会成为性能瓶颈呢?
我们先看两组数据,如图7-1和图7-2所示,一组来自Google Maglev(见链接[26]),即(Maglev负载均衡器在Linux Kernel模式及Kernel Bypass模式下的性能比较,另一组来自mTCP(见链接[27]),即Linux多核系统下mTCP用户态协议栈模式(mTCP)、REUSEPORT模式(REUSEPORT)、REUSEPORTMultiprocess模式(Multiprocess)及基本模式(Linux)下系统的连接吞吐量性能比较,通过观看图7-1和图7-2中的两组数据我们可以对内核性能瓶颈有一个直观的印象。.
不难看出,一方面,在只有一个CPU核心的情况下,基于内核的方案在转发性能上不如Kernel Bypass方案;另一方面,在SMP/NUMA体系结构具有多核心CPU的情况下,Kernel Bypass方案可以实现性能随CPU核心数量的线性扩展,但是内核却很难做到。
LVS的核心转发部分IPVS是在内核态实现的,虽然不同的版本有PER-CPU连接表、缩小锁的范围、报文尽早处理(pre_routeing)等优化手段,但对于高并发的负载均衡器来说,来自内核的性能问题依然存在。
7.2.3 内核成为瓶颈的原因
为什么内核会成为负载均衡器性能的瓶颈呢?总结下来,大致包括以下几个方面。
● 上下文切换。
● 资源共享与锁的使用。
● 中断风暴。
● 强大且复杂的网络协议栈。
● 数据复制。
下面来依次说明一下。
1.上下文切换
从广义上来说,上下文切换包括用户态/内核态的切换、多进程/线程上下文的切换等。上下文切换有一定的开销,应该尽量避免频繁切换。如果需要提高性能,则应用程序可以选择将任务和CPU核心进行亲和性绑定,而非采用大量线程或进程的模型。就像Nginx中的Worker进程所做的,将Worker进程数量设置为使用的CPU核心数,各个Woker进程之间对等且相互独立,每个Worker进程绑定一个CPU核心,一个请求始终在同一个Worker进程中处理,从而将多进程间上下文切换的开销降到最低。
如果想更好地理解事件驱动模型和异步编程,则可以参考“C10K问题”及其对应的具体的实现:Nginx、Lighttpd、Libevent等。
2.资源共享与锁的使用
我们知道,像UNIX、Linux这样的时分复用操作系统的一个基本目标就是提高资源利用率,使多个用户或多个进程/线程同时使用操作系统的资源,并且让它们都以为自己“独占”了资源。通过多个进程/线程共享资源并分时切换的实现,一个CPU核心可以并发执行多个任务。现代的SMP、NUMA等技术更是让多个进程/线程在物理上并行化。不管是什么形式,在有资源共享的情况下,一定要保护资源,不然就会产生竞争条件。
为了使共享资源不会被竞争破坏,只能为其加上各式各样的锁,或者采用类似技术,如原子变量、内存屏障等。
加锁就意味着,当拿不到资源时只能等待(不论是切换出去等,还是忙等),这会造成CPU浪费,降低性能。在系统使用SMP或NUMA架构和进程、线程数量变多的情况下,上述CPU浪费的问题则更加突出。虽然人们采用各种优化锁的技术,如读/写锁、spinlock、RCU、顺序锁等来减少锁对性能的影响,但是不管怎么个“锁”法,都不如“没锁”来得高效。
不过,从内核资源共享的本质上来看,不使用锁是不可能的。就内核的协议栈来说,虚拟设备需要锁、L2/L3/L4分用的表需要锁、Netfilter的Hook表需要锁、Conntrack需要锁、路由/ARP/IP地址需要锁、TCP/UDP层需要锁、Socket层需要锁、TC(qdisc)需要锁等。
毕竟,UNIX/Linux是“通用目的操作系统”,从来就不是为单个进程或某个特定高性能场景服务的“专用设备”而设计的,设计之初也没有考虑要为SMP、NUMA多CPU核心进行优化,多年来的性能提升和优化只能在资源共享、公平的前提下进行。比如,缩小锁的范围,使用合适的锁,有些地方甚至可以用PER-CPU的资源分配方式避免锁的使用。但终究太多资源共享了,不能破坏多用户、多任务、通用系统、公平这些大前提。因此,在某些特殊的高性能领域内,内核协议栈表现得不够高效就比较好理解了。这些领域包括高速数据包转发、抗DDoS(Distributed Denial of Service,分布式拒绝服务)攻击等。
另外,硬件性能,如网卡性能逐渐提高,网卡速度从100Mbit/s提高到1Gbit/s、10Gbit/s、25Gbit/s、100Gbit/s,硬件不再是性能的瓶颈。如果只是为了某个单一类型的应用,硬件是可以定制的。对于这种高性能要求的专有应用情况,为什么还要运行一个完整的操作系统及其复杂通用的协议栈呢?
3.中断风暴
我们知道,内核网卡驱动的收发包部分是通过硬件中断和下半部(bottom-half)软中断实现的,通过NAPI(New API)接口实现了“中断加轮询”的方式,一方面利用中断来及时处理响应,避免对用户造成延迟,另一方面为兼顾吞吐量等性能,每次软中断处理函数都会以一定的配额尝试轮询(polling)多个设备的报文。这种机制与各种硬件卸载(offload)和软件优化方案相结合,能够最大限度地兼顾大部分场景的延时和性能需求。
然而在高性能、高包转发率的特殊网络应用场景下,这种模式依然不够好,当网络I/O(尤其是包转发率)非常大时,在Linux中通过top命令就可以看到CPU被大量消耗在软中断及其处理函数上。在实践中,我们可以设置“中断/CPU亲和性”,充分利用网卡多队列和CPU多核心的能力,只是收发包依然会成为系统瓶颈。
关于中断风暴造成CPU浪费及性能瓶颈的问题,DPDK的答案是用轮询代替中断。为什么是“轮询”?提到轮询,大家首先想到:轮询间隔太短浪费CPU,轮询间隔过长会造成不必要的延迟和收包不及时,怎么设置这个轮询间隔都不好。但是,如果不考虑CPU浪费,没有间隔,以“死循环”方式进行轮询,每次轮询时都尝试批量读取和处理数据,那么不仅能解决延迟问题,性能也能得到大幅提升。毕竟这个CPU的核心在于全力进行包的接收和处理。
4.强大且复杂的网络协议栈
内核网络协议栈实现了太多的复杂功能,如Netfilter及相关的表和规则、Conntrack系统、各种各样的QoS(Quality of Service,服务质量)。这意味着一个数据包在内核中需要经过很多函数进行处理,路径非常长,看似微小的消耗会积少成多。即便将每个函数都优化到极致,不去耗费CPU太多时间,那么如果一个数据通过的函数调用链过长,其累计的性能消耗也会比较大。如图7-3所示(图片来自ACM论文,见链接[28]),内核网络协议栈收发部分的高度是不是特别“高”?这是由于多层函数调用累计的消耗所造成的。在某些特殊的场景下,其实不需要那么多功能。
5.数据复制
在用户态和内核态之间进行直接的数据复制会十分消耗CPU的资源。当网络数据通过内核和用户空间边界时,不但会产生系统调用的开销,还会产生一次额外的数据复制的开销。如果从网卡DMA到内存,那么整个数据复制的处理过程中不存在额外的数据复制,则性能也可以获得不错的提升。Linux的Sendfile机制就可以避免这种数据复制开销。而Kernel Bypass方案可以避免内核态和用户态之间直接进行数据复制。
说明一下,数据复制对于内核态实现的LVS的性能的影响较小,因为内核态实现的IPVS是不需要把数据复制到用户态后再复制到内核态的。但是,对于在内核态下进行收发包,而在用户态下处理应用逻辑的上层应用服务,进行数据复制时CPU开销是一个不可忽略的性能影响因素。我们只是指出数据复制非常影响性能,实现时要尽可能避免。
本文给大家讲解的内容是性能优化-性能挑战与分析
- 下篇文章给大家讲解是高性能四层负载均衡关键技术
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/57844.html