Boost application performance using asynchronous I/O把同步阻塞、同步非阻塞、异步阻塞、异步非阻塞的模型讲得很清楚。html
event-driven模型派(异步模型):react
有人对于event-driven模型有一些批判,认为多线程模型(同步阻塞模型)不比事件模型差:git
两种模型也不是说水火不容,SEDA提出了能够将两种模型结合起来,构建更具弹性的系统。10年以后该做者写了篇回顾文章A Retrospective on SEDA。程序员
SEDA提出了几个很具备见地的意见:github
什么是Well-conditioned service?web
Intuitively, a service is well-conditioned if it behaves like a simple pipeline, where the depth of the pipeline is determined by the path through the network and the processing stages within the service itself. As the offered load increases, the delivered throughput increases proportionally until the pipeline is full and the throughput saturates; additional load should not degrade throughput. Similarly, the response time exhibited by the service is roughly constant at light load, because it is dominated by the depth of the pipeline. As load approaches saturation, the queueing delay dominates. In the closed-loop scenario typical of many services, where each client waits for a response before delivering the next request, response time should increase linearly with the number of clients.The key property of a well-conditioned service is graceful degradation: as offered load exceeds capacity, the service maintains high throughput with a linear response-time penalty that impacts all clients equally, or at least predictably according to some service-specific policy. Note that this is not the typical Web experience; rather, as load increases, throughput decreases and response time increases dramatically, creating the impression that the service has crashed.算法
简单来讲当负载超过一个应用的容量时,其性能表现要知足如下两点:编程
事件驱动模型到最后就变成了Reactor Pattern,下面是几篇文章:segmentfault
Scalable IO in Java介绍了如何使用NIO,其中很重要的一点是handler用来处理non-blocking的task,若是task是blocking的,那么要交给其余线程处理。这不就是简化版的SEDA吗?服务器
Reactor Pattern的老祖宗论文:Reactor Pattern,TL;DR。Understanding Reactor Pattern: Thread-Based and Event-Driven帮助你快速理解什么是Reactor Pattern,文中提到若是要处理10K个长链接,Tomcat是开不了那么多线程的。对此有一个疑问,Tomcat能够采用NIO/NIO2的Connector,为啥不能算做是Reactor呢?这是由于Tomcat不是事件驱动的,因此算不上。
The reactor pattern and non-blocking IO对比了Tomcat和vert.x的性能差异,不过看下来发现文章的压测方式存在偏爱:
Thread.sleep()
。不过当我尝试在vert.x中使用sleep则发生了大量报错,应该是个人使用问题,后面就没有深究了。我写的测试能够在这里看到。
看了前面这么多文章其实总结下来就这么几点:
Jeff Darcy's notes on high-performance server design提到了高性能服务器的几个性能因素:
仔细看看有些因素不就是事件驱动模型和多线程模型都面临的问题吗?而又有一些因素则是两种模型提出的当时所各自存在的短板吗?而某些短板如今不是就已经解决了吗?
上面说的有点虚,下面讲点实在的。
若是你有10K个长链接,每一个链接大部分时间不使用CPU(处于Idle状态或者blocking状态),那么为每一个链接建立一个单独的线程就显得不划算。由于这样作会占用大量内存,而CPU的利用率却很低,由于大多数时间线程都闲着。
事件驱动模型解决的是C10K问题,注意C是Connection,解决的是用更少的硬件资源处理更多的链接的问题,它不解决让请求更快速的问题(这是程序员/算法的问题)。
要不要采用事件驱动模型取决于Task的CPU运算时间与Blocking时间的比例,若是比例很低,那么用事件驱动模型。对于长链接来讲,好比websocket,这个比例就很小,甚至可近似认为是0,这个时候用事件驱动模型比较好。若是比例比较高,用多线程模型也能够,它的编程复杂度很低。
不管是采用哪一种模型,都要用足硬件资源,这个资源能够是CPU也能够是网络带宽,若是发生资源闲置那你的吞吐量就上不去。
对于多线程模型来讲开多少线程合适呢?Thousands of Threads and Blocking I/O里讲得很对,当可以使系统饱和的时候就够了。好比CPU到100%了、网络带宽满了。若是内存用满了可是这两个都没用满,那么通常来讲是出现BUG了。
对于事件驱动模型来讲也有CPU用满的问题,现实中总会存在一些阻塞操做会形成CPU闲置,这也就是为何SEDA和Scalable IO in Java都提到了要额外开线程来处理这些阻塞操做。关于如何用满CPU我以前写了一篇文章如何估算吞吐量以及线程池大小能够看看。
如何用满网络带宽没有什么经验,这里就不说了。