最近很多面试在问高并发的处理问题,大神能聊聊吗?

这确实是一个很广泛的面试问题,尤其是在“大众创业,万众创新”的今天,传统企业纷纷转型,新人新项目不断杀入市场,每一天都有大量的新项目诞生。作为一名技术员,当你进入一家处于转型期企业或者是创业企业的时候,没有人带,团队不健全,项目不稳定等问题肯定都是你需要面对的,在面试过程中,当你听到面试官在问你关于高并发问题的时候,我觉得你首先应该高兴,因为初创项目能活下来的就不多,如果一个项目由于高并发而导致不稳定,也就证明至少他们的项目商业模式上已经得到验证,运营方向已经找准(无论赚不赚钱),在这样的团队中工作,你一定是能获得提升的机会的(要知道天赋再好的人也需要项目来锻炼和培养)。

接下来就是正题了,要回答高并发如何处理这个问题,首先就要搞清楚到底什么是并发。​

严格意义上的“并发”其实是指在同一时刻,注意了,这个同一时刻指的是时间点而不是某个时间段,所以“高并发”是说同一时刻访问服务器的连接数量大。

为什么要强调是严格意义上呢,因为面试官所说的“高并发”问题处理并不是我们讨论的严格意义上的并发,这一点,我会在下面做出解释。

虽然面试官的问题涉及到更多其他的内容,不过并发确实是我们需要关注的问题。

我之前有在我的技术经理/架构师课程中教过大家有关于计算机软件性能建模的一套科学方法,这套方法不算复杂,但可以帮助我们抽象和简化问题,抓住关键点,从而指导我们对项目进行优化。

现在来给大家简单描述一下

课程中关于计算机工作原理和硬件性能指标与参数等细节我就不介绍了,简化后的模型和场景​大致如下

以java网络应用程序为例,每一个网络连接都有一个独立线程来处理业务(php程序是每一个请求会启动一个进程去处理),

假设在某一时刻服务器上有1000个网络连接,则可以认为为了处理这些连接上承载的业务,java程序会启动1000个线程(php则是1000个进程)。

模型场景有了,那么我们开始对性能进行建模

假设并发数为n(这个例子中 n=1000)

我们的cpu主频为 s 

每个连接需要消耗的内存为 x

每个连接消耗的带宽为 a

那么我们可以计算出来 当n=1000时

我们的cpu在多线程处理时,会进行线程的切换,每个连接可以获得的cpu性能为  s/n

而1000个连接需要的带宽为 a*1000  

1000个连接需要的内存为 x*1000

也就是说,我们需要连接数n变得更大的时候,最直接的办法就是用更快的cpu,加大内存,提高带宽 ,但实际情况是,项目运营过程中,连接数是变化的,而我们选择的硬件不会轻易改变。

当cpu主频已定,内存已定,带宽已定的情况下,如果并发连接数很小,那么我们的机器性能会很富余,一切都正常,若并发增加,那么每个线程分配到的cpu的性能s/n的值将会不断变小,给我们的直观感受就是服务器的处理速度明显变慢,直到n*x大于机器实际可用内存,那么再有连接,可能就会用上硬盘来补内存的不足,而我们知道,硬盘的速度是远远低于内存和cpu的,这会导致性能更进一步降低, 若n*a大于实际可用带宽,那么抱歉,你的服务器将无法继续正常访问。

好吧,如果从这个角度分析,严格意义上的并发性能应该是和硬件直接相关的,cpu,内存 ,带宽,甚至是硬盘都影响我们的理论最大并发连接数,而每一个线程/进程需要的内存,cpu,带宽则是由应用场景和编写的程序有关。

这里还需要给大家提一个概念,叫做“空载并发”,这个概念是为了更好的研究架构设计和并发性能问题处理而提出来的。由于一个项目中不同的网络请求所处理的业务与对应的算法是完全不同的,也就是说1000个并发连接,对应的1000个线程(或者进程),可能每一个线程(或进程)对cpu,内存,带宽的消耗都是不同的,这样不利于我们对硬件以及操作系统等底层基础设施的性能做出准确的判断,那么我们如果假定这个服务器的所有连接都不干任何事情,只是保持连接,那么这个服务器上的并发就是空载并发,因此空载并发是衡量你的软件所处环境并发性能的一个指标。

如果站在“空载并发”这个定义之下,那么性能就只和硬件有关,通过硬件扩容,我们可以获得更好的并发性能,而扩容可以有两种方式,一种是增加配置,换更好的cpu,加更大的内存条,提高带宽,这种方式叫做垂直扩容,当然还有另一种方式,通过增加机器数量,利用负载均衡设备(如F5负载均衡器),或者软件(如linux的lvs)等方式实现平行扩容。

由于垂直扩容受科技水平的影响,硬件不可能无限扩大性能,因此平行扩容成为了几乎所有互联网项目的最优选择。

因此严格意义上的“高并发”应该是通过平行扩容来解决的,不过很遗憾的是,如果你真的对面试官这样回答,他应该不会满意,他会想:“负载均衡谁不知道啊,我想要的是程序上,软件上怎么优化”。而这才是这个面试问题的关键。

我想告诉你一个事实,其实面试官和聘用你的公司其实真正关心的不是“并发”,而是“负载”。

并发与负载,是完全不同的两个概念,并发只是一个关于连接数量的描述,而负载才是真正应该被关心的性能指标。

用户使用网页或者app的时候,客户端程序(通常是浏览器)会向我们的服务器发送一个请求,这个请求抵达服务器端并与服务器建立连接,然后服务器会对请求进行业务处理并返回数据,这不是一个时刻,而是一个时间段,我们每一个请求都占用了一个连接资源的一个时间段,直到请求断开为止,倘若我们100毫秒就能处理完一个请求,那么即便我们的硬件只能支持一个网络连接,我们也能在1秒内处理10个请求,如果我们的硬件能支持到1000个连接,那么我们就能处理10000个请求。在1秒内处理10000个用户请求,这也是一种指标,它被叫做“负载能力”

实际项目中的负载能力有很多中不同的计量方式,刚才的例子中 1秒内的请求处理量是一种常用的计量方式,社区项目我们常用 同时在线用户数来描述负载,12306则可以使用同时支持订票人数来描述负载,天猫可以用双十一当天总交易额来描述负载。

要知道,对于运营团队来说,并发没有实际意义,负载才是真正有意义的指标,而大部分面试官问关于高并发如何处理的时候,他们实际关心的是负载。

而提高负载我们需要另一套模型:

一个服务器能在一秒内支持多少用户访问网页

单位时间的用户请求处理量 S 

每一个请求处理时间 t 

服务器硬件支持的最大并发连接数 n 

完成单个用户请求需要的连接数 m t=0.1秒 

1秒内处理量  1/0.1 =10个 

n=1000 

10* 1000 =10000个 

如果是网站应用,我们访问一个网页的同时处理对应的web页面,还会连接服务器去下载页面引用的css文件js文件图片文件等静态资源文件而这会导致并发请求的产生。

单个用户请求网页如果需要下载19个静态资源文件,则需要的连接数 m=20 

10000/20 =500 次 

S=500 

S =(T/t)*n/m

那么我们就能得出一个结论,在单位时间T不变的情况下,要想让我们的服务支持更多的用户访问,可以选择减少分母m也就是网页引用资源数量,也可以减少单个请求的处理时间t,当然也可以选择扩容硬件来提高并发量n

怎样减少引用资源数量,我相信你们肯定能找到很多办法,而减少单个请求处理时间t,则是程序优化和数据库优化的主要工作。

希望我的回答能帮助到你,也希望能帮助到更多的人,谢谢!

评论