正文
Web服务端在等待上一个请求处理的过程中,我们可以让I/O循环打开以便处理其他应用请求,直到处理完成时启动一个请求并给予反馈,而不再是等待请求完成的过程中挂起进程。这样,我们可以节省一些没有必要的等待时间,用这些时间去处理更多的请求,这样我们就可以大大增加请求的吞吐量,也就是在宏观上提高了我们可处理的并发请求数。
这里我们用Python的一款Web框架Tornado来具体说明改变阻塞方式提高并发性能。
场景:我们构建一个向远端(某个十分稳定的网站)发送HTTP请求的简单Web应用。这期间,网络传输稳定,我们不考虑网络来带的影响。
在这个例子中,我们使用Siege(一款压力测试软件)对服务端在10秒内执行大约10个并发请求。
如图2所示,我们可以很容易看出,这里的问题是无论每个请求自身返回多么快,服务器对远端的访问请求往返都会产生足够大的滞后,因为进程直到请求完成并且数据被处理前都一直处于强制挂起状态。当一两个请求时这还不是一个问题,但达到100个(甚至10个)用户时,这意味着整体变慢。如图,不到10秒时间10个相似用户的平均响应时间达到了1.99秒,共计29次。这个例子只展示了非常简单的逻辑。如果你要添加其他业务逻辑或数据库的调用的话,结果会更糟糕。增加更多的用户请求时,同时可被处理的请求就会增长缓慢,甚至有些请求会发生超时或失败。
下面我们用Tornado执行非阻塞的HTTP请求。
如图3所示,我们从每秒3.20个事务提升到了12.59,在相同的时间内总共提供了118次请求。这真是一个非常大的改善!正如你所想象的,随着用户请求增多和测试时间增长时,它将能够提供更多连接,并且不会遇到上面版本遭受的变慢的问题。从而稳定的提高了可负载的并发请求数。
先来介绍一下基础知识:一个应用程序是运行在机器上的一个进程;进程是一个运行在自己内存地址空间里的独立执行体。一个进程由一个或多个操作系统线程组成,这些线程其实是共享同一个内存地址空间的一起工作的执行体。
传统计算方式单线程运行,效率低,计算能力弱。
一种解决办法就是完全避免使用线程。例如,可以使用多个进程将重担交给操作系统来处理。但是,有个劣势就是,我们必须处理所有进程间通信,通常这比共享内存的并发模型有更多的开销。