精华内容
下载资源
问答
  • 服务器并发处理能力

    千次阅读 2015-03-27 09:54:00
    3、服务器并发处理能力 3.1 吞吐率 3.2 CPU并发计算 3.3 系统调用 3.4 内存分配 3.5 持久连接 3.6 I/O模型 3.6 服务并发策略名词定义:3.1 吞吐率(Throughput) :单位时间内服务器处理的请求数来描述其并发...


    3、服务器并发处理能力

       3.1 吞吐率

       3.2 CPU并发计算

       3.3 系统调用

       3.4 内存分配

       3.5 持久连接

       3.6 I/O模型

       3.6 服务并发策略

    名词定义:

    3.1 吞吐率(Throughput)

           :单位时间内服务器处理的请求数来描述其并发的处理能力。

    3.1.1 吞吐率和压力测试

           :通过模拟足够数目的并发用户数,分别持续发送一定的HTTP请求,并统计测试持续的总时间,计算出基于这种压力下的吞吐率,即为一个平均值。

    3.1.2 压力测试的前提条件

       :一些潜在的前提,那就是压力的描述和请求性质的描述:

           . 并发用户数

       :指在某一时刻同时向服务器发送请求的用户总数(HttpWatch)

           . 总请求数

           . 请求资源描述

           . 请求等待时间(用户等待时间)

           . 用户平均请求的等待时间。

           . 服务器平均请求处理的时间。

           . 硬件环境

    3.2 CPU并发计算

          :服务器之所以可以同时处理多个请求,在于操作系统通过多执行流体系设计使得多个任务可以轮流使用系统资源,这些资源包括CPU、内存以及I/O等。

    3.2.1 进程

           :多执行流一般实现是进程,多进程的好处并不仅仅在于CPU时间的轮流使用,还在于对CPU计算和I/O操作进行了很好的重叠利用,这里的I/O主要是指磁盘I/O和网络I/O,进程大多时间都消耗在I/O操作上,现代计算机的DMA技术可以让CPU不参与I/O操作的全进程。

    3.2.2 轻量级进程

           :它是由一个新的系统调用clone()来创建,并由内核直接管理,像普通的进程一样独立存在,各自拥有进程描述符,但是这些进程已经允许共享一些资源,比如地址空间,打开的文件等。轻量级进程减少了内存的开销,并为多进程应用程序的数据共享提供了直接支持,但是其上下文切换的开销还是在所难免的。

     3.2.3 线程

           :Posix 1003.1c 为linux定义了线程的接口"pthread",有些不是由内核来直接支持,多线程只是一个普通的进程,它是由用户态通过一些库函数模拟实现的多执行流,所以多线程的管理完全在用户态完成,这种实现方式下线程切换的开销相比于进程和轻量级进程都要少些,但是它在多处理器的服务器(SMP)中表现较差,因为只有内核的进程调试器才有权利分配多个CPU时间。

           :LinuxThreads,它可以说是内核级线(Kernel-Lerel-Threads),因为它通过clone()来创建进程,它的实现原理是将线程和轻量级进程进行一对一关联,每个线程实际上就是一个轻量级进程,这样使得线程完全由内核的进程调试器来管理,所以它对于SMP的支持较好,但是线程切换的开销相比于用户态线程要多一些。

    3.2.4 进程高度器

           :单CPU同时刻只有一个进程处于运行状态,而其它进程有的处于挂起状态并等待就绪,有的已经就绪并等待CPU时间片,还有的处于其它状态。内核中的进程高度器(Scheduler)维护着各种状态的进程队列,在Linux中,进程调度器维护着一个包括所有可运行进程的队列,称为“运行队列”(Run Quere),以及一个包括所有休眠进程和僵尸进程的列表。进程调试器的一项重要工作就是决定下一个运行的进程,如果运行队列中有不止一进程就按照优先级。调试器也动态的调整它们的优化级。具体可听听(视频: 嵌入式Linux性能监控和调优--华清远见嵌入式培训视http://v.youku.com/v_show/id_XMTAyODIzNTM2.html)

    3.2.5  系统负载

           :在进程调试器维护的运行队列中,任何时刻至少存在一个进程,那就是正在运行的进程,正当运行队列中有不止一个进程的时候,就说明此时的CPU比较抢手,其它进程还在等着,进程调试器应该尽快让正在运行的进程释放CPU。通过 cat /proc/loadavg

    1
    2
    [root@web102 ~]# cat /proc/loadavg
    0.00 0.00 0.00 1/102 23534

    0.00 0.00 0.00   最近1分钟、5分钟、15分钟分别计算得出的系统负载

    1/102            运行队列中的进程个数/此时的进程总数

    23534            代表到此为止,最后创建的一个进程ID


    简单密集型CPU计算程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php
     $max = 10000000000;
     $sum = 0;
     for ($i = 0; $i $max; ++$i)
      {
        $sum += $i;
      }
     echo $sum;
    ?>

    3.2.6 进程切换

           :为了让所有的进程可以轮流使用系统资源,进程调试器在必要的时候挂起正在运行的进程,同时恢复以前挂起的进程,这种行为称为进程切换。 Nmon:ContextSwitch 上下文切换平均每秒次数。(单进程的上下文切换速度要比多进程的上下文切换速度快。)

    3.27 IOWait

           : 它是指CPU空闲并且等待I/O操作完成的时间比例。是一个比例而不是绝对时间,并不能衡量什么东西。

    3.28 锁竞争

           :大量并发请求时会以一些资源抢占竞争,需要一种机制来维护秩序,一般采用锁机制。(进程与线程的一个简单解释 http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html)、

    3.3 系统调用

           :进程有用户态和内核态两种运行模式,两种模式之间切换,也需要一定的开销,当进程需要对硬件外设进来操作时(读取磁盘文件、发送网络数据等),就必须切换到内核态,当内核态的任务完成后,进程又切换回用户态。内核提供了一系列的系统调用,同时C函数(libc)将系统调用封装在编程接口中,提供给用户态的进程,用户态的进程可以直接进行系统调用,也可以使用封装了系统调用的C APi,比如write().

    3.4 内存分配

       :内存机制以及内存与交换的交换数据,以及内存的分配和释放。根据算法(LRU)来进行分配。(浅谈Linux的内存管理机制http://ixdba.blog.51cto.com/2895551/541355)

           . Nginx内存分配

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

           . Apache内存分配

                    Apache使用多进程模型,使用基于内存池策略的内存管理方案。并将它抽象出来后移入APR库中作为通用内存管理模块,这种方案使用apache在开始运行便一次性申请大片的内存作内存池,这样在随后需要的时候只要在内在池中直接获取,而不需要再次分配,我们知道频繁的内存分配和翻译会引发一定时间的内存整理,这本身便影响了性能。内存池对于性能的弥补微不足道。

    3.5 持久连接(Keep-alive)

       : 也称长连接,它本身是TCP通信的一种普通方式,即在一次TCP连接中持续发送多份数据而不断开连接,与它相反的称之为短连接,也就是建立连接后发送一份数据便断开,然后再次建立连接发送下一份数据,周而复始。(网站相关技术探究keepalive_timeout:http://cwtea.blog.51cto.com/4500217/1170957)

    3.6 I/O模型

           :I/O操作主要是网络数据的接收和发送,以及磁盘文件的访问,我们将其归纳为多种模型,称为I/o模式,它们的本质区别在于CPU的参与方式。划为很多类型,例如:内存I/O、网络I/O、磁盘I/O。

               . 使用RAID磁盘陈列可以通过并行磁盘访问加快磁盘I/O速度。

    3.6.1 PIO与DMA

       :PIO,很早以前,磁盘和内存之间的数据传输是需要CPU控制的,也就是说,如果我们读取磁盘文件到内存中,数据要经过CPU存储转发,这种方式称为PIO,需要占用大量CPU时间来读取文件,造成文件访问时系统几乎停止响应

       :DMA(直接内存访问,Direct Memory/Access)取代了PIO,它可以不经过CPU而直接进行磁盘和内存的数据交换。在DMA模式下,CPu只需要向DMA控制器下达指令,让DMA控制器来处理数据的传送即可,DMA控制器通过系统总线来传略数据,传送完毕再通知CPU。

    3.6.2 同步阻塞I/O

       :是指当进程调用某些涉及I/O操作的系统调用或库函数时,比如accept(),send(),recv()等,进程便暂停下来,等待I/O操作完成后再继续运行,在等待时间上分为两步,一个是等待数据的就绪另一个是等待数据的复制。

           . I/O等待

           :用户请求的数据在网络上传输需要时间,而上下等待的时间称为I/O等待时间。

               . 阻塞

               :指当前发起I/O操作的进程被阻塞,并不是CPU被阻塞。

                   . 同步

                   :对于磁盘文件的访问,也有一个所谓的“同步”选项,即使用O_SYNC标志打开文件,在规范情况下,对磁盘文件调用write()则不同,它会在数据被复制到内核缓冲区后立即返回。如果使用O_SYNC票志打开文件,则对写文件操作产生影响,它使用write()必须等待数据真正写入磁盘后才返回。

    1
    2
    3
    [root@web102 ~]# strace -p 23361
    Process 23361 attached - interrupt to quit
    restart_syscall(<... resuming interrupted call ...>

    3.6.3 同步非阻塞I/O

       :同步非阻塞I/O的调用不会等待数据的就绪,如果数据不可读或者不可写,它会立即告诉进程。比如我们使用非阻塞recv()接收网络数据的时候,如果网卡缓冲区中没有可接收的数据,函数就会及时返回,告诉进程没有数据可读了,相比于阻塞I/O,这种非阻塞I/O结合反复轮询来尝试数据是否就绪,防止进程被阻塞,最大的好处便在于可以在一个进程里同时处理多个I/O操作。(这花费了太多的CPU时间,使得进程处理忙碌的等待状态。)

    3.6.4 多中I/O不绪通知

       :多路I/O就绪通知的出现,提供了对大量文件描述符就绪检查的高性能方案,它允许进程通过一种方法来同时监视所有的文件描述符,并可以快速获得所有就绪的文件描述符,然后只针对这些文件描述符进行数据访问。

       . select

       : 它通过一个select()系统调用来监视包含多个文件描述符的数组,当select()返回时,该数组中就绪的文件描述符便会被内核修改标识位,使得进程可以获得这些文件描述符从而进行后续的写操作。

       优点:

           1、良好的跨平台支持

       缺点:

           1、单进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核来提升这一限制

           2、select()所维护的存储大量文件描述符的数据结构,承受着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。

               . poll

               : 与select()没有本质上的区别,但是poll没有最大文件描述符限制。

               优点:

                   1、select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行I/O操作,那么下次调用select()或poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的信息,这种方式称为水平触发(Level Triggered)。

               . SIGIO

               :它通过实时信号(Real Time Signal)来实现select/poll的通知方法,它们的不同之处在于select/poll告诉我们哪些文件描述符是就绪的,一直到我们读写它之前,每次select/poll都会告诉我们,而SIGIO则是告诉我们哪些文件描述符刚刚变为就绪状态,它只说一次,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发(Edge Triggered)。SIGIO几乎是linux 2.4下性能最好的多路I/O就绪通知方法。

               . /dev/poll

               : Sun 的solaris提供了新的实现方法,它使用了虚拟的/dev/poll设备,你可以将要监视的文件描述符数组写入这个设备,然后通过ioctl()来等待事件通知,当ioctl()返回就绪的文件描述符后,你可以从/dev/poll中读取所有就绪的文件描述符数组,这点于SIGIO节省了扫描所有文件描述符的开销。

               . /dev/epoll

               : linux2.4以/dev/epoll的设备补丁的形式出现,它提供了类似/dev/poll的功能,而且增加了内在映射(mmap)技术,在一定程序上提高了性能。

               . epoll

               : linux2.6内核直接支持的实现方法,它几乎具备了之前所说的一切优点,被公认为linux2.6下性能最好的多路I/O就绪通知方法。可以同时支持水平触发和边缘触发,理论上边缘角发的性能要更高一些,默认情况下epoll采用水平触发,如果要使用边缘触发,则需要在事件注册时增加EPOLLET选项。另外,epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的并不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。epoll事先通过epoll_ctl()来注册每一个文件描述符,一但某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait(0时便得到通知。

                       lighttpd:

                           src/fdevent_linux_sysepoll.c 注释掉了EPOLLET,并没有使用边缘触发方式。

                       nginx:

                           src/event/modules/ngx_epoll_module.c 使用了边缘触发。

               . kqueue

               : 它像epoll一样可以设置水平触发或边缘触发,同时kqueue还可以用来监视磁盘文件和目录,但是它的APi在很多平台都不支持,而且文档相当少。

    3.6.5 内存映射

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

                   . 共享型

                       : 任何对内存的写操作都同步到磁盘文件,而且所有映射同一个文件的进程都共享任意一个进程对映射内存的修改。

                   . 私有型

                       :映射文件只能是只读文件,所以不可以将对内在的写同步到文件,而且多个进程不共享修改。

    3.6.6 直接I/O

               :不经过内核缓冲区直接写文件,linux提供了对这种需要的支持,即在open()系统调用中增加参数选项O-DIRECT,用它打开文件便可以绕过内核缓冲区的直接访问,这样便有效避免了CPU和内存的多余时间开销。

    3.6.7 sendfile

               : 在linux2.4内核中尝试着引入了一个称为khttpd的内核级web服务程序,它只处理静态文件的请求,引入它的目的便在于内核希望请求的处理尽量在内核完成,减少内核态的切换以及用户态数据的复制的开销。同时linux通过系统调用把这种机制提供给了开发者,那就是sendfile()系统调用。它可以将磁盘文件的特定部分直接传送到客户端的socket描述符,加快了静态文件的请求速度,同时也减少了CPU和内存的开销。

      3.6.8 异步I/O

               :阻塞和非阻塞是指当进程访问的数据如果尚未就绪,进程是否等等,简单说这相当于函数内部的实现区别,即未就绪时是直接返回还是等待就绪,而同步和异步是指访问数据的机制,同步一般指主动请求并等待I/O操作完毕的方式,当数据就绪后在读写的时候必须阻塞,异步则指主动请求数据后便可以继续处理其它任务,随后等待I/O操作完毕的通知,这可以使进程在数据读写时也不发生阻塞。

    3.7 服务并发策略

           :从本质上讲,所有到达服务器的请求都封装在IP包中,位于网卡的接收缓冲区,这时侯,WEB服务器不断地读取这些请求,然后进行处理,并将结果写到发送缓冲区,这其中包含一系列的I/O操作和CPU计算,而设计一个并发策略的目的,就是让I/O操作和CPU计算尽量重叠进行,一方面要让CPU在I/O等待时不要空闲,另一方面让CPU在I/O设度上尽量花费最少的时间。

    3.7.1 一个进程处理一个连接,非阻塞I/O

    3.7.2 一个线程处理一个连接,非阻塞I/O

    apache prefork模式

                       主进程预先创建一定数量的子进程,每个请求由一个子进程来处理,但是每个子进程可以处理多个请求。父进程往往只负责管理子进程,根据站点负载来调整子进程的数量,相当于动态维护一个进程池。

               3.7.3 一个进程处理多个连接,非阻塞I/O

    apache worker模式

                       这种方式允许在一个进程中通过多个纯种来处理多个连接,其中每个线程处理一个连接。它的目的主要在于减少prefork模式中太多的进程开销,使apache支持更多的并发连接。

               3.7.4 一个线程处理多个连接,非阻塞I/O

    nginx lighttpd worker模式

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

               3.7.5 一个线程处理多个连接,异步I/O


    展开全文
  • 说明 以下内容为入门级介绍,意在对老技术作较全的总结而不是较...一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强 有什么方法衡量服务器并发处理能力 1. 吞吐率 吞...

    转发自:https://www.cnblogs.com/zengjin93/p/5569556.html

    说明

    以下内容为入门级介绍,意在对老技术作较全的总结而不是较深的研究。主要参考《构建高性能Web站点》一书。

     

    什么是服务器并发处理能力

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

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

    1. 吞吐率

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

    从服务器角度,实际并发用户数的可以理解为服务器当前维护的代表不同用户的文件描述符总数,也就是并发连接数。服务器一般会限制同时服务的最多用户数,比如apache的MaxClents参数。

    这里再深入一下,对于服务器来说,服务器希望支持高吞吐率,对于用户来说,用户只希望等待最少的时间,显然,双方不能满足,所以双方利益的平衡点,就是我们希望的最大并发用户数。

    2. 压力测试

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

    压力测试前提考虑的条件

    • 并发用户数: 指在某一时刻同时向服务器发送请求的用户总数(HttpWatch)

    • 总请求数

    • 请求资源描述

    • 请求等待时间(用户等待时间)

    • 用户平均请求的等待时间

    • 服务器平均请求处理的时间

    • 硬件环境

    压力测试中关心的时间又细分以下2种:

    1. 用户平均请求等待时间(这里暂不把数据在网络的传输时间,还有用户PC本地的计算时间计算入内)

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

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

    怎么提高服务器的并发处理能力

    1. 提高CPU并发计算能力

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

    多进程 & 多线程

    多执行流的一般实现便是进程,多进程的好处可以对CPU时间的轮流使用,对CPU计算和IO操作重叠利用。这里的IO主要是指磁盘IO和网络IO,相对CPU而言,它们慢的可怜。

    而实际上,大多数进程的时间主要消耗在I/O操作上。现代计算机的DMA技术可以让CPU不参与I/O操作的全过程,比如进程通过系统调用,使得CPU向网卡或者磁盘等I/O设备发出指令,然后进程被挂起,释放出CPU资源,等待I/O设备完成工作后通过中断来通知进程重新就绪。对于单任务而言,CPU大部分时间空闲,这时候多进程的作用尤为重要。

    多进程不仅能够提高CPU的并发度。其优越性还体现在独立的内存地址空间和生命周期所带来的稳定性和健壮性,其中一个进程崩溃不会影响到另一个进程。

    但是进程也有如下缺点:

    1. fork()系统调用开销很大: prefork

    2. 进程间调度和上下文切换成本: 减少进程数量

    3. 庞大的内存重复:共享内存

    4. IPC编程相对比较麻烦

    减少进程切换

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

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

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

    减少使用不必要的锁

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

    通过锁竞争的本质,我们要意识到尽量减少并发请求对于共享资源的竞争。比如在允许情况下关闭服务器访问日志,这可以大大减少在锁等待时的延迟时间。要最大程度减少无辜的等待时间。

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

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

    考虑进程优先级

    进程调度器会动态调整运行队列中进程的优先级,通过top观察进程的PR值

    考虑系统负载

    可在任何时刻查看/proc/loadavg, top中的load average也可看出

    考虑CPU使用率

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

    2. 考虑减少内存分配和释放

    服务器的工作过程中,需要大量的内存,使得内存的分配和释放工作尤为重要。

    可以通过改善数据结构和算法复制度来适当减少中间临时变量的内存分配及数据复制时间,而服务器本身也使用了各自的策略来提高效率。

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

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

    另外,还可以考虑共享内存。

    共享内存指在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存,也可以由不同进程共享,是非常快的进程通信方式。

    但是使用共享内存也有不好的地方,就是对于多机器时数据不好统一。

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

    3. 考虑使用持久连接

    持久连接也为长连接,它本身是TCP通信的一种普通方式,即在一次TCP连接中持续发送多分数据而不断开连接,与它相反的方式称为短连接,也就是建立连接后发送一份数据就断开,然后再次建立连接发送下一份数据, 周而复始。是否采用持久连接,完全取决于应用特点。从性能角度看,建立TCP连接的操作本身是一项不小的开销,在允许的情况下,连接次数越少,越有利于性能的提升; 尤其对于密集型的图片或网页等小数据请求处理有明显的加速所用。

    HTTP长连接需要浏览器和web服务器的共同协作,目前浏览器普遍支持长连接,表现在其发出的HTTP请求数据头中包含关于长连接的声明,如下: Connection: Keep-Alive

    主流的web服务器都支持长连接,比如apache中,可以用KeepAlive off关闭长连接。

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

    4. 改进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操作达到很好的重叠。

    3. I/O多路复用

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

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

    epoll主要有2大改进:

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

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

    关于IO模型,可以参考笔者前面写的相关文章Java NIO.2; 关于epoll,可以参考笔者前面写的文章select、poll和epoll简介

    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调度上尽量花最少的时间。

    一个进程处理一个连接,非阻塞I/O

    这样会存在多个并发请求同时到达时,服务器必然要准备多个进程来处理请求。其进程的开销限制了它的并发连接数。但从稳定性和兼容性的角度,则其相对安全,任何一个子进程的崩溃不会影响服务器本身,父进程可以创建新的子进程;这种策略典型的例子就是Apache的fork和prefork模式。对于并发数不高(如150以内)的站点同时依赖Apache其它功能时的应用选择Apache还是可以的。

    一个线程处理一个连接,非阻塞IO

    这种方式允许在一个进程中通过多个线程来处理多个连接,一个线程处理一个连接。Apache的worker模式就是这种典型例子,使其可支持更多的并发连接。不过这种模式的总体性能还不如prefork,所以一般不选用worker模式。

    一个进程处理多个连接,异步I/O

    一个线程同时处理多个连接,潜在的前提条件就是使用IO多路复用就绪通知。

    这种情况下,将处理多个连接的进程叫做worker进程或服务进程。worker的数量可以配置,如Nginx中的worker_processes 4。

    一个线程处理多个连接,异步IO

    即使有高性能的IO多路复用就绪通知,但磁盘IO的等待还是无法避免的。更加高效的方法是对磁盘文件使用异步IO,目前很少有Web服务器真正意义上支持这种异步IO。

    6. 改进硬件环境

    还有一点要提及的是硬件环境,服务器的硬件配置对应用程序的性能提升往往是最直接,也是最简单的方式,这就是所谓的scale up。这里不做论述。

    展开全文
  • 一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强 有什么方法衡量服务器并发处理能力 1. 吞吐率 吞吐率,单位时间里服务器处理的最大请求数,单位req/s 从服务器角度,实际...

    作者:潇洒一剑 www.cnblogs.com/zengjin93/p/5569556.html

    以下内容为入门级介绍,意在对老技术作较全的总结而不是较深的研究。主要参考《构建高性能Web站点》一书。

    什么是服务器并发处理能力

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

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

    1. 吞吐率

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

    从服务器角度,实际并发用户数的可以理解为服务器当前维护的代表不同用户的文件描述符总数,也就是并发连接数。

    服务器一般会限制同时服务的最多用户数,比如apache的MaxClents参数。

    这里再深入一下,对于服务器来说,服务器希望支持高吞吐率,对于用户来说,用户只希望等待最少的时间,显然,双方不能满足,所以双方利益的平衡点,就是我们希望的最大并发用户数。

    2. 压力测试

    有一个原理一定要先搞清楚,假如100个用户同时向服务器分别进行10个请求,与1个用户向服务器连续进行1000次请求,对服务器的压力是一样吗?

    实际上是不一样的,因对每一个用户,连续发送请求实际上是指发送一个请求并接收到响应数据后再发送下一个请求。

    这样对于1个用户向服务器连续进行1000次请求, 任何时刻服务器的网卡接收缓冲区中只有1个请求,而对于100个用户同时向服务器分别进行10个请求,服务器的网卡接收缓冲区最多有100个等待处理的请求,显然这时的服务器压力更大。

    压力测试前提考虑的条件

    • 并发用户数: 指在某一时刻同时向服务器发送请求的用户总数(HttpWatch)

    • 总请求数

    • 请求资源描述

    • 请求等待时间(用户等待时间)

    • 用户平均请求的等待时间

    • 服务器平均请求处理的时间

    • 硬件环境

    压力测试中关心的时间又细分以下2种:

    1. 用户平均请求等待时间(这里暂不把数据在网络的传输时间,还有用户PC本地的计算时间计算入内)

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

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

    一般来说,用户平均请求等待时间 = 服务器平均请求处理时间 * 并发用户数


    怎么提高服务器的并发处理能力

    1. 提高CPU并发计算能力

    服务器之所以可以同时处理多个请求,在于操作系统通过多执行流体系设计使得多个任务可以轮流使用系统资源。

    这些资源包括CPU,内存以及I/O. 这里的I/O主要指磁盘I/O, 和网络I/O。

    多进程 & 多线程

    多执行流的一般实现便是进程,多进程的好处可以对CPU时间的轮流使用,对CPU计算和IO操作重叠利用。这里的IO主要是指磁盘IO和网络IO,相对CPU而言,它们慢的可怜。

    而实际上,大多数进程的时间主要消耗在I/O操作上。

    现代计算机的DMA技术可以让CPU不参与I/O操作的全过程,比如进程通过系统调用,使得CPU向网卡或者磁盘等I/O设备发出指令,然后进程被挂起,释放出CPU资源,等待I/O设备完成工作后通过中断来通知进程重新就绪。

    对于单任务而言,CPU大部分时间空闲,这时候多进程的作用尤为重要。CPU 是怎么认识代码的?推荐大家看下。

    多进程不仅能够提高CPU的并发度。其优越性还体现在独立的内存地址空间和生命周期所带来的稳定性和健壮性,其中一个进程崩溃不会影响到另一个进程。

    但是进程也有如下缺点

    1. fork()系统调用开销很大: prefork

    2. 进程间调度和上下文切换成本: 减少进程数量

    3. 庞大的内存重复:共享内存

    4. IPC编程相对比较麻烦

    ####

    减少进程切换

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

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

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

    减少使用不必要的锁

    服务器处理大量并发请求时,多个请求处理任务时存在一些资源抢占竞争,这时一般采用“锁”机制来控制资源的占用。到底什么是重入锁,推荐大家看下。

    当一个任务占用资源时,我们锁住资源,这时其它任务都在等待锁的释放,这个现象称为锁竞争

    通过锁竞争的本质,我们要意识到尽量减少并发请求对于共享资源的竞争。

    比如在允许情况下关闭服务器访问日志,这可以大大减少在锁等待时的延迟时间。要最大程度减少无辜的等待时间。

    这里说下无锁编程,就是由内核完成这个锁机制,主要是使用原子操作替代锁来实现对共享资源的访问保护。

    使用原子操作时,在进行实际的写操作时,使用了lock指令,这样就可以阻止其他任务写这块内存,避免出现数据竞争现象。原子操作速度比锁快,一般要快一倍以上

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

    关注微信公众号:Java技术栈,在后台回复:多线程,可以获取我整理的 N 篇最新 Java多线程教程,都是干货。

    考虑进程优先级

    进程调度器会动态调整运行队列中进程的优先级,通过top观察进程的PR值

    考虑系统负载

    可在任何时刻查看/proc/loadavg, top中的load average也可看出

    考虑CPU使用率

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

    2. 考虑减少内存分配和释放

    服务器的工作过程中,需要大量的内存,使得内存的分配和释放工作尤为重要。

    可以通过改善数据结构和算法复制度来适当减少中间临时变量的内存分配及数据复制时间,而服务器本身也使用了各自的策略来提高效率。

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

    再如Nginx使用多线程来处理请求,使得多个线程之间可以共享内存资源,从而令它的内存总体使用量大大减少。

    另外,Nginx分阶段的内存分配策略,按需分配,及时释放,使得内存使用量保持在很小的数量范围。

    另外,还可以考虑共享内存

    共享内存指在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存,也可以由不同进程共享,是非常快的进程通信方式。

    但是使用共享内存也有不好的地方,就是对于多机器时数据不好统一。

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

    3. 考虑使用持久连接

    持久连接也为长连接,它本身是TCP通信的一种普通方式,即在一次TCP连接中持续发送多分数据而不断开连接。

    与它相反的方式称为短连接,也就是建立连接后发送一份数据就断开,然后再次建立连接发送下一份数据, 周而复始。

    是否采用持久连接,完全取决于应用特点。

    从性能角度看,建立TCP连接的操作本身是一项不小的开销,在允许的情况下,连接次数越少,越有利于性能的提升; 尤其对于密集型的图片或网页等小数据请求处理有明显的加速所用。

    HTTP长连接需要浏览器和web服务器的共同协作,目前浏览器普遍支持长连接,表现在其发出的HTTP请求数据头中包含关于长连接的声明,如下:Connection: Keep-Alive

    主流的web服务器都支持长连接,比如apache中,可以用KeepAlive off关闭长连接。

    对于长连接的有效使用,还有关键一点在于长连接超时时间的设置,即长连接在什么时候关闭吗?

    Apache的默认设置为5s, 若这个时间设置过长,则可能导致资源无效占有,维持大量空闲进程,影响服务器性能。

    4. 改进I/O 模型

    I/O操作根据设备的不同分为很多类型,比如内存I/O, 网络I/O, 磁盘I/O。详解 Java 中 4 种 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的参与方式。

    DMA技术

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

    异步I/O

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

    异步I/O是非阻塞的,当函数返回时,真正的I/O传输已经完成,这让CPU处理和I/O操作达到很好的重叠。

    I/O多路复用

    epoll服务器同时处理大量的文件描述符是必不可少的,若采用同步非阻塞I/O模型,若同时接收TCP连接的数据,就必须轮流对每个socket调用接收数据的方法,不管这些socket有没有可接收的数据,都要询问一次。

    假如大部分socket并没有数据可以接收,那么进程便会浪费很多CPU时间用于检查这些socket有没有可以接收的数据。

    多路I/O就绪通知的出现,提供了对大量文件描述符就绪检查的高性能方案,它允许进程通过一种方法同时监视所有文件描述符,并可以快速获得所有就绪的文件描述符,然后只针对这些文件描述符进行数据访问。

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

    epoll主要有2大改进:

    1. epoll只告知就绪的文件描述符,而且当调用epoll_wait()获得文件描述符时,返回并不是实际的描述符,而是一个代表就绪描述符数量的值,然后只需去epoll指定的一个数组中依次取得相应数量的文件描述符即可。

      这里使用了内存映射(mmap)技术,这样彻底省掉了这些文件描述符在系统调用时复制的开销。

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

    关于IO模型,可以参考笔者前面写的相关文章Java NIO.2;关于epoll,可以参考笔者前面写的文章select、poll和epoll简介。

    Sendfile

    大多数时候,我们都向服务器请求静态文件,比如图片,样式表等。

    在处理这些请求时,磁盘文件的数据先经过内核缓冲区,然后到用户内存空间,不需经过任何处理,其又被送到网卡对应的内核缓冲区,接着再被送入网卡进行发送。

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

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

    内存映射

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

    多数情况下,内存映射可以提高磁盘I/O的性能,无须使用read()或write()等系统调用来访问文件,而是通过mmap()系统调用来建立内存和磁盘文件的关联,然后像访问内存一样自由访问文件。

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

    直接I/O

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

    引入内核缓冲区的目的在于提高磁盘文件的访问性能,然而对于一些复杂的应用,比如数据库服务器,它们为了进一步提高性能,希望绕过内核缓冲区,由自己在用户态空间实现并管理I/O缓冲区,比如数据库可根据更加合理的策略来提高查询缓存命中率。

    另一方面,绕过内核缓冲区也可以减少系统内存的开销,因内核缓冲区本身就在使用系统内存。

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

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

    5. 改进服务器并发策略

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

    一个进程处理一个连接,非阻塞I/O

    这样会存在多个并发请求同时到达时,服务器必然要准备多个进程来处理请求。其进程的开销限制了它的并发连接数。

    但从稳定性和兼容性的角度,则其相对安全,任何一个子进程的崩溃不会影响服务器本身,父进程可以创建新的子进程;这种策略典型的例子就是Apache的fork和prefork模式。

    对于并发数不高(如150以内)的站点同时依赖Apache其它功能时的应用选择Apache还是可以的。

    一个线程处理一个连接,非阻塞IO

    这种方式允许在一个进程中通过多个线程来处理多个连接,一个线程处理一个连接。Apache的worker模式就是这种典型例子,使其可支持更多的并发连接。不过这种模式的总体性能还不如prefork,所以一般不选用worker模式。推荐阅读:14个Java并发容器

    一个进程处理多个连接,异步I/O

    一个线程同时处理多个连接,潜在的前提条件就是使用IO多路复用就绪通知。

    这种情况下,将处理多个连接的进程叫做worker进程或服务进程。worker的数量可以配置,如Nginx中的worker_processes 4。

    一个线程处理多个连接,异步IO

    即使有高性能的IO多路复用就绪通知,但磁盘IO的等待还是无法避免的。更加高效的方法是对磁盘文件使用异步IO,目前很少有Web服务器真正意义上支持这种异步IO。

    6. 改进硬件环境

    还有一点要提及的是硬件环境,服务器的硬件配置对应用程序的性能提升往往是最直接,也是最简单的方式,这就是所谓的scale up。这里不做论述。

    关注公众号Java技术栈回复"面试"获取我整理的2020最全面试题及答案。

    推荐去我的博客阅读更多:

    1.Java JVM、集合、多线程、新特性系列教程

    2.Spring MVC、Spring Boot、Spring Cloud 系列教程

    3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程

    4.Java、后端、架构、阿里巴巴等大厂最新面试题

    觉得不错,别忘了点赞+转发哦!

    最后,关注下面的栈长的微信公众号:Java技术栈,回复:福利,可以免费获取一份我整理的 2020 最新 Java 面试题,真的非常全(含答案),无任何套路。

    展开全文
  • (一)什么是服务器并发处理能力 (二)有什么方法衡量服务器并发处理能力 1.吞吐率 2.压力测试 (三)怎么提高服务器的并发处理能力 1,提高CPU并发计算能力 (1)多进程&amp;多线程 (2)减少进程切换,...

    目录

     

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

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

    1.吞吐率

    2.压力测试

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

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

    2,减少系统调用

    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、栈(存放着局部变量和函数参数等数据),向下生长   (可读可写可执行)

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

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

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

     

     

     

    (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计算尽量重叠进行,一方面让CPUI/O等待时不要空闲,另一方面让CPUI/O调度上尽量花最少的时间。

     

     

     

     

     

     

     

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

     

     

     

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

     

     

     

     

     

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

     

     

     

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

     

     

     

    服务器通常维护者大量的空闲连接,有些可能由于使用长连接而在等待超时,有些可能是网络传输的延时等等,这时epoll只会关注活跃连接,而不在死连接上浪费时间,但是selectpoll会扫描所有文件描述符,这个是个非常昂贵的开销。一个典型的应用就是图片服务器,它们希望为用户提供网页中大量图片的快速下载,采用长连接,但是这些大量连接在等待超时关闭前,处于空闲状态,这种情况下,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()更出色。

     

     

    七,改进硬件环境

     

    还有一点要提及的是硬件环境,服务器的硬件配置对站点代理的性能提升肯定是有的,但这里不作详细讨论。

    展开全文
  • 什么是服务器并发处理能力一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强有什么方法衡量服务器并发处理能力1. 吞吐率吞吐率,单位时间里服务器处理的最大请求数,单位r.....
  • 一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强 有什么方法衡量服务器并发处理能力 吞吐率 吞吐率,单位时间里服务器处理的最大请求数,单位req/s 从服务器角度,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,311
精华内容 2,924
关键字:

并发处理能力