转——浅谈如何提升服务器并发处理能力

目录linux

 

(一)什么是服务器并发处理能力nginx

(二)有什么方法衡量服务器并发处理能力web

1.吞吐率算法

2.压力测试sql

(三)怎么提升服务器的并发处理能力shell

1,提升CPU并发计算能力
(1)多进程&多线程
(2)减小进程切换,使用线程,考虑进程绑定CPU
(3)减小使用没必要要的锁,考虑无锁编程
(4)考虑进程优先级
(5)关注系统负载
(6)关注CPU使用率,除了用户空间和内核空间的CPU使用率之外,还要关注I/O wait数据库

2,减小系统调用apache

3,考虑减小内存分配和释放编程

(1)改善数据结构和算法复制度后端

(2)使用内存池

(3)考虑使用共享内存

4,考虑使用持久链接

5,改进I/O模型

(1)DMA技术

(2)异步I/O
(3)改进多路I/O就绪通知策略,epoll
(4)Sendfile
(5)内存映射
(6)直接I/O

6,改进服务器并发策略

1)一个进程处理一个链接,非阻塞I/O,使用长链接

 

(2)一个进程处理多个链接,异步I/O, 使用长链接

 

7,改进硬件环境

 

 

 

(一)什么是服务器并发处理能力?

 

 

一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强

 

服务器的本质工做就是,争取以最快的速度将内核缓冲区中的用户请求数据一个不剩地都拿出来,而后尽快处理,再将响应数据放到一块又可以与发送数据的缓冲区中,接着处理下一拨请求。

 

 

(二)有什么方法衡量服务器并发处理能力?

 

一,吞吐率

 

量化指标:吞吐率,单位时间里服务器处理的最大请求数,单位req/s

 

再深刻一些,HTTP请求一般是对不一样资源的请求,也就是请求不一样的URL,有的是请求图片,有的是获取动态内容,有的是静态页面,显然这些请求所花费的时间各不相同,而这些请求再不一样时间的组成比例又是不肯定的,因此实际状况下的吞吐率是很是复杂的。

 

 

正由于这些请求的性质不一样,因此服务器并发能力强弱关键在于如何正对不一样的请求性质来设计最优并发策略。如一台服务器处理诸多不一样性质的请求,在必定程度上使得服务器的性能没法充分发挥。而并发策略的设计就是在服务器同时处理较多请求时,合理协调和充分利用CPU计算和I/O操做,使其在较大并发用户数的状况下提供较高吞吐率。

 

 

另外,实际上多少用户同时发来请求并非服务器所能决定的,一旦实际并发用户数过多,则势必影响站点质量。因此得出最大并发用户数的意义,在于了解服务器的承载能力,而且结合用户规模考虑适当的扩展方案。

 

在考虑用户模型时,用户访问 web站点时一般使用浏览器,浏览器对于同一域名下URL的并发下载是多线程的,不过有最大限制的,因此前面说到的最大并发数,具体到真实的用户,可能不是一对一的关系。

 

而从服务器角度,实际并发用户数的能够理解为服务器当前维护的表明不一样用户的文件描述符总数,也就是并发链接数。服务器通常会限制同时服务的最多用户数,好比apache的MaxClents参数。

 

这里再深刻一下,对于服务器来讲,服务器但愿支持高吞吐率,对于用户来讲,用户只但愿等待最少的时间,显然,双方不能知足,因此双方利益的平衡点,就是咱们但愿的最大并发用户数。

 

 

 

二,压力测试

 

有一个原理必定要先搞清楚,假如100个用户同时向服务器分别进行10个请求,与1个用户向服务器连续进行1000次请求,对服务器的压力是同样吗?其实是不同的,因对每个用户,连续发送请求其实是指发送一个请求并接收到响应数据后再发送下一个请求。这样对于1个用户向服务器连续进行1000次请求, 任什么时候刻服务器的网卡接收缓冲区中只有1个请求,而对于100个用户同时向服务器分别进行10个请求,服务器的网卡接收缓冲区最多有100个等待处理的请求,显然这时的服务器压力更大。

 

压力测试前提考虑的条件:

1) 并发用户数

2) 总请求数

3) 请求资源描述

 

并发用户数是指某一时刻同时向服务器发送请求的用户总数。

 

 

压力测试中关系的时间有细分如下2种,

(1) 用户平均请求等待时间 

(这里暂不把数据在网络的传输时间,还有用户PC本地的计算时间计算入内)

(2) 服务器平均请求处理时间

 

 

用户平均请求等待时间主要用于衡量服务器在必定并发用户数下,单个用户的服务质量;而服务器平均请求处理时间就是吞吐率的倒数,通常来讲,用户平均请求等待时间=服务器平均请求处理时间*并发用户数

 

  

 

(三)怎么提升服务器的并发处理能力呢?

 

一,提升CPU并发计算能力

 

 

服务器之因此能够同时处理多个请求,在于操做系统经过多执行流体系设计使得多个任务能够轮流使用系统资源,这些资源包括CPU,内存以及I/O. 这里的I/O主要指磁盘I/O, 和网络I/O

 

 

(1)多进程&多线程

 

多执行流的通常实现就是进程。多进程的好处不只在于CPU时间的轮流使用,还在于对CPU计算和I/O操做进行很好的重叠利用,这里的I/O主要指磁盘I/O和网络I/O. 实际上,大多数进程的时间主要消耗在I/O操做上,现代计算机的DMA技术可让CPU不参与I/O操做的全过程,好比进程经过系统调用,使得CPU向网卡或者磁盘等I/O设备发出指令,而后进程被挂起,释放出CPU资源,等待I/O设备完成工做后经过中断来通知进程从新就绪。对于单任务而言,CPU大部分时间空闲,这时候多进程的做用尤其重要。

 

并且进程的优越性还在其相互独立带来的稳定性和健壮性方面。

 

进程的缺点: 每一个进程都有本身的独立空间和生命周期。当子进程被父进程建立后,便将父进程地址空间的全部数据复制到本身的地址空间中,彻底继承父进程的上下文信息。进程的建立使用fork()系统调用,仍是有必定的开销的,这个开销若太频繁,其可能成为影响性能的主要因素。

 

 

 

 

 

 

那是否越多进程数越好呢? 请看下面讨论:

(2)减小进程切换

 

进程拥有独立的内存空间,每一个进程都只能共享CPU寄存器。一个进程被挂起的本质是将它在CPU寄存器中的数据拿出来暂存在内存态堆栈着那个,而一个进程恢复工做的本质就是把它的数据从新装入CPU寄存器,这段装入和移出的数据称为“硬件上下文”,除此以外,进程上下文还包含进程容许所需的一切状态信息。

 

当硬件上下文频繁装入和移出时,所消耗的时间是很是可观的。可用Nmon工具监视服务器每秒的上下文切换次数。

 

为了尽可能减小上下文切换次数,最简单的作法就是减小进程数,尽可能使用线程并配合其它I/O模型来设计并发策略。

 
还能够考虑使用进程绑定CPU技术,增长CPU缓存的命中率。若进程不断在各CPU上切换,这样旧的CPU缓存就会失效。

 

(3)减小使用没必要要的锁

服务器处理大量并发请求时,多个请求处理任务时存在一些资源抢占竞争,这时通常采用“锁”机制来控制资源的占用,当一个任务占用资源时,咱们锁住资源,这时其它任务都在等待锁的释放,这个现象称为锁竞争。

 

经过锁竞争的本质,咱们要意识到尽可能减小并发请求对于共享资源的竞争。好比在容许状况下关闭服务器访问日志,这能够大大减小在锁等待时的延迟时间。要最大程度减小无辜的等待时间。

 

这里说下无锁编程,就是由内核完成这个锁机制,主要是使用原子操做替代锁来实现对共享资源的访问保护 ,使用原子操做时,在进行实际的写操做时,使用了lock指令,这样就能够阻止其余任务写这块内存,避免出现数据竞争现象。原子操做速度比锁快,通常要快一倍以上。

 

例如fwrite(), fopen(),其是使用append方式写文件,其原理就是使用了无锁编程,无锁编程的复杂度高,可是效率快,并且发生死锁几率低。

 

(4)除了上述所说,要优化服务器的并发处理能力,还要考虑进程优先级(可由进程决定),进程调度器会动态调整运行队列中进程的优先级,经过top观察进程的PR值

 

 

(5)还要关注系统负载,可在任什么时候刻查看/proc/loadavg, top中的load average也可看出

 

 

(6)还要关注CPU使用率,除了用户空间和内核空间的CPU使用率之外,还要关注I/O wait,它是指CPU空闲而且等待I/O操做完成的时间比例。(top中查看wa的值)

 

 

二,考虑系统调用

 

进程若运行在用户态,这时可以使用CPU和内存来完成一些任务,而当进程须要对硬件外设进行操做的时候(如读取磁盘文件,发送网络数据等),就必须切换到内核态,这时它拥有更多的权力来操纵整个计算机。

 

而系统调用涉及进程从用户态到内核态的切换,致使必定的内存交换,这也是必定程度上的上下文切换,因此系统调用的开销一般认为比较昂贵的。

 

因此要减小没必要要的系统调用,也是服务器性能优化的一个方面。例如在apache中,修改httpd.conf文件,能够减小对文件路径中各级目录下检测是否存在.htacess文件这个open()系统调用; 还能够修改httpd.conf文件来减小多余的gettimeofday()系统调用。

 

 但有时若使用一些简单的系统调用能代替大量的逻辑运算,这样反而使用系统调用更能优化性能

 

 

 

 

三,考虑减小内存分配和释放

 

服务器的工做过程当中,须要大量的内存,使得内存的分配和释放工做尤其重要。

 

能够经过改善数据结构和算法复制度来适当减小中间临时变量的内存分配及数据复制时间,而服务器自己也使用了各自的策略来提升效率。

 

例如Apache,在运行开始时一次申请大片的内存做为内存池,若随后须要时就在内存池中直接获取,不须要再次分配,避免了频繁的内存分配和释放引发的内存整理时间。

 

 

再如Nginx使用多线程来处理请求,使得多个线程之间能够共享内存资源,从而令它的内存整体使用量大大减小,另外,nginx分阶段的内存分配策略,按需分配,及时释放,使得内存使用量保持在很小的数量范围。

 

 

顺便说下 Linux进程的地址空间分段

一、栈(存放着局部变量和函数参数等数据),向下生长   (可读可写可执行)

二、堆(给动态分配内存是使用),向上生长        (可读可写可执行)

三、数据段(保存全局数据和静态数据)              (可读可写不可执行)

四、代码段(保存代码)                       (可读可执行不可写)

 

 

 

(1)  代码段(.text)。这里存放的是CPU要执行的指令。代码段是可共享的,相同的代码在内存中只会有一个拷贝,同时这个段是只读的,防止程序因为错误而修改自身的指令。

(2)  初始化数据段(.data)。这里存放的是程序中须要明确赋初始值的变量,例如位于全部函数以外的全局变量:int val=100。须要强调的是,以上两段都是位于程序的可执行文件中,内核在调用exec函数启动该程序时从源程序文件中读入。

(3)    未初始化数据段(.bss)。位于这一段中的数据,内核在执行该程序前,将其初始化为0或者null。例如出如今任何函数以外的全局变量:int sum;

(4)    堆(Heap)。这个段用于在程序中进行动态内存申请,例如常常用到的malloc,new系列函数就是从这个段中申请内存。

(5)    栈(Stack)。函数中的局部变量以及在函数调用过程当中产生的临时变量都保存在此段中

 

 

还能够考虑使用共享内存

共享内存指在多处理器的计算机系统中,能够被不一样中央处理器(CPU)访问的大容量内存,也能够由不一样进程共享,是很是快的进程通讯方式。

可是使用共享内存也有很差的地方,就是对于多机器时数据很差统一

 

shell命令ipcs可用来显示系统下共享内存的状态,函数shmget能够建立或打开一块共享内存区,函数shmat将一个存在的共享内存段链接到本进程空间, 函数shmctl能够对共享内存段进行多种操做,函数shmdt函数分离该共享内存

 

 

四,考虑使用持久链接

 

持久链接也为长链接,它自己是TCP通讯的一种普通方式,即在一次TCP链接中持续发送多分数据而不断开链接,与它相反的方式称为短链接,也就是创建链接后发送一份数据就断开,而后再次创建链接发送下一份数据, 周而复始。是否采用持久链接,彻底取决于应用特色。从性能角度看,创建TCP链接的操做自己是一项不小的开销,在容许的状况下,链接次数越少,越有利于性能的提高; 尤为对于密集型的图片或网页等小数据请求处理有明显的加速所用。

 

 

 

HTTP长链接须要浏览器和web服务器的共同协做,目前浏览器广泛支持长链接,表如今其发出的HTTP请求数据头中包含关于长链接的声明,以下: Connection: Keep-Alive

 

主流的web服务器都支持长链接,好比apache中,能够用KeepAlive off关闭长链接。

 

对于长链接的有效使用,还有关键一点在于长链接超时时间的设置,即长链接在何时关闭吗? Apache的默认设置为5s, 若这个时间设置过长,则可能致使资源无效占有,维持大量空闲进程,影响服务器性能。

 

 

 

 

五,改进I/O 模型

I/O操做根据设备的不一样分为不少类型,好比内存I/O, 网络I/O, 磁盘I/O.  对于网络I/O和磁盘I/O, 它们的速度要慢不少,尽管使用RAID磁盘阵列可经过并行磁盘磁盘来加快磁盘I/O速度,购买大连独享网络带宽以及使用高带宽网络适配器能够提升网络i/O的速度。但这些I/O操做须要内核系统调用来完成,这些须要CPU来调度,这使得CPU不得不浪费宝贵的时间来等待慢速I/O操做咱们但愿让CPU足够少的时间在i/O操做的调度上,如何让高速的CPU和慢速的I/O设备更好地协调工做,是现代计算机一直探讨的话题。各类I/O模型的本质区别在于CPU的参与方式。

 

(1)DMA技术I/O设备和内存之间的数据传输方式由DMA控制器完成。在DMA模式下,CPU只需向DMA下达命令,让DMA控制器来处理数据的传送,这样能够大大节省系统资源。

 

(2)异步I/O

 

异步I/O指主动请求数据后即可以继续处理其它任务,随后等待I/O操做的通知,这样进程在数据读写时不发生阻塞。而同步则在数据就绪后在读写时必须阻塞。

 

 

 

异步I/O是非阻塞的,当函数返回时,真正的I/O传输还没开始,这让CPU处理和I/O操做达到很好的重叠。

 

 

 

 

 

顺便说说同步阻塞I/O的缺点,其虽然能够和多进程有效利用CPU资源,但代价是占用了大量的内存开销。而同步非阻塞I/O须要进程执行屡次轮训查看数据是否就绪,花费了大量的CPU时间。

 

(3)改进多路I/O就绪通知策略,epoll服务器同时处理大量的文件描述符是必不可少的,若采用同步非阻塞I/O模型,若同时接收TCP链接的数据,就必须轮流对每一个socket调用接收数据的方法,无论这些socket有没有可接收的数据,都要询问一次,假如大部分socket并无数据能够接收,那么进程便会浪费不少CPU时间用于检查这些socket.有没有能够接收的数据, 多路I/O就绪通知的出现,提供了对大量文件描述符就绪检查的高性能方案,它容许进程经过一种方法同时监视全部文件描述符,并能够快速得到全部就绪的文件描述符,而后只针对这些文件描述符进行数据访问。

 

 

 

下面详细介绍被公认为linux 2.6下性能最好的多路I/O就绪通知方法epoll., 几乎具有select, poll, /dev/poll等模型的所有优势

 

 

 

epoll能够同时支持水平触发和边缘触发,理论上边缘触发性能更高,可是代码实现复杂,由于任何意外的丢失事件都会形成请求处理错误。

 

 

 

epoll主要有2大改进:

 

(1)epoll只告知就绪的文件描述符,并且当调用epoll_wait()得到文件描述符时,返回并非实际的描述符,而是一个表明就绪描述符数量的值,而后只需去epoll指定的一个数组中依次取得相应数量的文件描述符便可,这里使用了内存映射(mmap)技术,这样完全省掉了这些文件描述符在系统调用时复制的开销。

 

 

 

(2)epoll采用基于事件的就绪通知方式。其事先经过epoll_ctrl()注册每个文件描述符,一旦某个文件描述符就绪时,内核会采用相似callback的回调机制,当进程调用epoll_wait()时获得通知

 

 

 

 

 

(4)Sendfile

 

 

 

大多数时候,咱们都向服务器请求静态文件,好比图片,样式表等,在处理这些请求时,磁盘文件的数据先通过内核缓冲区,而后到用户内存空间,不需通过任何处理,其又被送到网卡对应的内核缓冲区,接着再被送入网卡进行发送。

 

 

 

Linux提供sendfile()系统调用,能够讲磁盘文件的特定部分直接传送到表明客户端的socket描述符,加快了静态文件的请求速度,同时减小CPU和内存的开销。

 

 

 

适用场景: 对于请求较小的静态文件,sendfile发挥的做用不那么明显,因发送数据的环节在整个过程当中所占时间的比例相比于大文件请求时小不少。

 

 

 

 

 

 

 

(5)内存映射

 

Linux内核提供一种访问磁盘文件的特殊方式,它能够将内存中某块地址空间和咱们指定的磁盘文件相关联,从而对这块内存的访问转换为对磁盘文件的访问。这种技术称为内存映射。

 

 

 

 

 

多数状况下,内存映射能够提升磁盘I/O的性能,无须使用read()或write()等系统调用来访问文件,而是经过mmap()系统调用来创建内存和磁盘文件的关联,而后像访问内存同样自由访问文件。

 

 

 

缺点:在处理较大文件时,内存映射会致使较大的内存开销,得不偿失。

 

 

 

 

 

 

 

(6)直接I/O

 

 

 

在linux 2.6中,内存映射和直接访问文件没有本质差别,由于数据须要通过2次复制,即在磁盘与内核缓冲区之间以及在内核缓冲区与用户态内存空间。

 

 

 

引入内核缓冲区的目的在于提升磁盘文件的访问性能,然而对于一些复杂的应用,好比数据库服务器,它们为了进一步提升性能,但愿绕过内核缓冲区,由本身在用户态空间实现并管理I/O缓冲区,好比数据库可根据更加合理的策略来提升查询缓存命中率。另外一方面,绕过内核缓冲区也能够减小系统内存的开销,因内核缓冲区自己就在使用系统内存。

 

 

 

Linux在open()系统调用中增长参数选项O_DIRECT,便可绕过内核缓冲区直接访问文件,实现直接I/O。

 

 

 

在Mysql中,对于Innodb存储引擎,自身进行数据和索引的缓存管理,可在my.cnf配置中分配raw分区跳过内核缓冲区,实现直接I./O

 

 

 

 

 

六,改进服务器并发策略

 

 

 

服务器并发策略的目的,是让I/O操做和CPU计算尽可能重叠进行,一方面让CPU在I/O等待时不要空闲,另外一方面让CPU在I/O调度上尽可能花最少的时间。

 

 

 

 

 

 

 

(1)一个进程处理一个链接,非阻塞I/O,使用长链接

 

 

 

Apache使用这个模型,其进程的开销限制了它的并发链接数,但从稳定性和兼容性的角度,则其相对安全,任何一个子进程的崩溃不会影响Apache自己,Apache父进程能够建立新的子进程;另外一方面,Apache通过长期的考验和广发的使用,功能模块很是丰富。因此对于一些并发数要求不高(如150之内),还对其它功能有依赖,那么可考虑Apache这个模型。

 

 

 

 

 

(2)一个进程处理多个链接,异步I/O, 使用长链接

 

 

 

一个进程处理多个链接,潜在条件就是多路I/O就绪通知的应用。

 

 

 

服务器一般维护者大量的空闲链接,有些可能因为使用长链接而在等待超时,有些多是网络传输的延时等等,这时epoll只会关注活跃链接,而不在死链接上浪费时间,可是select和poll会扫描全部文件描述符,这个是个很是昂贵的开销。一个典型的应用就是图片服务器,它们但愿为用户提供网页中大量图片的快速下载,采用长链接,可是这些大量链接在等待超时关闭前,处于空闲状态,这种状况下,epoll依然能很好工做。

 

 

POSIX的标准库(aio.h)中定义了AIO的一系列接口,它几乎屏蔽了一切网络通讯的细节,对使用者而言很是简单。AIO没有提供非阻塞的open()方法,进程仍使用open()系统调用来打开文件,而后填充一些I/O请求的数据结构(struct aiocb),接下来调用aid_read()或aid_write()来发起异步I/O操做,一旦请求进入操做队列后,函数便返回,进程能够在此调用aid_error()来检查正在运行的I/O操做的状态

 

 

 

 

aiocb中相关的域

 

 

 

AIO接口API 

 

 

 

 

关于进程的数量,这个不是越多越好的。大量的进程能够维持更多的活跃链接数,但每一个链接的下载速度要远远小于前者(因上下文切换的CPU时间减小,有更多的时间用于发起sendfile()系统调用),则怎么决定worker的进程数取决于应用,例如是但愿为更多的用户同时提供慢速下载服务,仍是但愿为有限的用户提供快速的下载服务。

 

 

 

 

 

对于动态内容,如PHP脚本,worker进程一般只是负责转发请求给独立的fastcgi进程,或者做为反向代理服务器将请求转发给后端服务器,worker进程不太依赖太多的本地资源,能够适当提升并发链接数,但太多的worker进程又会带来更多的上下文切换开销和内存开销,从而总体上全部链接的相应时间变长。

 

 

 

而读取磁盘文件能够考虑使用异步I/O,在某些场景比性能sendfile()更出色。

 

 

七,改进硬件环境

 

还有一点要说起的是硬件环境,服务器的硬件配置对站点代理的性能提高确定是有的,但这里不做详细讨论。

相关文章
相关标签/搜索