线程模型演化

1. 单线程java

  时间回到十几年前,那时主流的CPU都仍是单核(除了商用高性能的小机),CPU的核心频率是机器最重要的指标之一。react

在编程领域当时比较流行的是单线程编程,对于CPU密集型的应用程序而言,频繁的经过多线程进行协做和抢占时间片反而会下降性能。c++

2. 多线程编程

  随着硬件性能的提高,CPU的核数愈来愈越多,不少服务器标配已经达到32或64核。经过多线程并发编程,能够充分利用多核CPU的处理能力,提高系统的处理效率和并发性能。后端

从2005年开始,随着多核处理器的逐步普及,多线程并发编程也逐渐流行起来,用户能够经过 new Thread()的方式建立新的线程。安全

根据不一样的语言多线程之间的同步、协做、建立和销毁等工做都须要用户本身处理。因为建立和销毁线程是个相对比较重量级的操做,所以,这种原始的多线程编程效率和性能都不高。服务器

3. 线程池网络

  为了提高多线程编程的效率和性能,下降用户开发难度。JDK1.5推出了java.util.concurrent并发编程包。在并发编程类库中,提供了线程池、线程安全容器、原子类等新的类库,极大的提高了Java多线程编程的效率,下降了开发难度。若是使用c/c++这些就须要本身实现,固然对线程的理解也会更深刻。多线程

 

从JDK1.5开始,基于线程池的并发编程已经成为Java多核编程的主流。架构

4. Reactor模型

  不管是C++仍是Java编写的网络框架,大多数都是基于Reactor模式进行设计和开发,Reactor模式基于事件驱动,特别适合处理海量的I/O事件。

4.1  单线程模型

  Reactor单线程模型,指的是全部的IO操做都在同一个NIO线程上面完成,NIO线程的职责以下:

  1)做为NIO服务端,接收客户端的TCP链接;

  2)做为NIO客户端,向服务端发起TCP链接;

  3)读取通讯对端的请求或者应答消息;

  4)向通讯对端发送消息请求或者应答消息。

  Reactor单线程模型示意图以下所示:

            图1-1 Reactor单线程模型

 

  因为Reactor模式使用的是异步非阻塞IO,全部的IO操做都不会致使阻塞,理论上一个线程能够独立处理全部IO相关的操做。从架构层面看,一个NIO线程确实能够完成其承担的职责。例如,经过Acceptor类接收客户端的TCP链接请求消息,链路创建成功以后,经过Dispatch将对应的ByteBuffer派发到指定的Handler上进行消息解码。用户线程能够经过消息编码经过NIO线程将消息发送给客户端。

对于一些小容量应用场景,可使用单线程模型。可是对于高负载、大并发的应用场景却不合适,主要缘由以下:

  1) 一个NIO线程同时处理成百上千的链路,性能上没法支撑,即使NIO线程的CPU负荷达到100%,也没法知足海量消息的编码、解码、读取和发送;

  2) 当NIO线程负载太重以后,处理速度将变慢,这会致使大量客户端链接超时,超时以后每每会进行重发,这更加剧了NIO线程的负载,最终会致使大量消息积压和处理超时,成为系统的性能瓶颈;

  3)可靠性问题:一旦NIO线程意外跑飞,或者进入死循环,会致使整个系统通讯模块不可用,不能接收和处理外部消息,形成节点故障。

  为了解决这些问题,演进出了Reactor多线程模型,下面咱们一块儿学习下Reactor多线程模型。

4.2  多线程模型

  Rector多线程模型与单线程模型最大的区别就是有一组NIO线程处理IO操做,它的原理图以下:

            图1-2 Reactor多线程模型

Reactor多线程模型的特色:

  1) 有专门一个NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP链接请求;

  2) 网络IO操做-读、写等由一个NIO线程池负责,线程池能够采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;

  3) 1个NIO线程能够同时处理N条链路,可是1个链路只对应1个NIO线程,防止发生并发操做问题。

  在绝大多数场景下,Reactor多线程模型均可以知足性能需求;可是,在极个别特殊场景中,一个NIO线程负责监听和处理全部的客户端链接可能会存在性能问题。例如并发百万客户端链接,或者服务端须要对客户端握手进行安全认证,可是认证自己很是损耗性能。在这类场景下,单独一个Acceptor线程可能会存在性能不足问题,为了解决性能问题,产生了第三种Reactor线程模型-主从Reactor多线程模型。

4.3  主从多线程模型

  主从Reactor线程模型的特色是:服务端用于接收客户端链接的再也不是个1个单独的NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端TCP链接请求处理完成后(可能包含接入认证等),将新建立的SocketChannel注册到IO线程池(sub reactor线程池)的某个IO线程上,由它负责SocketChannel的读写和编解码工做。Acceptor线程池仅仅只用于客户端的登录、握手和安全认证,一旦链路创建成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操做。

它的线程模型以下图所示:

            图1-3 主从Reactor多线程模型

  利用主从NIO线程模型,能够解决1个服务端监听线程没法有效处理全部客户端链接的性能不足问题。

它的工做流程总结以下:

  1. 从主线程池中随机选择一个Reactor线程做为Acceptor线程,用于绑定监听端口,接收客户端链接;

  2. Acceptor线程接收客户端链接请求以后建立新的SocketChannel,将其注册到主线程池的其它Reactor线程上,由其负责接入认证、IP黑白名单过滤、握手等操做;

  3. 步骤2完成以后,业务层的链路正式创建,将SocketChannel从主线程池的Reactor线程的多路复用器上摘除,从新注册到Sub线程池的线程上,用于处理I/O的读写操做。

参考:Netty系列之Netty线程模型

相关文章
相关标签/搜索