异步机制(Asynchronous) -- (四)缺点兼谈系统测试

来源:互联网 发布:android调用js方法失败 编辑:程序博客网 时间:2024/05/22 02:48

接上篇。

这是这个系列的最后一篇了,是我在春节期间陆陆续续写的,所以可能不是很顺畅....

之前列了很多异步机制的用法和好处,这里写写它的缺点。毫无疑问,异步机制很难用是众所周知的一个问题,原因在前面的几篇中也解释过了,因为你需要将同步机制中逻辑上顺序出现的代码切割成独立的几个小块,然后要非常小心的处理各个代码块之间的关系。
不过这里想解释的是另外一个问题,就是异步机制的系统很容易陷入到Overload的境地。可以简单的想象一下,比如一个异步调用,当这个调用返回时只是表示请求被接收了,并不表示请求完成了,这样一个调用可能只需要消耗一点点内存和一点点CPU,所以系统可以在很短的时间内完成大量的这种异步调用。但是,每个请求在执行的过程中可能要消耗大量的内存或者IO或者CPU,整个系统就不堪重负了。

 

整个更实在的例子吧:
假设Server最多能够并行处理5个request,每个请求需要花费100ms的时间,所以系统的最大吞吐量为50 request/sec。分别用两个Client向Server发送请求。其中Client A是同步调用,Client B是异步消息调用。
如果两个Client一开始都以50 request/sec(即每100ms发送5个request)的速度向server发送请求(对于Client A来说,它可以启动5个thread,每个thread不断的发送request,自然就可以达到要求的速度; 对于Client B来说,也可以启动5个thread,每个thread执行一次调用之后就sleep 100ms,再执行下一次调用),由于没有超过Server的最大负载能力,所以,无论是A还是B,都能够将这个速度保持下去。


接下来,尝试着增大Client的发送速度。Client A启动6个thread,不断的向Server发送request,因此可以认为在一开始,Client A是以60 request/s的速度来发送request的;但是,如图所示,在第一个100ms的中,A发送过来的6个request在Server端只能被处理5个,于是,在100ms这个时间点,A的6个thread只有5个的请求调用返回了,剩下的一个只能继续等待,所以,在第二个100ms,只有5个thread发送了下一个request,于是,A的发送速度自然的下降为50 request/s。以此类推,之后A的发送速度一直维持在50 request/s。(当然,和之前的5个thread相比还是有些区别的,能够发现,每次发送的5个request其中有一个的延迟达到了200ms)

 

如果使用Client B,也是启动6个thread,每个thread固定的以100ms的间隔执行异步请求调用(如下图所示),在第一个100ms,发送了6个request,其中5个被处理,还有一个在等待下一个100ms被处理(假设Server满足FIFO),在第二个100ms内,Client B又发送了6个request,但是系统首先需要将之前等待的request处理,于是只能再处理4个,所有,这次将有两个request处于等待状态。随着时间的流逝,所有新发送的request都将处于等待状态,因为Server会一直忙于处理之前发送的request,并且,request的latency趋近于无限长。

以上的例子并不很严谨,实际系统的情况会更复杂和不可预测。但它能够说明,如果不控制异步系统或者异步调用的调用速度,很容易的让系统陷入“超载”的境地。


为了避免这种情况的发生,一个实用的办法就是在异步系统的前端挂上一个有限长度的队列,所有接收的请求首先要被放入这个队列中,而一旦队列被填满,那么请求将不再被接受,在Client端会显示异步调用失败。这就好比是一个池子,当进水口的吞吐量大于出水口的吞吐量时,必须允许水可以从池子中溢出来,否则,池子是会爆炸的...

 

上面的例子其实还有另外一个角度的意义,即如何对系统性能进行测试?
我知道国内有很多的互联网公司,当一个系统进行压力测试时,就找几台机器,然后创建一些模拟客户端线程/进程,每个线程不断的向系统发送request并接收response,一如我们前面提及的同步机制。如果想加大压力,就增加模拟线程的数量。
这其实是最常见的一种做法,它的好处在于整个测试相对简单,并且很容易得到系统的Capability(只要计算一下模拟客户端的发送速率就可以了)。而且,往往client发送request所消耗的资源要远大于server处理request的消耗,所以,只要少量的客户端进程就能够使得系统达到它的capability。
但是,问题在于,用这种方式测试系统,很难使得系统处于Overload的状态,也就没法观察系统在Overload的时候的表现。因为正如前面所解释的,当你增大模拟线程的数量后,很多线程其实会被阻塞住等待response,所以也就没法发送新的request,因此,虽然request的latency会随着模拟线程数量的增加而变大,但是Server其实一直处于满负荷的状态,却并不会Overload。对于某些系统来说,在使用中可能很难Overload,但是对于很多其它的系统,Overload在实际运行中是常常碰见的情况。比如Web Server,有经验性的数据表明,Web Server遭遇到的burst的峰值是它平均负载的8~10倍,所以,对于类似于Web Server这样的系统,它在Overload情况下的表现是非常重要的一个性能指标,但这却正是常用的测试方法所不能观察到的。

 

-- END --

原创粉丝点击