关于同步/异步、阻塞/非阻塞IO的摘要

四种IO模型

Boost application performance using asynchronous I/O把同步阻塞、同步非阻塞、异步阻塞、异步非阻塞的模型讲得很清楚。html

处理大量链接的问题

event-driven模型派(异步模型):react

有人对于event-driven模型有一些批判,认为多线程模型(同步阻塞模型)不比事件模型差:git

  • Thousands of Threads and Blocking I/O,讲了C10K提到的多线程模型的性能瓶颈在现在的内核里已经不存在了,而多线程模型开发起来更简单。
  • Why Events are a Bad Idea(for high concurrency servers) Rob von Behren,讲了多线程模型的性能瓶颈基本上是由于内核支持的很差、多线程类库有缺陷形成的。认为能够经过编译器的优化、修复内核、修复多线程类库来达到和事件驱动模型至关的结果。且认为事件驱动模型的开发比较复杂。

两种模型也不是说水火不容,SEDA提出了能够将两种模型结合起来,构建更具弹性的系统。10年以后该做者写了篇回顾文章A Retrospective on SEDA程序员

SEDA提出了几个很具备见地的意见:github

  1. 应用程序的各个stage的压力应该是可观测和可调节的。
  2. 应用程序应该是well-conditioned。

什么是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

事件驱动模型到最后就变成了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的性能差异,不过看下来发现文章的压测方式存在偏爱:

  1. 文中给Tomcat的线程少了(只给了500),只利用了40%左右的CPU,而vert.x的测试的CPU利用率为100%。我把的Tomcat的线程设到2000,测试结果就和vert.x差很少了(验证了多线程模型派的观点)。
  2. vert.x的测试代码和Tomcat的测试代码不等价,没有使用Thread.sleep()。不过当我尝试在vert.x中使用sleep则发生了大量报错,应该是个人使用问题,后面就没有深究了。

我写的测试能够在这里看到。

总结

看了前面这么多文章其实总结下来就这么几点:

  1. 选择事件驱动模型仍是多线程模型要根据具体状况来(不过这是一句废话,; )
  2. 推崇、反对某个模型的文章/论文都是在当时的历史状况下写出来的,说白了就是存在历史局限性,所以必定要本身验证,当时正确的论断对如今来说未必正确,事情是会发生变化的。
  3. 看测试报告的时候必定要本身试验,有些测试可能自己设计的就有问题,致使结果存在偏见。对于大多数性能测试来讲,我以为只要抓住一点就好了,就是CPU必定要用足。
  4. 咱们真正应该关注的是不变的东西。

Jeff Darcy's notes on high-performance server design提到了高性能服务器的几个性能因素:

  • data copy,问题依然存在,须要程序员去优化。
  • context switch,这个问题已经没有了(见多线程派的几篇文章),现代操做系统不论有多少thread,开销不会有显著增长。
  • memory allocation,这个要看看,不过在Java里彷佛和JVM GC有关。
  • lock contention,这个问题依然存在,应该尽可能使用lock-free/non-blocking的数据结构。
  • 另外补充:在C10M里提到kernel和内核的network stack也是瓶颈。

仔细看看有些因素不就是事件驱动模型和多线程模型都面临的问题吗?而又有一些因素则是两种模型提出的当时所各自存在的短板吗?而某些短板如今不是就已经解决了吗?

上面说的有点虚,下面讲点实在的。

若是你有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闲置,这也就是为何SEDAScalable IO in Java都提到了要额外开线程来处理这些阻塞操做。关于如何用满CPU我以前写了一篇文章如何估算吞吐量以及线程池大小能够看看。

如何用满网络带宽没有什么经验,这里就不说了。

相关文章
相关标签/搜索