面试官 | 如何提高服务器的并发能力?

作者 | 潇洒一剑

来源 | cnblogs.com/zengjin93/p/5569556.html

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

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

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

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的参与方式。

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模式。

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

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

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

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

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

6. 改进硬件环境

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

【END】

近期热文

 
  • 面试珍藏:最常见的200多道Java面试题

  • 被一个熟悉的面试题问懵了:String...

  • 面试官:如何实现幂等性校验?

  • 年终盘点 | 2019年Java面试题汇总篇(附答案)

关注下方二维码,订阅更多精彩内容

朕已阅 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/546410.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html网页的结构框架代码

推荐使用Notepad编辑器写HTML代码 Notepad官网下载地址 html学习网址&#xff1a; w3school在线教程 html网页的结构框架.html <!--文档声明&#xff0c;声明当前网页的版本--> <!DOCTYPE html> <!--html的根标签&#xff08;元素&#xff09;&#xff0c;网…

Spring 经典面试题汇总.pdf(2020版)

作者 | 静默虚空来源 | juejin.im/post/5cbda379f265da03ae74c2821、基础1.1. 不同版本的 Spring Framework 有哪些主要功能&#xff1f;Version FeatureSpring 2.5发布于 2007 年。这是第一个支持注解的版本。Spring 3.0发布于 2009 年。它完全利用了 Java…

面试官 | SpringBoot 中如何实现异步请求和异步调用?

作者 | 会炼钢的小白龙来源 | cnblogs.com/baixianlong/p/10661591.html一、SpringBoot中异步请求的使用1、异步请求与同步请求 特点&#xff1a;可以先释放容器分配给请求的线程与相关资源&#xff0c;减轻系统负担&#xff0c;释放了容器所分配线程的请求&#xff0c;其响应将…

HTML网页结构化框架、meta标签和语义化标签

1.HTML网页结构化框架代码示例 myhtml.html <!--文档声明&#xff0c;声明当前网页的版本--> <!DOCTYPE html> <!--html的根标签&#xff08;元素&#xff09;&#xff0c;网页中的所有内容都要写在根元素的里边--> <html lang"en"> <!…

Android之个性化ListView实现

2019独角兽企业重金招聘Python工程师标准>>> Android中提供的ListView部件&#xff0c;用来在android手机中展现一个列表&#xff0c;这个列表的表现形式&#xff0c;完全由你选择的Adapter有关系&#xff0c;android框架中已有的adapter&#xff0c;形式都比较单调…

面试官 | Class.forName 和 ClassLoader 有什么区别?

作者 | 纪莫来源 | dwz.date/eUc在 Java 中 Class.forName() 和 ClassLoader 都可以对类进行加载。ClassLoader 就是遵循双亲委派模型最终调用启动类加载器的类加载器&#xff0c;实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”&#xff0c;获取到二进制流…

微信公众号使用Chrome插件:Markdown Nice优化微信公众号排版教程

Markdown Nice 是一个为了解决微信公众号排版而生的 Markdown 编辑器&#xff0c;当前有在线编辑器和 Chrome 插件 2 种产品形态。 下面介绍Chrome 插件&#xff1a;Markdown Nice 一、下载和安装Markdown Nice 1.从 墨滴 网站下载mdnice谷歌浏览器插件&#xff0c;如下图&a…

面试官 | 如何优雅的设计Java 异常?

作者 | lrwinx来源 | lrwinx.github.io异常处理是程序开发中必不可少操作之一&#xff0c;但如何正确优雅的对异常进行处理确是一门学问。异常的类别正如我们所知道的&#xff0c;java中的异常的超类是java.lang.Throwable(后文省略为Throwable),它有两个比较重要的子类,java.l…

3.Python Django之GET请求和POST请求及响应处理

一、请求中的方法 方法描述GET请求指定的页面信息&#xff0c;并返回实体主体。HEAD类似于GET请求&#xff0c;只不过返回的响应中没有具体的内容&#xff0c;用于获取报头。POST向指定资源提交数据进行处理请求&#xff08;例如&#xff1a;提交表单或者上传文件&#xff09;…

面试官 | Java中的注解是如何工作的?

自Java5.0版本引入注解之后&#xff0c;它就成为了Java平台中非常重要的一部分。开发过程中&#xff0c;我们也时常在应用代码中会看到诸如Override&#xff0c;Deprecated这样的注解。这篇文章中&#xff0c;我将向大家讲述到底什么是注解&#xff1f;为什么要引入注解&#x…

优秀的代码原来是这样分层的

作者 | 咖啡拿铁来源 | t.cn/RdrmI7i1、背景说起应用分层&#xff0c;大部分人都会认为这个不是很简单嘛 就controller&#xff0c;service, mapper三层。看起来简单&#xff0c;很多人其实并没有把他们职责划分开&#xff0c;在很多代码中,controller做的逻辑比service还多,se…

面试官 | 什么是 Lambda?该如何使用?

作者 | Mingqi来源 | zhihu.com/question/20125256/answer/3241213081.什么是Lambda?我们知道&#xff0c;对于一个Java变量&#xff0c;我们可以赋给其一个“值”。如果你想把“一块代码”赋给一个Java变量&#xff0c;应该怎么做呢&#xff1f;比如&#xff0c;我想把右边那…

面试官 | 线程间是如何通信的?

作者 | wingjay来源 | wingjay.com正常情况下&#xff0c;每个线程独立完成自己的任务就结束了&#xff0c;但某些特殊情况下&#xff0c;我们需要多个线程来共同完成某项任务&#xff0c;这时就涉及到了线程间通信了。本文涉及到的知识点&#xff1a;thread.join()object.wait…

Linux debian解压和压缩.rar文件教程

一、简介 我们上传到Linux服务器上的文件日常是.zip或.tat.gz的文件&#xff0c;我们可以用相应的命令对文件进行解压。有时会遇到.rar文件。本次使用rar软件版本是5.6.0。官方网站为http://www.rarlab.com/ 二、下载和安装rar文件 1.创建文件夹 [rootdoudou ~]# mkdir /us…

windos手工扩展分区

为什么80%的码农都做不了架构师&#xff1f;>>> windows 2003系统下手工扩展分区&#xff08;2008系统直接在磁盘管理里扩展卷即可&#xff09; 1、虚拟机关机&#xff0c;将目标磁盘扩展到所需大小 2、开机&#xff0c;进入命令行模式 3、diskpart 进入diskpart工…

面试突击 | 彻底搞定 JVM 这几道高频面试题

前言 Java 相比 C/C 最显著的特点便是引入了自动垃圾回收 (下文统一用 GC 指代自动垃圾回收)&#xff0c;它解决了 C/C 最令人头疼的内存管理问题&#xff0c;让程序员专注于程序本身&#xff0c;不用关心内存回收这些恼人的问题&#xff0c;这也是 Java 能大行其道的重要原因之…

阿里面试官给你的一些忠告,这样做肯定错不了!附视频

作者 | 梦游的龙猫来源 | http://dwz.win/2pU近期面试了许多&#xff0c;真的是许多同学&#xff0c;讲道理其实我是比较有耐心的面试官&#xff0c;但是还是忍不住想要吐槽&#xff0c;因此写下这篇文章&#xff0c;一方面希望可以帮助到正在面试&#xff0c;或者在来面试路上…

Java面试详解(2020版):500+ 面试题和核心知识点详解

与其在网上拼命的找面试题&#xff0c;不如加入我们畅快的阅读。为了写好这些面试题&#xff0c;我先后拜访了一二十家互联网公司&#xff0c;与不同的面试官和面试者进行面对面探讨&#xff0c;深入了解了企业对于面试者的要求和常见的 Java 面试题型。之后我花了大半年的时间…

Linux debian安装Vim和Vim使用教程

Vim 是一个开源免费工具&#xff0c;具有命令行界面和图形用户界面。它对于编辑用 shell、python、Perl、c/c 等编写的配置文件和程序特别有用。最新版本的 Vim 包括一些新功能、错误修复和文档更新。 Vim安装步骤 步骤一、首先使用下面命令更新一下系统&#xff0c;确保您的…

给所有开发人员的 11 条忠告(第 4 条亮了)

1、初学者尽量去有成熟技术团队的公司 其实很多小型的互联网创业公司只有一两个人技术人员&#xff0c;如果里面有大牛能带你成长还好&#xff0c;但大部分估计都是要你自己去摸索学习来解决公司的问题&#xff0c;对个人成长其实是很慢长对过程&#xff0c;相反如果你去大厂&a…