本章钻研对象:分布式计算的网络应用程序,基本功能能够被简略演绎为“收到数据,算一算,收回去”

单线程服务器

最罕用的为“non-blocking IO + IO multiplexing”,即Reactor模式,例如

  • lighttpd
  • Nginx
  • libevent
  • Java NIO
  • Twisted(Python)

此外还有ASIO应用的Proactor模式

Reactor

构造

  • 事件循环(Event-loop)
  • 应用epoll进入阻塞监听事件,依照事件类型调用hanlder

特点

  • 须要非阻塞编程,回调函数必须是非阻塞的,只能在epoll处阻塞

    • 这导致容易割裂业务逻辑,不容易了解与保护
  • 实用于IO密集型

    • 计算太多也会阻塞
  • 尽管单线程模型不须要思考同步问题,但不能很好的利用多核,而如果同时应用多个过程,则势必会波及到同步问题

多线程服务器

  • 阻塞+每个申请新建一个线程
  • 阻塞+线程池
  • 非阻塞 + IO multiplexing 也即作者所称的“non-blocking IO + one loop per thread”
  • Leader/Follower等高级模式

One loop per thread

益处

  • 线程数目固定
  • 不便地在线程间调配负载
  • IO事件产生的线程是固定的,一个连贯会被注册到某个线程的loop中,并始终由这个线程负责,同一个TCP连贯不必思考并发

但多线程的loop相比单线程的loop的要求变高,即须要做到线程平安,重要的问题是“如何容许一个线程往另一个线程的loop里塞货色?”

线程池

  • 实用计算工作较多的状况,重要的基础设施是BlockingQueue实现的工作队列或生产者消费者队列
  • 把计算工作或待计算数据调配给线程池中的线程

过程间通信只用TCP

其余IPC办法: pipe,FIFO,音讯队列,共享内存,信号
TCP的益处:跨主机,伸缩性,双向,无负作用,可记录,可重现,容易定位故障

多线程服务器的实用场合

解决并发连贯的两种形式

  • Go goroutine, python gevent等的语言提供的用户态线程,由语言的runtime自行调度

    • 便宜,能够大量创立
    • 通常配合“看起来是阻塞的IO”,这里指对于用户态的调度器阻塞,而不是对于操作系统阻塞,否则用户态多线程无奈成立
  • 内核级线程配合Reactor模式,例如libevent,muduo,Netty

    • 数量与CPU数目相当
    • 非阻塞IO

因为C++并没有第一类的工具,所以本书只思考第二类

model剖析

  • 单过程+单线程 : 没有伸缩性
  • 单过程+多线程
  • 多过程+单线程

    • 复制多份单线程的过程,应用多个TCP port提供服务
    • 主过程+worker过程
  • 多过程+多线程 : 汇集了2和3的毛病

单线程

  • 须要fork的时候
  • 须要限度程序CPU占用率的时候
  • 单线程的Reactor的次要毛病是响应工夫慢,多线程改善这一点

多线程

  • IO吞吐(网络/磁盘)是瓶颈时,多线程不会减少吞吐,这时候单线程+单过程就够了
  • CPU算力是瓶颈时,多线程不会减少吞吐,这时候单线程+多过程更适合也更简略
  • 比照多过程
    8外围,压缩100个文件
    多过程:每个过程压缩1个文件
    多线程:每个文件用8个线程并行压缩
    总耗时雷同,因为CPU都是满载的,然而后者能更快拿到第一个压缩完的文件,这体现了多线程的低提早
    但实际上,线程间算法的并行度很难达到100%,所以:同一个工作,多线程或者会比多过程吞吐量降落一点,但肯定能够带来响应工夫的晋升。

多线程的益处在于

  • 进步响应速度,让IO和计算重叠
  • 相比过程每次切换都使CPU Cache生效,线程间切换老本小,因而实用于“工作集”比拟大的状况
  • 宰割事务,将IO线程、计算线程和第三方库(例如logging)线程离开

多线程应用BlockingQueue在过程间传递数据

  • 计算操作:一个IO线程,8个计算线程(线程池),IO线程向BlockingQueue中增加数据,计算线程收到唤醒后开始计算
  • 写操作:一个独自的logging线程,通过一个或多个BlockingQueue对外提供接口,别的线程把日志写入Queue中即可,不须要期待,这样升高了服务线程的响应工夫

    • 留神,读操作并不能利用多线程的便利性,因为无论如何都须要等到后果之后能力持续进行

线程池

  • 当计算在每次响应申请中占比比拟高时(20%以上)适宜
  • 能简化编程;防止实时创立线程的开销;加重IO线程的累赘(IO线程进行计算的话,就不能相应IO了,会导致响应速度变差)
  • 线程池的大小须要阻抗匹配,例如8核CPU,每个计算工作线程的密集计算所占工夫比重为50%,那么16个线程就能跑满全副CPU,线程池太小,会不能高效利用硬件,太大会导致额定的线程切换和内存开销

多线程不能缩小工作量,而是一种兼顾的思路让工作提前完结。